#pragma once #include "providers/twitch/twitchaccount.hpp" #include "providers/twitch/twitchserver.hpp" #include "singletons/helper/pubsubactions.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chatterino { namespace singletons { using WebsocketClient = websocketpp::client; using WebsocketHandle = websocketpp::connection_hdl; using WebsocketErrorCode = websocketpp::lib::error_code; #define MAX_PUBSUB_LISTENS 50 #define MAX_PUBSUB_CONNECTIONS 10 struct Listener { std::string topic; bool authed; bool persistent; bool confirmed = false; }; class PubSubClient : public std::enable_shared_from_this { WebsocketClient &websocketClient; WebsocketHandle handle; uint16_t numListens = 0; std::vector listeners; std::atomic awaitingPong{false}; std::atomic started{false}; public: PubSubClient(WebsocketClient &_websocketClient, WebsocketHandle _handle); void Start(); void Stop(); bool Listen(rapidjson::Document &message); void UnlistenPrefix(const std::string &prefix); void HandlePong(); bool isListeningToTopic(const std::string &topic); private: void Ping(); bool Send(const char *payload); }; class PubSubManager { PubSubManager(); using WebsocketMessagePtr = websocketpp::config::asio_tls_client::message_type::ptr; using WebsocketContextPtr = websocketpp::lib::shared_ptr; template using Signal = pajlada::Signals::Signal; // type-id is vector> WebsocketClient websocketClient; std::unique_ptr mainThread; public: enum class State { Connected, Disconnected, }; static PubSubManager &getInstance(); void Start(); bool IsConnected() const { return this->state == State::Connected; } pajlada::Signals::NoArgSignal connected; struct { struct { Signal chatCleared; Signal modeChanged; Signal moderationStateChanged; Signal userBanned; Signal userUnbanned; } moderation; struct { // Parsing should be done in PubSubManager as well, // but for now we just send the raw data Signal received; Signal sent; } whisper; } sig; void ListenToWhispers(std::shared_ptr account); void UnlistenAllModerationActions(); void ListenToChannelModerationActions( const QString &channelID, std::shared_ptr account); std::vector> requests; private: void listenToTopic(const std::string &topic, std::shared_ptr account); void Listen(rapidjson::Document &&msg); bool TryListen(rapidjson::Document &msg); bool isListeningToTopic(const std::string &topic); void AddClient(); State state = State::Connected; std::map, std::owner_less> clients; std::unordered_map> moderationActionHandlers; void OnMessage(websocketpp::connection_hdl hdl, WebsocketMessagePtr msg); void OnConnectionOpen(websocketpp::connection_hdl hdl); void OnConnectionClose(websocketpp::connection_hdl hdl); WebsocketContextPtr OnTLSInit(websocketpp::connection_hdl hdl); void HandleListenResponse(const rapidjson::Document &msg); void HandleMessageResponse(const rapidjson::Value &data); void RunThread(); }; } // namespace singletons } // namespace chatterino