diff --git a/CMakeLists.txt b/CMakeLists.txt index ccede4b..b6de0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,12 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_BUILD_TYPE "Debug") + +set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fno-omit-frame-pointer -fsanitize=address") +set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") + + include(FetchContent) FetchContent_Declare(json GIT_REPOSITORY https://github.com/nlohmann/json.git diff --git a/compile_commands.json b/compile_commands.json index 645d257..a38514b 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -4,6 +4,12 @@ "/usr/bin/c++", "-c", "-DCURL_STATICLIB", + "-DFMT_LOCALE", + "-DFMT_SHARED", + "-DSPDLOG_COMPILED_LIB", + "-DSPDLOG_FMT_EXTERNAL", + "-DSPDLOG_SHARED_LIB", + "-I/home/alistair/Documents/programming/bog/bog/spdlog/include", "-I/home/alistair/Documents/programming/bog/bog/tgbot-cpp/include", "-I/home/alistair/Documents/programming/bog/bog/build/_deps/json-src/single_include", "-I/home/alistair/Documents/programming/bog/bog/build/_deps/cpr-src/include", @@ -11,6 +17,7 @@ "-I/home/alistair/Documents/programming/bog/bog/build/_deps/curl-build/include/curl", "-std=c++11", "-Wall", + "-std=c++17", "-o", "CMakeFiles/telegram_bog.dir/telegram_bot.cpp.o", "../telegram_bot.cpp" diff --git a/telegram_bot b/telegram_bot deleted file mode 100755 index c8f2a7e..0000000 Binary files a/telegram_bot and /dev/null differ diff --git a/telegram_bot.cpp b/telegram_bot.cpp index 9062a7a..776068b 100644 --- a/telegram_bot.cpp +++ b/telegram_bot.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -198,6 +199,7 @@ class songdb { for (int i = 0; i < argc; i++) { values->insert({azColName[i], argv[i]}); } + return 0; } bool @@ -229,7 +231,9 @@ class songdb { "user INT NOT NULL, " "rating NOT NULL, " " FOREIGN KEY (song) REFERENCES tracks (id)," - " FOREIGN KEY (list) REFERENCES songlists (id));"; + " FOREIGN KEY (list) REFERENCES songlists (id)," + " PRIMARY KEY (song, list, user)" + ");"; std::string create_tracks_table = "CREATE TABLE IF NOT EXISTS tracks (" @@ -295,11 +299,15 @@ class songdb { check_error(err); } - sqlite3_reset(statement); + sqlite3_finalize(statement); } - int get_song_list_id(int64_t group_id) { + /* 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 = ?;"; @@ -308,6 +316,7 @@ class songdb { */ 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); @@ -330,40 +339,85 @@ class songdb { } else if (err == SQLITE_DONE) { // create the list - sqlite3_reset(statement); + sqlite3_finalize(statement); create_new_list(group_id); - sqlite3_prepare_v2(db, list_query.c_str(), list_query.length(), &statement, NULL); - err = sqlite3_bind_int64(statement, 0, 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 { + else if (err != SQLITE_DONE) { + check_error(err); } - + } else { + check_error(err); } + return list_id; } - bool insert_song(std::string name, std::string artist, std::optional spotify_id) { + struct track_entry { + int64_t id; + std::string name; + std::string artist; + std::optional spotify_id; - spdlog::debug("insert_song"); - std::string check_exist_spot = "SELECT * FROM tracks WHERE spotifyid = ?;"; - std::string check_exist_nospot = "SELECT * FROM tracks WHERE name = ? AND artist = ?;"; + }; - 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); + std::optional 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 get_song(std::string name, std::string artist) { + return get_song(name, artist, {}); + } + + std::optional get_song(std::string name, std::string artist, std::optional 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) { @@ -383,16 +437,49 @@ class songdb { 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."); - return true; } else if (err != SQLITE_DONE) { check_error(err); - return true; + sqlite3_finalize(statement); + return {}; } + sqlite3_finalize(statement); + return {}; + + } + + std::optional insert_song(std::string name, std::string artist, std::optional 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); @@ -413,12 +500,57 @@ class songdb { err = sqlite3_step(statement); if (err != SQLITE_DONE) { - sqlite3_reset(statement); + 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; } @@ -488,8 +620,6 @@ int main() { Bot bot(teletoken); - - InlineKeyboardMarkup::Ptr keyboard(new InlineKeyboardMarkup); std::vector row0; @@ -518,14 +648,42 @@ int main() { 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().onCallbackQuery([&bot, &keyboard](CallbackQuery::Ptr query) { - if (StringTools::startsWith(query->data, "check")) { - std::string response = "ok"; - bot.getApi().sendMessage(query->message->chat->id, response, false, 0, keyboard, "Markdown"); } }); @@ -551,14 +709,21 @@ int main() { 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; - response += "\n\r\n\r"; + 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"; - data.insert_song(title, artist, {}); 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!");