diff options
| author | moderndevslulw <moderndevslulw@alright.party> | 2025-04-04 14:30:36 +0500 |
|---|---|---|
| committer | moderndevslulw <moderndevslulw@alright.party> | 2025-04-04 14:30:36 +0500 |
| commit | a44fc682a5f2eb54dfac6705ff21d0778639af6a (patch) | |
| tree | a7b3d624bf89ed5b05a6890a00c40e22d9445b45 | |
| parent | b19cee38da6bcdbaa8cd3a4115c0f24e7dadc9cd (diff) | |
feat: 7TV API client
| -rw-r--r-- | include/emotespp/emotes.hpp | 9 | ||||
| -rw-r--r-- | include/emotespp/seventv.hpp | 62 | ||||
| -rw-r--r-- | src/seventv.cpp | 93 |
3 files changed, 164 insertions, 0 deletions
diff --git a/include/emotespp/emotes.hpp b/include/emotespp/emotes.hpp index 5a5e172..4f08f39 100644 --- a/include/emotespp/emotes.hpp +++ b/include/emotespp/emotes.hpp @@ -3,6 +3,7 @@ #include <functional> #include <optional> #include <string> +#include <vector> namespace emotespp { struct Emote { @@ -45,4 +46,12 @@ namespace emotespp { std::function<void(std::string, std::optional<std::string>, T)>> emote_update; }; + + template <typename T> + class RetrieveEmoteAPI { + public: + virtual std::vector<T> get_channel_emotes( + std::string &channel_id) const = 0; + virtual std::vector<T> get_global_emotes() const = 0; + }; }
\ No newline at end of file diff --git a/include/emotespp/seventv.hpp b/include/emotespp/seventv.hpp index cce8ae7..77db912 100644 --- a/include/emotespp/seventv.hpp +++ b/include/emotespp/seventv.hpp @@ -1,9 +1,12 @@ #pragma once #include <nlohmann/json.hpp> +#include <optional> +#include <stdexcept> #include <string> #include <vector> +#include "cpr/cpr.h" #include "emotespp/emotes.hpp" #include "ixwebsocket/IXWebSocket.h" @@ -25,4 +28,63 @@ namespace emotespp { bool is_connected = false; }; + + struct User { + std::string alias_id, id, username, emote_set_id; + }; + + struct EmoteSet { + std::string id, name; + User owner; + std::vector<Emote> emotes; + }; + + class SevenTVAPIClient : public RetrieveEmoteAPI<Emote> { + public: + SevenTVAPIClient() = default; + ~SevenTVAPIClient() = default; + + std::vector<Emote> get_channel_emotes( + std::string &channel_id) const override { + cpr::Response r = + cpr::Get(cpr::Url{base_url + "/users/twitch/" + channel_id}); + + if (r.status_code != 200) { + throw std::runtime_error( + "Failed to get channel emotes. Status code: " + + std::to_string(r.status_code)); + } + + nlohmann::json j = nlohmann::json::parse(r.text); + nlohmann::json set = j["emote_set"]; + + return this->parse_emoteset(set); + } + + std::vector<Emote> get_global_emotes() const override { + cpr::Response r = cpr::Get(cpr::Url{base_url + "/emote-sets/global"}); + + if (r.status_code != 200) { + throw std::runtime_error( + "Failed to get global emotes. Status code: " + + std::to_string(r.status_code)); + } + + nlohmann::json j = nlohmann::json::parse(r.text); + + return this->parse_emoteset(j); + } + + std::optional<User> get_user_by_twitch_id( + const unsigned int &twitch_id) const; + std::optional<User> get_user(const std::string &id) const; + std::optional<EmoteSet> get_emote_set( + const std::string &emote_set_id) const; + + private: + std::vector<Emote> parse_emoteset(const nlohmann::json &value) const; + std::optional<User> parse_user(const nlohmann::json &value) const; + + const std::string base_url = "https://7tv.io/v3"; + }; }
\ No newline at end of file diff --git a/src/seventv.cpp b/src/seventv.cpp index 51c306c..62ea550 100644 --- a/src/seventv.cpp +++ b/src/seventv.cpp @@ -3,6 +3,7 @@ #include <algorithm> #include <nlohmann/json.hpp> #include <optional> +#include <string> namespace emotespp { SevenTVWebsocketClient::SevenTVWebsocketClient() { @@ -162,4 +163,96 @@ namespace emotespp { return {id, code, original_code}; } + + std::vector<Emote> SevenTVAPIClient::parse_emoteset( + const nlohmann::json &value) const { + std::vector<Emote> emotes; + + nlohmann::json e = value["emotes"]; + + for (const nlohmann::json v : e) { + std::string id = v["id"]; + std::string code = v["name"]; + std::optional<std::string> original_code = v["data"]["name"]; + + if (code == original_code) { + original_code = std::nullopt; + } + + emotes.push_back({id, code, original_code}); + } + + return emotes; + } + + std::optional<User> SevenTVAPIClient::get_user_by_twitch_id( + const unsigned int &twitch_id) const { + cpr::Response r = cpr::Get(cpr::Url{this->base_url + "/users/twitch/" + + std::to_string(twitch_id)}); + + if (r.status_code != 200) { + return std::nullopt; + } + + nlohmann::json j = nlohmann::json::parse(r.text); + + std::string alias_id = j["id"]; + std::string username = j["username"]; + std::string emote_set_id = j["emote_set_id"]; + std::string id = j["user"]["id"]; + + return (User){alias_id, id, username, emote_set_id}; + } + + std::optional<User> SevenTVAPIClient::get_user(const std::string &id) const { + cpr::Response r = cpr::Get(cpr::Url{this->base_url + "/users/" + id}); + + if (r.status_code != 200) { + return std::nullopt; + } + + nlohmann::json j = nlohmann::json::parse(r.text); + + return this->parse_user(j); + } + + std::optional<EmoteSet> SevenTVAPIClient::get_emote_set( + const std::string &emote_set_id) const { + cpr::Response r = + cpr::Get(cpr::Url{this->base_url + "/emote-sets/" + emote_set_id}); + + if (r.status_code != 200) { + return std::nullopt; + } + + nlohmann::json j = nlohmann::json::parse(r.text); + + std::string id = j["id"]; + std::string name = j["name"]; + std::optional<User> owner = this->parse_user(j["owner"]); + + if (!owner.has_value()) { + return std::nullopt; + } + + return (EmoteSet){id, name, owner.value(), this->parse_emoteset(j)}; + } + + std::optional<User> SevenTVAPIClient::parse_user( + const nlohmann::json &value) const { + std::string id = value["id"]; + + nlohmann::json conns = value["connections"]; + auto twitchconn = std::find_if( + conns.begin(), conns.end(), + [](const nlohmann::json &x) { return x["platform"] == "TWITCH"; }); + + if (twitchconn == conns.end()) { + return std::nullopt; + } + + nlohmann::json c = *twitchconn; + + return (User){c["id"], id, c["username"], c["emote_set_id"]}; + } }
\ No newline at end of file |
