#ifndef GATEWAY_WEBSOCKET_HPP_ #define GATEWAY_WEBSOCKET_HPP_ #include #include #include class EventEmitter { private: using eventHandlers = std::function; std::unordered_map> handlers; protected: void emit(const std::string& event, const nlohmann::json& data) { if (handlers.find(event) != handlers.end()) { for (const auto& handler : handlers[event]) { handler(data); } } } public: void on(const std::string& event, eventHandlers handler) { handlers[event].emplace_back(handler); } void once(const std::string& event, eventHandlers handler) { auto wrappedHandler = [this, event, handler](const nlohmann::json& data) { handler(data); off(event, handler); }; handlers[event].emplace_back(wrappedHandler); } void off(const std::string& event, eventHandlers handler) { if (handlers.find(event) != handlers.end()) { auto& vec = handlers[event]; vec.erase(std::remove_if(vec.begin(), vec.end(), [&handler](const eventHandlers& h) { return h.target() == handler.target(); }), vec.end()); } } }; class WebSocket : public EventEmitter { private: std::string token; int intents; bool isBot; ix::WebSocket webSocket; const nlohmann::json payload = { {"op", 1}, {"d", nullptr} }; WebSocket& operator=(const WebSocket&) = delete; WebSocket& operator=(WebSocket&&) = delete; WebSocket(WebSocket&&) = delete; WebSocket(const WebSocket&) = delete; WebSocket(const std::string& token = "", const int& intents = 0, const bool& isBot = true) : token(token), intents(intents), isBot(isBot) { nlohmann::json id = { {"op", 2}, {"d", { {"token", token}, {"intents", intents}, {"properties", { {"os", "linux"}, {"browser", "firefox"}, {"device", "firefox"} }}, {"compress", 1}, {"presence", { {"activities", nlohmann::json::array({ { {"name", "meow"}, {"type", 2} } })}, {"status", "idle"}, {"since", 91879201}, {"afk", false} }} }} }; ix::initNetSystem(); webSocket.setUrl("wss://gateway.discord.gg/?v=10&encoding=json"); //webSocket.disableAutomaticReconnection(); Log::create(INFO, WEBSOCKET, "ixwebsocket init"); webSocket.setOnMessageCallback([this, res = nlohmann::json(), heartbeat_interval = 0, connected = false, id](const ix::WebSocketMessagePtr& msg) mutable { switch (msg->type) { case ix::WebSocketMessageType::Message: res = nlohmann::json::parse(msg->str); Log::create(INFO, WEBSOCKET, res["op"].dump() + " " + res["t"].dump()); switch (res["op"].get()) { case GatewayOpcodes::Hello: heartbeat_interval = res["d"]["heartbeat_interval"].get(); !connected ? connected = true, webSocket.send(id.dump()) : 0; std::thread([this, &heartbeat_interval, &connected]() { while (connected && heartbeat_interval != -1) { Log::create(INFO, WEBSOCKET, "Heartbeat " + std::to_string(heartbeat_interval)); std::this_thread::sleep_for(std::chrono::milliseconds(heartbeat_interval)); webSocket.send(payload.dump()); } }).detach(); break; case GatewayOpcodes::Dispatch: emit(res["t"].get(), res); break; default: break; } break; case ix::WebSocketMessageType::Error: std::cout << msg->errorInfo.reason << std::endl; exit(-1); break; default: break; } }); webSocket.start(); } public: [[maybe_unused]] void sendPresenceUpdate(const int& statusType, const std::string& activityName) { nlohmann::json prsUpdate = { {"op", 3}, {"d", { {"since", 0}, {"activities", nlohmann::json::array({{"name", activityName}, {"type", 2}})}, {"status", statusType == 1 ? "online" : "idle"}, {"afk", false} }} }; webSocket.send(prsUpdate.dump()); } static WebSocket& getInstance(const std::string& token = "", const int& intents = 0, const bool& bot = true) { Log::create(INFO, WEBSOCKET, "Instance event"); static WebSocket instance(token, intents, bot); return instance; } ~WebSocket() { webSocket.close(); ix::uninitNetSystem(); } std::string getToken() const { return isBot ? std::string("Bot " + WebSocket::token) : WebSocket::token; } int getIntents() const { return WebSocket::intents; } void start() { while (1) std::this_thread::sleep_for(std::chrono::milliseconds(1)); } }; #endif