|
|
|
@ -21,6 +21,7 @@
@@ -21,6 +21,7 @@
|
|
|
|
|
#include <sqlite3.h> |
|
|
|
|
#include <cstdlib> |
|
|
|
|
#include <map> |
|
|
|
|
#include <spdlog/spdlog.h> |
|
|
|
|
|
|
|
|
|
using namespace TgBot; |
|
|
|
|
using json = nlohmann::json; |
|
|
|
@ -146,6 +147,7 @@ class spotify {
@@ -146,6 +147,7 @@ class spotify {
|
|
|
|
|
} |
|
|
|
|
return info; |
|
|
|
|
} else { |
|
|
|
|
std::cout << r.text << std::endl; |
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -179,15 +181,6 @@ class songdb {
@@ -179,15 +181,6 @@ class songdb {
|
|
|
|
|
/* need one table for every chat: keep it all in memory? */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* |
|
|
|
|
* song lists: [list id (autoincrement int), (int64) telegram group id, (string) list name ] |
|
|
|
|
* - these are tied to telegram groups |
|
|
|
|
* |
|
|
|
|
* songs : [Song spotify ID (string), list id (foreign key)], votes tally (int), number of votes (int) |
|
|
|
|
* - these are tied to song lists |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
std::string filepath; |
|
|
|
|
sqlite3 *db; |
|
|
|
@ -213,6 +206,15 @@ class songdb {
@@ -213,6 +206,15 @@ class songdb {
|
|
|
|
|
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, " |
|
|
|
@ -220,17 +222,23 @@ class songdb {
@@ -220,17 +222,23 @@ class songdb {
|
|
|
|
|
"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));"; |
|
|
|
|
|
|
|
|
|
std::string create_tracks_table =
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS tracks ("
|
|
|
|
|
"spotifyid VARCHAR(200) PRIMARY KEY, " |
|
|
|
|
"list INT NOT NULL, " |
|
|
|
|
"vote_total INT NOT NULL," |
|
|
|
|
"vote_count INT NOT NULL," |
|
|
|
|
"FOREIGN KEY (list)" |
|
|
|
|
" REFERENCES songlists (id)" |
|
|
|
|
"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) { |
|
|
|
@ -238,7 +246,6 @@ class songdb {
@@ -238,7 +246,6 @@ class songdb {
|
|
|
|
|
exit (1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = sqlite3_exec(db, create_tracks_table.c_str(), NULL, 0, &errmsg); |
|
|
|
|
|
|
|
|
|
if (err != SQLITE_OK) { |
|
|
|
@ -246,16 +253,23 @@ class songdb {
@@ -246,16 +253,23 @@ class songdb {
|
|
|
|
|
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) |
|
|
|
|
free(errmsg); |
|
|
|
|
sqlite3_free(errmsg); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int check_error(int rc) { |
|
|
|
|
if (rc != SQLITE_OK) { |
|
|
|
|
std::cout << "SQL Error: " << sqlite3_errmsg(db) << std::endl; |
|
|
|
|
spdlog::error("SQLite: {}", sqlite3_errmsg(db)); |
|
|
|
|
spdlog::dump_backtrace(); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
@ -266,18 +280,28 @@ class songdb {
@@ -266,18 +280,28 @@ class songdb {
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
void create_new_list(int64_t group_id) { |
|
|
|
|
spdlog::debug("create_new_list {}", group_id); |
|
|
|
|
|
|
|
|
|
sqlite3_stmt *statement; |
|
|
|
|
std::string insert_query = "INSERT INTO songlists (groupid, name) VALUES (? , default);"; |
|
|
|
|
sqlite3_prepare_v2(db, insert_query.c_str(), insert_query.length(), &statement, NULL); |
|
|
|
|
sqlite3_bind_int64(statement, 0, group_id); |
|
|
|
|
int err = sqlite3_step(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_reset(statement); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int 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
|
|
|
|
@ -286,18 +310,20 @@ class songdb {
@@ -286,18 +310,20 @@ class songdb {
|
|
|
|
|
|
|
|
|
|
sqlite3_stmt *statement; |
|
|
|
|
sqlite3_prepare_v2(db, list_query.c_str(), list_query.length(), &statement, NULL); |
|
|
|
|
err = sqlite3_bind_int64(statement, 0, group_id); |
|
|
|
|
err = sqlite3_bind_int64(statement, 1, group_id); |
|
|
|
|
check_error(err); |
|
|
|
|
err = sqlite3_step(statement); |
|
|
|
|
int list_id; |
|
|
|
|
|
|
|
|
|
if (err == SQLITE_ROW) { |
|
|
|
|
list_id = sqlite3_column_int64(statement, 0); |
|
|
|
|
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
|
|
|
|
|
std::cout << "bad\n"; |
|
|
|
|
spdlog::error("Should only be one list per group?"); |
|
|
|
|
spdlog::dump_backtrace(); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -325,25 +351,74 @@ class songdb {
@@ -325,25 +351,74 @@ class songdb {
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool insert_song(std::string spotify_id, int64_t group_id) { |
|
|
|
|
|
|
|
|
|
bool insert_song(std::string name, std::string artist, std::optional<std::string> 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 = "INSERT INTO tracks (spotifyid, list, vote_total, vote_count) VALUES(?, ?, 0, 0);"; |
|
|
|
|
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); |
|
|
|
|
// int list_id = get_song_list_id(group_id);
|
|
|
|
|
sqlite3_stmt *statement; |
|
|
|
|
sqlite3_prepare_v2(db, ins_query.c_str(), ins_query.length(), &statement, NULL); |
|
|
|
|
sqlite3_bind_text(statement, 0, spotify_id.c_str(), spotify_id.length(), NULL); |
|
|
|
|
sqlite3_bind_int(statement, 1, list_id); |
|
|
|
|
|
|
|
|
|
int err = sqlite3_step(statement); |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
/* 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) { |
|
|
|
|
spdlog::info("Entry exists."); |
|
|
|
|
return true; |
|
|
|
|
} else if (err != SQLITE_DONE) { |
|
|
|
|
check_error(err); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_reset(statement); |
|
|
|
|
spdlog::warn("Failed insertion Sqlite: {}", sqlite3_errstr(err)); |
|
|
|
|
spdlog::dump_backtrace(); |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -372,6 +447,10 @@ class songdb {
@@ -372,6 +447,10 @@ class songdb {
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@ -474,11 +553,10 @@ int main() {
@@ -474,11 +553,10 @@ int main() {
|
|
|
|
|
|
|
|
|
|
std::string response = "Added song: " + title + ", by " + artist; |
|
|
|
|
response += "\n\r\n\r"; |
|
|
|
|
response += "Everyone, please rate this suggestion /5"; |
|
|
|
|
response += "Everyone, please rate how well you know this song /5"; |
|
|
|
|
|
|
|
|
|
data.insert_song(*resp, message->chat->id); |
|
|
|
|
data.insert_song(title, artist, {}); |
|
|
|
|
bot.getApi().sendMessage(message->chat->id, response, false, 0, keyboard, "Markdown"); |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|