#pragma once #include "websocket.hpp" #include "http.hpp" #include "objects.hpp" #include "store.hpp" #include #include #include #include #include #include #include #include // bruh #ifdef GetMessage #undef GetMessage #endif // https://stackoverflow.com/questions/29775153/stopping-long-sleep-threads/29775639#29775639 class HeartbeatWaiter { public: template bool wait_for(std::chrono::duration const &time) const { std::unique_lock lock(m); return !cv.wait_for(lock, time, [&] { return terminate; }); } void kill() { std::unique_lock lock(m); terminate = true; cv.notify_all(); } private: mutable std::condition_variable cv; mutable std::mutex m; bool terminate = false; }; class Abaddon; class DiscordClient { friend class Abaddon; public: static const constexpr char *DiscordGateway = "wss://gateway.discord.gg/?v=6&encoding=json&compress=zlib-stream"; static const constexpr char *DiscordAPI = "https://discord.com/api"; static const constexpr char *GatewayIdentity = "Discord"; public: DiscordClient(); void Start(); void Stop(); bool IsStarted() const; using guilds_type = Store::guilds_type; using channels_type = Store::channels_type; using messages_type = Store::messages_type; using users_type = Store::users_type; using roles_type = Store::roles_type; using members_type = Store::members_type; const guilds_type &GetGuilds() const; const UserData &GetUserData() const; const UserSettingsData &GetUserSettings() const; std::vector GetUserSortedGuilds() const; std::set GetMessagesForChannel(Snowflake id) const; std::set GetPrivateChannels() const; void UpdateSettingsGuildPositions(const std::vector &pos); void FetchMessagesInChannel(Snowflake id, std::function &)> cb); void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function &)> cb); const MessageData *GetMessage(Snowflake id) const; const ChannelData *GetChannel(Snowflake id) const; const UserData *GetUser(Snowflake id) const; const RoleData *GetRole(Snowflake id) const; const GuildData *GetGuild(Snowflake id) const; Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const; std::unordered_set GetUsersInGuild(Snowflake id) const; void SendChatMessage(std::string content, Snowflake channel); void DeleteMessage(Snowflake channel_id, Snowflake id); void EditMessage(Snowflake channel_id, Snowflake id, std::string content); void SendLazyLoad(Snowflake id); void UpdateToken(std::string token); private: static const constexpr int InflateChunkSize = 0x10000; std::vector m_compressed_buf; std::vector m_decompress_buf; z_stream m_zstream; void HandleGatewayMessageRaw(std::string str); void HandleGatewayMessage(std::string str); void HandleGatewayReady(const GatewayMessage &msg); void HandleGatewayMessageCreate(const GatewayMessage &msg); void HandleGatewayMessageDelete(const GatewayMessage &msg); void HandleGatewayMessageUpdate(const GatewayMessage &msg); void HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg); void HeartbeatThread(); void SendIdentify(); bool CheckCode(const cpr::Response &r); std::string m_token; void AddMessageToChannel(Snowflake msg_id, Snowflake channel_id); std::unordered_map> m_chan_to_message_map; void AddUserToGuild(Snowflake user_id, Snowflake guild_id); std::unordered_map> m_guild_to_users; UserData m_user_data; UserSettingsData m_user_settings; Store m_store; HTTPClient m_http; Websocket m_websocket; std::atomic m_client_connected = false; std::atomic m_ready_received = false; std::unordered_map m_event_map; void LoadEventMap(); std::thread m_heartbeat_thread; std::atomic m_last_sequence = -1; std::atomic m_heartbeat_msec = 0; HeartbeatWaiter m_heartbeat_waiter; std::atomic m_heartbeat_acked = true; // signals public: typedef sigc::signal type_signal_gateway_ready; typedef sigc::signal type_signal_channel_list_refresh; typedef sigc::signal type_signal_message_create; typedef sigc::signal type_signal_message_delete; typedef sigc::signal type_signal_message_update; typedef sigc::signal type_signal_guild_member_list_update; type_signal_gateway_ready signal_gateway_ready(); type_signal_channel_list_refresh signal_channel_list_refresh(); type_signal_message_create signal_message_create(); type_signal_message_delete signal_message_delete(); type_signal_message_update signal_message_update(); type_signal_guild_member_list_update signal_guild_member_list_update(); protected: type_signal_gateway_ready m_signal_gateway_ready; type_signal_channel_list_refresh m_signal_channel_list_refresh; type_signal_message_create m_signal_message_create; type_signal_message_delete m_signal_message_delete; type_signal_message_update m_signal_message_update; type_signal_guild_member_list_update m_signal_guild_member_list_update; };