#include "emotespp/seventv.hpp" #include #include #include #include namespace emotespp { SevenTVWebsocketClient::SevenTVWebsocketClient() { this->websocket.setUrl("wss://events.7tv.io/v3"); this->websocket.enableAutomaticReconnection(); this->websocket.setOnMessageCallback( [this](const ix::WebSocketMessagePtr &msg) { switch (msg->type) { case ix::WebSocketMessageType::Message: { nlohmann::json j = nlohmann::json::parse(msg->str); int opcode = j["op"].get(); // hello opcode if (opcode == 1) { this->is_connected = true; std::for_each(this->ids.begin(), this->ids.end(), [this](const std::string &id) { this->subscribe_emote_set(id); }); return; } else if (opcode != 0) { return; } nlohmann::json data = j["d"]; std::string type = data["type"]; if (type != "emote_set.update") { return; } data = data["body"]; std::string channel_id = data["id"]; nlohmann::json actor = data["actor"]; std::string actor_id = actor["id"]; if (data.contains("pushed") && this->emote_create.has_value()) { for (const nlohmann::json &d : data["pushed"]) { nlohmann::json emote = d["value"]; this->emote_create.value()(channel_id, actor_id, this->create_emote(emote)); } } if (data.contains("pulled") && this->emote_delete.has_value()) { for (const nlohmann::json &d : data["pulled"]) { nlohmann::json emote = d["old_value"]; this->emote_delete.value()(channel_id, actor_id, this->create_emote(emote)); } } if (data.contains("updated") && this->emote_update.has_value()) { for (const nlohmann::json &d : data["updated"]) { nlohmann::json old_emote = d["old_value"]; nlohmann::json emote = d["value"]; std::string id = old_emote["id"]; std::string code = emote["name"]; std::optional original_code = old_emote["name"]; if (code == original_code) { original_code = std::nullopt; } Emote emote_data{id, code, original_code}; this->emote_update.value()(channel_id, actor_id, emote_data); } } break; } case ix::WebSocketMessageType::Close: case ix::WebSocketMessageType::Error: { this->is_connected = false; break; } case ix::WebSocketMessageType::Open: case ix::WebSocketMessageType::Ping: case ix::WebSocketMessageType::Pong: case ix::WebSocketMessageType::Fragment: break; } }); } void SevenTVWebsocketClient::subscribe_emote_set( const std::string &emote_set_id) { if (!std::any_of(this->ids.begin(), this->ids.end(), [&emote_set_id](const std::string &id) { return id == emote_set_id; })) { this->ids.push_back(emote_set_id); } if (this->is_connected) { nlohmann::json j; j["op"] = 35; nlohmann::json d; nlohmann::json c; c["object_id"] = emote_set_id; d["type"] = "emote_set.update"; d["condition"] = c; j["d"] = d; this->websocket.sendUtf8Text(j.dump()); } } void SevenTVWebsocketClient::unsubscribe_emote_set( const std::string &emote_set_id) { if (this->is_connected) { nlohmann::json j; j["op"] = 36; nlohmann::json d; nlohmann::json c; c["object_id"] = emote_set_id; d["type"] = "emote_set.update"; d["condition"] = c; j["d"] = d; this->websocket.sendUtf8Text(j.dump()); } auto it = std::find_if( this->ids.begin(), this->ids.end(), [&emote_set_id](const std::string &x) { return x == emote_set_id; }); if (it != this->ids.end()) { this->ids.erase(it); } } void SevenTVWebsocketClient::start() { this->websocket.start(); } const std::vector &SevenTVWebsocketClient::get_ids() const { return this->ids; } Emote SevenTVWebsocketClient::create_emote(const nlohmann::json &data) { std::string id = data["id"]; std::string code = data["name"]; std::optional original_code = data["data"]["name"]; if (code == original_code) { original_code = std::nullopt; } return {id, code, original_code}; } std::vector SevenTVAPIClient::parse_emoteset( const nlohmann::json &value) const { std::vector emotes; nlohmann::json e = value["emotes"]; for (const nlohmann::json v : e) { std::string id = v["id"]; std::string code = v["name"]; std::optional original_code = v["data"]["name"]; if (code == original_code) { original_code = std::nullopt; } emotes.push_back({id, code, original_code}); } return emotes; } std::optional 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 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 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 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 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"]}; } }