summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoderndevslulw <moderndevslulw@alright.party>2025-04-04 01:12:56 +0500
committermoderndevslulw <moderndevslulw@alright.party>2025-04-04 01:12:56 +0500
commitb19cee38da6bcdbaa8cd3a4115c0f24e7dadc9cd (patch)
treebab265f888a37838607eb688924fb87640ef26ab
parentee132dc6c326708706e0c88ba234a90aec7ee936 (diff)
feat: 7TV websocket client
-rw-r--r--include/emotespp/seventv.hpp25
-rw-r--r--src/seventv.cpp162
2 files changed, 184 insertions, 3 deletions
diff --git a/include/emotespp/seventv.hpp b/include/emotespp/seventv.hpp
index 8a4149d..cce8ae7 100644
--- a/include/emotespp/seventv.hpp
+++ b/include/emotespp/seventv.hpp
@@ -1,7 +1,28 @@
#pragma once
+#include <nlohmann/json.hpp>
+#include <string>
+#include <vector>
+
+#include "emotespp/emotes.hpp"
+#include "ixwebsocket/IXWebSocket.h"
+
namespace emotespp {
- class SevenTVEmoteListener {
- SevenTVEmoteListener();
+ class SevenTVWebsocketClient : public RetrieveEmoteWebsocket<Emote> {
+ public:
+ SevenTVWebsocketClient();
+
+ void subscribe_emote_set(const std::string &emote_set_id);
+ void unsubscribe_emote_set(const std::string &emote_set_id);
+
+ void start();
+
+ private:
+ Emote create_emote(const nlohmann::json &data);
+
+ std::vector<std::string> ids;
+ ix::WebSocket websocket;
+
+ bool is_connected = false;
};
} \ No newline at end of file
diff --git a/src/seventv.cpp b/src/seventv.cpp
index 1fae55e..51c306c 100644
--- a/src/seventv.cpp
+++ b/src/seventv.cpp
@@ -1,5 +1,165 @@
#include "emotespp/seventv.hpp"
+#include <algorithm>
+#include <nlohmann/json.hpp>
+#include <optional>
+
namespace emotespp {
- SevenTVEmoteListener::SevenTVEmoteListener() {}
+ 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<int>();
+
+ // 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<std::string> 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(); }
+
+ Emote SevenTVWebsocketClient::create_emote(const nlohmann::json &data) {
+ std::string id = data["id"];
+ std::string code = data["name"];
+ std::optional<std::string> original_code = data["data"]["name"];
+
+ if (code == original_code) {
+ original_code = std::nullopt;
+ }
+
+ return {id, code, original_code};
+ }
} \ No newline at end of file