You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
753 lines
22 KiB
753 lines
22 KiB
#include "cpr/cprtypes.h" |
|
#include "cpr/parameters.h" |
|
#include "tgbot/net/BoostHttpOnlySslClient.h" |
|
#include "tgbot/net/HttpParser.h" |
|
#include <bits/stdint-intn.h> |
|
#include <bits/stdint-uintn.h> |
|
#include <csignal> |
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <exception> |
|
#include <istream> |
|
#include <sstream> |
|
#include <string> |
|
#include <nlohmann/json.hpp> |
|
#include <memory> |
|
|
|
#include "Base64.hpp" |
|
#include <sys/types.h> |
|
#include <tgbot/tgbot.h> |
|
#include <cpr/cpr.h> |
|
#include <optional> |
|
#include <sqlite3.h> |
|
#include <cstdlib> |
|
#include <map> |
|
#include <spdlog/spdlog.h> |
|
|
|
using namespace TgBot; |
|
using json = nlohmann::json; |
|
|
|
|
|
namespace util { |
|
|
|
std::string |
|
trim_whitespace(std::string s) |
|
{ |
|
int ff = s.find_first_not_of(" \n\t"); |
|
int ll = s.find_last_not_of(" \n\t"); |
|
return s.substr(ff, ll - ff + 1); |
|
} |
|
|
|
|
|
|
|
std::string |
|
read_file(std::string const &fpath) |
|
{ |
|
std::ostringstream sstr; |
|
std::ifstream in (fpath); |
|
sstr << in.rdbuf(); |
|
return sstr.str(); |
|
} |
|
|
|
void write_file(std::string const &fpath, std::string const &content) { |
|
std::fstream s; |
|
s.open(fpath, std::ios_base::out); |
|
if (!s.is_open()) { |
|
std::cerr << "Error: failed to open file "<< fpath; |
|
return; |
|
} |
|
s << content; |
|
s.close(); |
|
} |
|
|
|
}; |
|
|
|
namespace kare { |
|
|
|
class spotify { |
|
|
|
const std::string API_NAME_BASE = "https://api.spotify.com/"; |
|
std::string access_token; |
|
cpr::Header auth_header; |
|
|
|
|
|
public: |
|
|
|
void |
|
verify_logged_in() |
|
{ |
|
/* |
|
* |
|
* Would be a good idea to just requrst a page to chek. but the /me |
|
* endpoint is for logged in users i guess? and this iis jut an app. |
|
* |
|
cpr::Response r = cpr::Get(cpr::Url{API_NAME_BASE + "v1/me"},cpr::Header{{"Authorization", "Bearer " + access_token}}); |
|
std::istringstream isj {r.text}; |
|
json auth_response; |
|
try { |
|
isj >> auth_response; |
|
} catch (json::exception & e) { |
|
std::cout << e.id << r.text; |
|
} |
|
|
|
if (auth_response.count("display_name")) { |
|
std::cout << "Logged in as " << auth_response["display_name"] << std::endl << auth_response["href"] << std::endl; |
|
} else { |
|
std::cout << "Failed to log in" << std::endl << auth_response.dump(4) << std::endl; |
|
} |
|
*/ |
|
} |
|
|
|
spotify(std::string access_token) : access_token(access_token) |
|
{ |
|
auth_header = cpr::Header{{"Authorization", "Bearer " + access_token}}; |
|
verify_logged_in(); |
|
} |
|
|
|
spotify (std::string client_id, std::string client_secret) |
|
{ |
|
auto ascii_token = client_id + ":" + client_secret; |
|
|
|
size_t buf_length = base64::get_encoded_length(ascii_token.length()); |
|
auto buf = std::make_unique<uint8_t[]>(buf_length); |
|
std::string auth_token; |
|
|
|
base64::encode((uint8_t *)(ascii_token.c_str()), ascii_token.length(), buf.get(), buf_length); |
|
auth_token = std::string {(char *)buf.get(), buf_length}; |
|
|
|
cpr::Response r = cpr::Post(cpr::Url{"https://accounts.spotify.com/api/token"},cpr::Header{{"Authorization", "Basic " + auth_token}}, cpr::Parameters{{"grant_type", "client_credentials"}}); |
|
|
|
std::istringstream isj {r.text}; |
|
json auth_response; |
|
isj >> auth_response; |
|
|
|
if (auth_response.count("access_token")) { |
|
access_token = auth_response["access_token"]; |
|
std::cout << "Successfully logged into spotify" << std::endl << "Access token: " << access_token << std::endl; |
|
} else { |
|
std::cout << "Unable to log into spotify:" << std::endl << auth_response.dump(4) << std::endl; |
|
exit (1); |
|
} |
|
|
|
auth_header = cpr::Header{{"Authorization", "Bearer " + access_token}}; |
|
verify_logged_in(); |
|
} |
|
|
|
std::optional<json> |
|
get_track(std::string track_id) |
|
{ |
|
|
|
auto r = cpr::Get(cpr::Url{API_NAME_BASE + "v1/tracks/" + track_id}, auth_header); |
|
if (r.status_code == 200) { |
|
std::istringstream isj {r.text}; |
|
json info ; |
|
try { |
|
isj >> info; |
|
} catch (json::exception & e) { |
|
std::cout << "Json error" << std::endl; |
|
} |
|
return info; |
|
} else { |
|
std::cout << r.text << std::endl; |
|
return {}; |
|
} |
|
|
|
} |
|
|
|
/* |
|
* Parse track link from a spotify url like: |
|
* |
|
* https://open.spotify.com/track/4UO1pfxi5fDbxshrwwznJ2?si=BtN9Yn_JQXSHGUa4CEZKvQ&utm_source=copy-link |
|
* |
|
* |
|
*/ |
|
std::optional<std::string> |
|
track_id_from_link(std::string link) |
|
{ |
|
const std::string start = "spotify.com/track/"; |
|
auto f = link.find(start); |
|
if (f == std::string::npos) { |
|
return {}; |
|
} |
|
|
|
auto end = link.find("?", f); |
|
auto begin = f + start.length(); |
|
return link.substr(begin, end - begin); |
|
} |
|
|
|
}; |
|
|
|
|
|
class songdb { |
|
/* need one table for every chat: keep it all in memory? */ |
|
|
|
|
|
|
|
std::string filepath; |
|
sqlite3 *db; |
|
|
|
enum error_codes { |
|
NOT_FOUND, |
|
ALREADY_ADDED |
|
}; |
|
|
|
static int |
|
callback(void *valmap, int argc, char **argv, char **azColName) |
|
{ |
|
std::map<std::string, std::string> *values = (std::map<std::string, std::string> *)valmap; |
|
|
|
for (int i = 0; i < argc; i++) { |
|
values->insert({azColName[i], argv[i]}); |
|
} |
|
return 0; |
|
} |
|
|
|
bool |
|
setup_tables() |
|
{ |
|
char *errmsg; |
|
int err; |
|
|
|
/* |
|
* songlists: [list id (autoincrement int), (int64) telegram group id, (string) list name ] |
|
* |
|
* songs : [songid int autoincrement, song name NOT NULL, song artist NULL, Song spotify ID (string) ] |
|
* |
|
* votes: [song id foreign key, list id foreign key, telegram user id, int rating ] |
|
* |
|
*/ |
|
|
|
std::string create_songlist_table = |
|
"CREATE TABLE IF NOT EXISTS songlists (" |
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, " |
|
"groupid INT NOT NULL, " |
|
"name VARCHAR(200) NOT NULL " |
|
");"; |
|
|
|
std::string create_votes_table = |
|
"CREATE TABLE IF NOT EXISTS votes (" |
|
"song INT NOT NULL, " |
|
"list INT NOT NULL, " |
|
"user INT NOT NULL, " |
|
"rating NOT NULL, " |
|
" FOREIGN KEY (song) REFERENCES tracks (id)," |
|
" FOREIGN KEY (list) REFERENCES songlists (id)," |
|
" PRIMARY KEY (song, list, user)" |
|
");"; |
|
|
|
std::string create_tracks_table = |
|
"CREATE TABLE IF NOT EXISTS tracks (" |
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, " |
|
"name VARCHAR(200) NOT NULL, " |
|
"artist VARCHAR(200), " |
|
"spotifyid VARCHAR(200) " |
|
");"; |
|
|
|
err = sqlite3_exec(db, create_songlist_table.c_str(), NULL, 0, &errmsg); |
|
|
|
if (err != SQLITE_OK) { |
|
std::cout << "SQLite Error " << create_songlist_table << "\n" << errmsg << std::endl; |
|
exit (1); |
|
} |
|
|
|
err = sqlite3_exec(db, create_tracks_table.c_str(), NULL, 0, &errmsg); |
|
|
|
if (err != SQLITE_OK) { |
|
std::cout << "SQLite Error: " << create_tracks_table << "\n" << errmsg << std::endl; |
|
exit (1); |
|
} |
|
|
|
|
|
err = sqlite3_exec(db, create_votes_table.c_str(), NULL, 0, &errmsg); |
|
|
|
if (err != SQLITE_OK) { |
|
std::cout << "SQLite Error: " << create_votes_table << "\n" << errmsg << std::endl; |
|
exit (1); |
|
} |
|
|
|
if (errmsg) |
|
sqlite3_free(errmsg); |
|
return false; |
|
} |
|
|
|
int check_error(int rc) { |
|
if (rc != SQLITE_OK) { |
|
spdlog::error("SQLite: {}", sqlite3_errmsg(db)); |
|
spdlog::dump_backtrace(); |
|
exit(1); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
|
|
public: |
|
|
|
void create_new_list(int64_t group_id) { |
|
spdlog::debug("create_new_list {}", group_id); |
|
|
|
sqlite3_stmt *statement; |
|
int err; |
|
std::string insert_query = "INSERT INTO songlists (groupid, name) VALUES (? , \"default\");"; |
|
err = sqlite3_prepare_v2(db, insert_query.c_str(), insert_query.length(), &statement, NULL); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 1, group_id); |
|
check_error(err); |
|
err = sqlite3_step(statement); |
|
|
|
if (err != SQLITE_DONE) { |
|
check_error(err); |
|
} |
|
|
|
sqlite3_finalize(statement); |
|
|
|
} |
|
|
|
/* Retrieve or create the song list id for a group. |
|
* |
|
* As is, song lists are unique to telegram groups |
|
*/ |
|
int64_t get_song_list_id(int64_t group_id) { |
|
spdlog::debug("get_song_list_id {}", group_id); |
|
|
|
std::string list_query = "SELECT * FROM songlists WHERE groupid = ?;"; |
|
/* A constraint of this implementation is that every group can only have |
|
* one list |
|
*/ |
|
int err; |
|
|
|
|
|
sqlite3_stmt *statement; |
|
sqlite3_prepare_v2(db, list_query.c_str(), list_query.length(), &statement, NULL); |
|
err = sqlite3_bind_int64(statement, 1, group_id); |
|
check_error(err); |
|
err = sqlite3_step(statement); |
|
int list_id; |
|
|
|
if (err == SQLITE_ROW && sqlite3_column_count(statement)) { |
|
|
|
list_id = sqlite3_column_int(statement, 0); |
|
|
|
err = sqlite3_step(statement); |
|
if (err != SQLITE_DONE) { |
|
// should only ever be 1 entry |
|
spdlog::error("Should only be one list per group?"); |
|
spdlog::dump_backtrace(); |
|
exit(1); |
|
} |
|
|
|
|
|
} else if (err == SQLITE_DONE) { |
|
// create the list |
|
sqlite3_finalize(statement); |
|
create_new_list(group_id); |
|
|
|
err = sqlite3_prepare_v2(db, list_query.c_str(), list_query.length(), &statement, NULL); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 1, group_id); |
|
check_error(err); |
|
|
|
err = sqlite3_step(statement); |
|
|
|
if (err == SQLITE_ROW){ |
|
list_id = sqlite3_column_int64(statement, 0); |
|
} |
|
else if (err != SQLITE_DONE) { |
|
|
|
check_error(err); |
|
} |
|
} else { |
|
check_error(err); |
|
} |
|
|
|
|
|
return list_id; |
|
|
|
} |
|
|
|
struct track_entry { |
|
int64_t id; |
|
std::string name; |
|
std::string artist; |
|
std::optional<std::string> spotify_id; |
|
|
|
}; |
|
|
|
|
|
std::optional<track_entry> get_song(int64_t id) { |
|
std::string check_exist = "SELECT * FROM tracks WHERE id = ?;"; |
|
|
|
int err; |
|
sqlite3_stmt *statement; |
|
err = sqlite3_prepare_v2(db, check_exist.c_str(), check_exist.length(), &statement, NULL); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 1, id); |
|
check_error(err); |
|
|
|
err = sqlite3_step(statement); |
|
if (err == SQLITE_ROW) { |
|
|
|
int64_t id = sqlite3_column_int64(statement, 0); |
|
std::string name {(char *)sqlite3_column_text(statement, 1)}; |
|
std::string artist {(char *)sqlite3_column_text(statement, 2)}; |
|
char * spotid = (char *)sqlite3_column_text(statement, 3); |
|
|
|
if (spotid) { |
|
track_entry e {id, name, artist, std::string(spotid)}; |
|
sqlite3_finalize(statement); |
|
return e; |
|
} |
|
track_entry e {id, name, artist}; |
|
sqlite3_finalize(statement); |
|
return e; |
|
} |
|
sqlite3_finalize(statement); |
|
return {}; |
|
} |
|
|
|
std::optional<track_entry> get_song(std::string name, std::string artist) { |
|
return get_song(name, artist, {}); |
|
} |
|
|
|
std::optional<track_entry> get_song(std::string name, std::string artist, std::optional<std::string> spotify_id) { |
|
|
|
spdlog::debug("get_song"); |
|
|
|
std::string check_exist_spot = "SELECT * FROM tracks WHERE spotifyid = ?;"; |
|
std::string check_exist_nospot = "SELECT * FROM tracks WHERE name = ? AND artist = ?;"; |
|
|
|
int err; |
|
sqlite3_stmt *statement; |
|
|
|
/* check whether the song exists */ |
|
if (spotify_id) { |
|
err = sqlite3_prepare_v2(db, check_exist_spot.c_str(), check_exist_spot.length(), &statement, NULL); |
|
check_error(err); |
|
|
|
err = sqlite3_bind_text(statement, 1, spotify_id->c_str(), spotify_id->length(), NULL); |
|
check_error(err); |
|
} else { |
|
err = sqlite3_prepare_v2(db, check_exist_nospot.c_str(), check_exist_nospot.length(), &statement, NULL); |
|
check_error(err); |
|
|
|
err = sqlite3_bind_text(statement, 1, name.c_str(), name.length(), NULL); |
|
check_error(err); |
|
|
|
err = sqlite3_bind_text(statement, 2, artist.c_str(), artist.length(), NULL); |
|
check_error(err); |
|
} |
|
|
|
err = sqlite3_step(statement); |
|
if (err == SQLITE_ROW) { |
|
|
|
int64_t id = sqlite3_column_int64(statement, 0); |
|
std::string name {(char *)sqlite3_column_text(statement, 1)}; |
|
std::string artist {(char *)sqlite3_column_text(statement, 2)}; |
|
char * spotid = (char *)sqlite3_column_text(statement, 3); |
|
|
|
if (spotid) { |
|
track_entry e {id, name, artist, std::string(spotid)}; |
|
sqlite3_finalize(statement); |
|
return e; |
|
} |
|
track_entry e {id, name, artist}; |
|
sqlite3_finalize(statement); |
|
return e; |
|
spdlog::info("Entry exists."); |
|
} else if (err != SQLITE_DONE) { |
|
check_error(err); |
|
sqlite3_finalize(statement); |
|
return {}; |
|
} |
|
|
|
sqlite3_finalize(statement); |
|
return {}; |
|
|
|
} |
|
|
|
std::optional<track_entry> insert_song(std::string name, std::string artist, std::optional<std::string> spotify_id) { |
|
|
|
spdlog::debug("insert_song"); |
|
|
|
std::string ins_query_spot = "INSERT INTO tracks (name, artist, spotifyid) VALUES(?, ?, ?);"; |
|
std::string ins_query_nospot = "INSERT INTO tracks (name, artist) VALUES(?, ?);"; |
|
|
|
// int list_id = get_song_list_id(group_id); |
|
sqlite3_stmt *statement; |
|
int err; |
|
|
|
auto e = get_song(name, artist, spotify_id); |
|
if (e) { |
|
return e; |
|
} |
|
|
|
if (spotify_id) { |
|
err = sqlite3_prepare_v2(db, ins_query_spot.c_str(), ins_query_spot.length(), &statement, NULL); |
|
} else { |
|
err = sqlite3_prepare_v2(db, ins_query_nospot.c_str(), ins_query_nospot.length(), &statement, NULL); |
|
} |
|
check_error(err); |
|
|
|
err = sqlite3_bind_text(statement, 1, name.c_str(), name.length(), NULL); |
|
check_error(err); |
|
err = sqlite3_bind_text(statement, 2, artist.c_str(), artist.length(), NULL); |
|
check_error(err); |
|
|
|
if (spotify_id) { |
|
err = sqlite3_bind_text(statement, 3, spotify_id->c_str(), spotify_id->length(), NULL); |
|
check_error(err); |
|
} |
|
|
|
err = sqlite3_step(statement); |
|
if (err != SQLITE_DONE) { |
|
sqlite3_finalize(statement); |
|
spdlog::warn("Failed insertion Sqlite: {}", sqlite3_errstr(err)); |
|
spdlog::dump_backtrace(); |
|
sqlite3_finalize(statement); |
|
return {}; |
|
} |
|
|
|
sqlite3_finalize(statement); |
|
return get_song(name, artist, spotify_id); |
|
} |
|
|
|
bool |
|
insert_vote(int64_t user, int64_t group, int value, int64_t songid) |
|
{ |
|
spdlog::debug("insert_vote"); |
|
auto song = get_song(songid); |
|
if (!song) { |
|
spdlog::error("Failed to add vote, couldnt find song id: {}", songid); |
|
return true; |
|
} |
|
|
|
int64_t list = get_song_list_id(group); |
|
|
|
|
|
/* find existing vote */ |
|
|
|
|
|
std::string ins_query = "INSERT OR REPLACE INTO votes (song, list, user, rating) " |
|
"VALUES (?, ?, ?, ?);"; |
|
|
|
sqlite3_stmt *statement; |
|
int err; |
|
err = sqlite3_prepare_v2(db, ins_query.c_str(), ins_query.length(), &statement, NULL); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 1, song->id); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 2, list); |
|
check_error(err); |
|
err = sqlite3_bind_int64(statement, 3, user); |
|
check_error(err); |
|
err= sqlite3_bind_int64(statement, 4, value); |
|
check_error(err); |
|
|
|
err = sqlite3_step(statement); |
|
if (err != SQLITE_DONE) { |
|
check_error(err); |
|
sqlite3_finalize(statement); |
|
return true; |
|
} |
|
|
|
sqlite3_finalize(statement); |
|
return false; |
|
} |
|
|
|
songdb (std::string filepath): filepath(filepath) { |
|
int err = sqlite3_open(filepath.c_str(), &db); |
|
if (err) { |
|
std::cout << "Failed to open database: " << sqlite3_errmsg(db); |
|
exit(1); |
|
} |
|
|
|
setup_tables(); |
|
} |
|
|
|
~songdb () { |
|
sqlite3_close(db); |
|
} |
|
|
|
bool add_song(std::string spotify_track_id) { |
|
// request spotify api for info |
|
// yeet into db |
|
|
|
return true; |
|
}; |
|
|
|
}; |
|
}; |
|
|
|
int main() { |
|
|
|
spdlog::set_level(spdlog::level::info); // Set global log level to debug |
|
spdlog::enable_backtrace(32); |
|
|
|
char *teletok = getenv("TELEGRAM_TOKEN"); |
|
|
|
// needed if file not exist |
|
char *spotid = getenv("SPOTIFY_ID"); |
|
char *spotsecret = getenv("SPOTIFY_SECRET"); |
|
char *spotaccess_token = getenv("SPOTIFY_TOKEN"); |
|
|
|
|
|
if (!teletok) { |
|
std::cout << "Need to set environment variable TELEGRAM_TOKEN" << std::endl; |
|
exit(1); |
|
} |
|
|
|
if (!spotaccess_token) { |
|
if (!spotid) { |
|
std::cout << "Need to set environment variable SPOTIFY_ID or SPOTIFY_TOKEN" << std::endl; |
|
exit(1); |
|
} |
|
if (!spotsecret) { |
|
std::cout << "Need to set environment variable SPOTIFY_SECRET of SPOTIFY_TOKEN" << std::endl; |
|
exit(1); |
|
} |
|
} |
|
|
|
kare::songdb data {"test.db"}; |
|
|
|
std::string teletoken {teletok}; |
|
|
|
kare::spotify *s; |
|
if (spotaccess_token) |
|
s = new kare::spotify(spotaccess_token); |
|
else |
|
s = new kare::spotify(spotid, spotsecret); |
|
|
|
|
|
Bot bot(teletoken); |
|
|
|
InlineKeyboardMarkup::Ptr keyboard(new InlineKeyboardMarkup); |
|
std::vector<InlineKeyboardButton::Ptr> row0; |
|
|
|
InlineKeyboardButton::Ptr button1(new InlineKeyboardButton); |
|
button1->text = "1"; |
|
button1->callbackData = "1"; |
|
row0.push_back(button1); |
|
|
|
InlineKeyboardButton::Ptr button2(new InlineKeyboardButton); |
|
button2->text = "2"; |
|
button2->callbackData = "2"; |
|
row0.push_back(button2); |
|
|
|
InlineKeyboardButton::Ptr button3(new InlineKeyboardButton); |
|
button3->text = "3"; |
|
button3->callbackData = "3"; |
|
row0.push_back(button3); |
|
|
|
InlineKeyboardButton::Ptr button4(new InlineKeyboardButton); |
|
button4->text = "4"; |
|
button4->callbackData = "4"; |
|
row0.push_back(button4); |
|
|
|
InlineKeyboardButton::Ptr button5(new InlineKeyboardButton); |
|
button5->text = "5"; |
|
button5->callbackData= "5"; |
|
row0.push_back(button5); |
|
|
|
keyboard->inlineKeyboard.push_back(row0); |
|
|
|
bot.getEvents().onCallbackQuery([&bot, &keyboard, &data](CallbackQuery::Ptr query) { |
|
|
|
spdlog::info("Query: {}, mesg {}", query->data, query->message->text); |
|
|
|
if ((query->data == "1") || (query->data == "2") || (query->data == "3") || (query->data == "4") || (query->data == "5")) { |
|
|
|
std::istringstream is {query->data}; |
|
int value; |
|
is >> value; |
|
|
|
std::string songidflag = "songid:"; |
|
auto a = query->message->text.find(songidflag); |
|
auto b = query->message->text.find("\n", a); |
|
|
|
if (a==std::string::npos || b==std::string::npos) { |
|
spdlog::error("Parse songid"); |
|
spdlog::dump_backtrace(); |
|
return; |
|
} |
|
a += songidflag.length(); |
|
|
|
std::istringstream is2 {query->message->text.substr(a, b - a)}; |
|
int64_t songid; |
|
is2 >> songid; |
|
auto song = data.get_song(songid); |
|
if (!song) { |
|
spdlog::error ("bad song id"); |
|
} |
|
|
|
spdlog::info("Adding vote for {}: {}",song->name, value); |
|
|
|
data.insert_vote(query->from->id, query->message->chat->id, value, songid); |
|
|
|
|
|
} |
|
}); |
|
|
|
bot.getEvents().onCommand("add", [&bot, &keyboard, &data, s](Message::Ptr message) { |
|
|
|
std::string link = util::trim_whitespace(message->text.substr(message->text.find("add"))); |
|
auto resp = s->track_id_from_link(link); |
|
if (!resp) { |
|
bot.getApi().sendMessage(message->chat->id, "Sorry, I don't understand that link."); |
|
return; |
|
} |
|
|
|
auto spot_resp = s->get_track(*resp); |
|
|
|
if (!spot_resp) { |
|
bot.getApi().sendMessage(message->chat->id, "Sorry, I cannot find that track in spotify."); |
|
return; |
|
} |
|
|
|
json track_data = *spot_resp; |
|
|
|
std::cout << spot_resp->dump(4); |
|
std::string title = track_data["name"]; |
|
std::string artist = track_data["artists"][0]["name"]; |
|
|
|
auto song = data.insert_song(title, artist, *resp); |
|
|
|
std::string response = "Added song: " + title + ", by " + artist; |
|
std::ostringstream os; |
|
os << song->id; |
|
response += "\n\rsongid:" + os.str() + "\n\r\n\r"; |
|
response += "Everyone, please rate how well you know this song /5"; |
|
|
|
bot.getApi().sendMessage(message->chat->id, response, false, 0, keyboard, "Markdown"); |
|
}); |
|
|
|
bot.getEvents().onCommand("vote", [&bot](Message::Ptr message) { |
|
bot.getApi().sendMessage(message->chat->id, "Hi!"); |
|
}); |
|
|
|
|
|
bot.getEvents().onCommand("start", [&bot](Message::Ptr message) { |
|
bot.getApi().sendMessage(message->chat->id, "Hi!"); |
|
}); |
|
|
|
signal(SIGINT, [](int s) { |
|
printf("SIGINT got\n"); |
|
exit(0); |
|
}); |
|
|
|
|
|
try { |
|
printf("Bot username: %s\n", bot.getApi().getMe()->username.c_str()); |
|
bot.getApi().deleteWebhook(); |
|
|
|
TgLongPoll longPoll(bot); |
|
while (true) { |
|
printf("Long poll started\n"); |
|
longPoll.start(); |
|
} |
|
} catch (std::exception& e) { |
|
printf("error: %s\n", e.what()); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|