From a03540d61ed913b78f7c02f8b695fd69eea3e217 Mon Sep 17 00:00:00 2001 From: Cara Salter Date: Sat, 15 Apr 2023 15:09:01 -0400 Subject: Finish spotify for now Change-Id: I77c7d319ddf37c7259f4de89567f9a6719f88796 --- .vscode/launch.json | 4 +-- include/142bot/db.hpp | 6 ++-- modules/spotify/spotify.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++--- src/db.cpp | 4 +-- 4 files changed, 89 insertions(+), 11 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 89efb96..c72eb2c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,13 +5,13 @@ "version": "0.2.0", "configurations": [ { - "name": "C++ Launch", + "name": "C++ Debug", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/142bot", "args": [], "environment": [], "cwd": "${workspaceFolder}" - } + }, ] } \ No newline at end of file diff --git a/include/142bot/db.hpp b/include/142bot/db.hpp index 0a82b84..505d6db 100644 --- a/include/142bot/db.hpp +++ b/include/142bot/db.hpp @@ -78,7 +78,7 @@ namespace pqxx static asdf::timestamp from_string( std::string_view &text ) { asdf::timestamp ts; - if( !asdf::from_iso8601_str( std::string{ text } + "00", ts ) ) + if( !asdf::from_iso8601_str( std::string{ text }, ts ) ) throw argument_error{ "Failed conversion to " + static_cast< std::string >( name() ) @@ -96,7 +96,7 @@ namespace pqxx } static char* into_buf(char* begin, char* end, asdf::timestamp const &value) { - return pqxx::internal::generic_into_buf(begin, end, value); + return pqxx::string_traits::into_buf(begin, end, asdf::to_iso8601_str(value)); } static zview to_buf(char* begin, char* end, asdf::timestamp const &value) { @@ -104,7 +104,7 @@ namespace pqxx } static std::size_t size_buffer(asdf::timestamp const &value) noexcept { - return pqxx::string_traits::size_buffer(asdf::to_iso8601_str(value)) + 1; + return pqxx::string_traits::size_buffer(asdf::to_iso8601_str(value)); } }; diff --git a/modules/spotify/spotify.cpp b/modules/spotify/spotify.cpp index 8df83a4..e00a83b 100644 --- a/modules/spotify/spotify.cpp +++ b/modules/spotify/spotify.cpp @@ -32,6 +32,7 @@ class SpotifyModule: public Module { std::string spotifyRegex; std::string defaultSpotifyAccount; std::string spotifyBaseUrl; + std::string spotifyDefaultDevice; public: SpotifyModule(Bot* creator, ModuleLoader* ml) : Module(creator, ml) { ml->attach({I_OnMessage, I_OnCommand}, this); @@ -39,6 +40,7 @@ public: this->spotifyRegex = "^https:\/\/open.spotify.com\/track\/([a-zA-Z0-9]+)(.*)$"; this->defaultSpotifyAccount = "1"; this->spotifyBaseUrl = "https://api.spotify.com/v1"; + this->spotifyDefaultDevice = ""; } virtual std::string version() { @@ -120,7 +122,7 @@ public: * Performs a GET request to the Spotify API */ json spotify_get(const std::string route) { - bot->core->log(dpp::ll_debug, fmt::format("Making GET request to {}", route)); + bot->core->log(dpp::ll_debug, fmt::format("Making GET request to {}/{}", this->spotifyBaseUrl, route)); std::string token = get_spotify_token(); bot->core->log(dpp::ll_trace, "Obtained token."); @@ -138,6 +140,31 @@ public: return json::parse(r.text); } + void spotify_post(const std::string route) { + return spotify_post(route, 200); + } + + /** + * Performs a POST request to the Spotify API + */ + void spotify_post(const std::string route, int expected_code) { + bot->core->log(dpp::ll_debug, fmt::format("Making spotify POST to {}", route)); + std::string token = get_spotify_token(); + bot->core->log(dpp::ll_trace, "Obtained Token."); + + cpr::Response r = cpr::Post(cpr::Url{fmt::format("{}/{}", this->spotifyBaseUrl, route).c_str()}, + cpr::Bearer{token}); + bot->core->log(dpp::ll_trace, fmt::format("Made request. Code: {}", r.status_code)); + + if (r.status_code != expected_code) { + bot->core->log(dpp::ll_error, fmt::format("Spotify API Error: {}", r.text)); + throw std::exception(); + } + + return; + } + + virtual bool OnMessage(const dpp::message_create_t &message, const std::string& clean_message, bool mentioned, const std::vector & mentions) { sentry_set_tag("module", "spotify"); bot->core->log(dpp::ll_debug, "Got message event"); @@ -150,10 +177,15 @@ public: try { json res = spotify_get("tracks/" + match.str(1)); + std::string post_rt = fmt::format("me/player/queue?uri=spotify:track:{}{}", match.str(1), this->spotifyDefaultDevice != "" ? "&device_id=" + this->spotifyDefaultDevice : ""); + + spotify_post(post_rt, 204); + dpp::embed embed = dpp::embed() .set_title(res["name"]) - .set_author(res["artists"][0]["name"], "", "") - .set_image(res["album"]["images"][0]["url"]); + .set_author(res["artists"][0]["name"], res["artists"][0]["external_urls"]["spotify"], "") + .set_thumbnail(res["album"]["images"][0]["url"]) + .set_description("Added to the queue!"); bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id)); return true; @@ -194,7 +226,7 @@ public: bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id)); return true; - } else if (subcommand == "default") { + } else if (subcommand == "account") { pqxx::work tx(bot->conn); try { @@ -216,6 +248,52 @@ public: } EmbedSuccess("Updated default account.", message.msg.channel_id); + }else if (subcommand == "devices") { + json res = spotify_get("me/player/devices"); + + auto devices = res["devices"]; + dpp::embed embed = dpp::embed(). + set_color(dpp::colors::green) + .set_title("Spotify Devices") + .set_description("List of Spotify devices. Username is field ID and ID is field content.\n\nCurrent default account ID: " + this->defaultSpotifyAccount); + + + for (int i = 0; i < devices.size(); i++) { + std::string name = fmt::format("{} ({})", devices[i]["name"], devices[i]["id"].get()); + std::string content = fmt::format("{} {}", devices[i]["type"].get(), devices[i]["id"].get() == this->spotifyDefaultDevice ? "(Default)" : ""); + embed.add_field(name, content, true); + } + bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id)); + + } else if (subcommand == "device") { + json res = spotify_get("me/player/devices"); + + auto devices = res["devices"]; + + for (int i = 0; i < devices.size(); i++) { + if (devices[i]["id"] == params[2]) { + this->spotifyDefaultDevice = params[2]; + EmbedSuccess("Changed default spotify device", message.msg.channel_id); + return true; + } + } + + EmbedError("Invalid ID", message.msg.channel_id); + } else if (subcommand == "status") { + auto res = spotify_get("me/player"); + + auto item = res["item"]; + auto device = res["device"]; + auto album = item["album"]; + + dpp::embed embed = dpp::embed() + .set_author(item["artists"][0]["name"], item["artists"][0]["external_urls"]["spotify"], "") + .set_title(fmt::format("\"{}\" on {} by {}", item["name"], album["name"], album["artists"][0]["name"])) + .set_thumbnail(album["images"][0]["url"]) + .add_field("Status", res["is_playing"].get() ? "Playing" : "Paused", true) + .add_field("Repeating?", res["repeat_state"], true); + + bot->core->message_create(dpp::message(message.msg.channel_id, embed).set_reference(message.msg.id)); } else { EmbedError("Unknown Command", message.msg.channel_id); } diff --git a/src/db.cpp b/src/db.cpp index 73a7f02..120c9d4 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -106,13 +106,13 @@ namespace asdf bool from_iso8601_str( const std::string& s, timestamp& ts ) { std::istringstream stream{ s }; - stream >> date::parse( "%F %T%z", ts ); + stream >> date::parse( "%F %T", ts ); return !stream.fail(); } std::string to_iso8601_str( const timestamp& ts ) { - return date::format( "%F %T%z", ts ); + return date::format( "%F %T", ts ); } std::string to_http_ts_str( const timestamp& ts ) -- cgit v1.2.3