From a51a54bc5979a2491f152abc47ad54e6b63f27c8 Mon Sep 17 00:00:00 2001 From: Dylam De La Torre Date: Tue, 23 Nov 2021 05:21:56 +0100 Subject: Restructure source and resource files (#46) importantly, res is now res/res and css is now res/css --- discord/activity.cpp | 116 --- discord/activity.hpp | 137 --- discord/auditlog.cpp | 34 - discord/auditlog.hpp | 120 --- discord/ban.cpp | 6 - discord/ban.hpp | 10 - discord/channel.cpp | 97 -- discord/channel.hpp | 92 -- discord/discord.cpp | 2310 ---------------------------------------------- discord/discord.hpp | 449 --------- discord/emoji.cpp | 51 - discord/emoji.hpp | 25 - discord/errors.hpp | 36 - discord/guild.cpp | 221 ----- discord/guild.hpp | 100 -- discord/httpclient.cpp | 139 --- discord/httpclient.hpp | 39 - discord/interactions.cpp | 11 - discord/interactions.hpp | 25 - discord/invite.cpp | 39 - discord/invite.hpp | 41 - discord/json.hpp | 148 --- discord/member.cpp | 40 - discord/member.hpp | 27 - discord/message.cpp | 265 ------ discord/message.hpp | 218 ----- discord/objects.cpp | 534 ----------- discord/objects.hpp | 747 --------------- discord/permissions.cpp | 11 - discord/permissions.hpp | 224 ----- discord/relationship.cpp | 6 - discord/relationship.hpp | 21 - discord/role.cpp | 14 - discord/role.hpp | 20 - discord/snowflake.cpp | 67 -- discord/snowflake.hpp | 55 -- discord/sticker.cpp | 52 -- discord/sticker.hpp | 40 - discord/store.cpp | 2232 -------------------------------------------- discord/store.hpp | 302 ------ discord/user.cpp | 197 ---- discord/user.hpp | 82 -- discord/usersettings.cpp | 40 - discord/usersettings.hpp | 47 - discord/webhook.cpp | 13 - discord/webhook.hpp | 24 - discord/websocket.cpp | 66 -- discord/websocket.hpp | 41 - 48 files changed, 9631 deletions(-) delete mode 100644 discord/activity.cpp delete mode 100644 discord/activity.hpp delete mode 100644 discord/auditlog.cpp delete mode 100644 discord/auditlog.hpp delete mode 100644 discord/ban.cpp delete mode 100644 discord/ban.hpp delete mode 100644 discord/channel.cpp delete mode 100644 discord/channel.hpp delete mode 100644 discord/discord.cpp delete mode 100644 discord/discord.hpp delete mode 100644 discord/emoji.cpp delete mode 100644 discord/emoji.hpp delete mode 100644 discord/errors.hpp delete mode 100644 discord/guild.cpp delete mode 100644 discord/guild.hpp delete mode 100644 discord/httpclient.cpp delete mode 100644 discord/httpclient.hpp delete mode 100644 discord/interactions.cpp delete mode 100644 discord/interactions.hpp delete mode 100644 discord/invite.cpp delete mode 100644 discord/invite.hpp delete mode 100644 discord/json.hpp delete mode 100644 discord/member.cpp delete mode 100644 discord/member.hpp delete mode 100644 discord/message.cpp delete mode 100644 discord/message.hpp delete mode 100644 discord/objects.cpp delete mode 100644 discord/objects.hpp delete mode 100644 discord/permissions.cpp delete mode 100644 discord/permissions.hpp delete mode 100644 discord/relationship.cpp delete mode 100644 discord/relationship.hpp delete mode 100644 discord/role.cpp delete mode 100644 discord/role.hpp delete mode 100644 discord/snowflake.cpp delete mode 100644 discord/snowflake.hpp delete mode 100644 discord/sticker.cpp delete mode 100644 discord/sticker.hpp delete mode 100644 discord/store.cpp delete mode 100644 discord/store.hpp delete mode 100644 discord/user.cpp delete mode 100644 discord/user.hpp delete mode 100644 discord/usersettings.cpp delete mode 100644 discord/usersettings.hpp delete mode 100644 discord/webhook.cpp delete mode 100644 discord/webhook.hpp delete mode 100644 discord/websocket.cpp delete mode 100644 discord/websocket.hpp (limited to 'discord') diff --git a/discord/activity.cpp b/discord/activity.cpp deleted file mode 100644 index 95dda5d..0000000 --- a/discord/activity.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "activity.hpp" - -void from_json(const nlohmann::json &j, ActivityTimestamps &m) { - JS_O("start", m.Start); - JS_O("end", m.End); -} - -void to_json(nlohmann::json &j, const ActivityTimestamps &m) { - JS_IF("start", m.Start); - JS_IF("end", m.End); -} - -void from_json(const nlohmann::json &j, ActivityEmoji &m) { - JS_D("name", m.Name); - JS_O("id", m.ID); - JS_O("animated", m.IsAnimated); -} - -void to_json(nlohmann::json &j, const ActivityEmoji &m) { - j["name"] = m.Name; - if (m.ID.has_value()) - j["id"] = *m.ID; - if (m.IsAnimated.has_value()) - j["animated"] = *m.IsAnimated; -} - -void from_json(const nlohmann::json &j, ActivityParty &m) { - JS_O("id", m.ID); - JS_O("size", m.Size); -} - -void to_json(nlohmann::json &j, const ActivityParty &m) { - JS_IF("id", m.ID); - JS_IF("size", m.Size); -} - -void from_json(const nlohmann::json &j, ActivityAssets &m) { - JS_O("large_image", m.LargeImage); - JS_O("large_text", m.LargeText); - JS_O("small_image", m.SmallImage); - JS_O("small_text", m.SmallText); -} - -void to_json(nlohmann::json &j, const ActivityAssets &m) { - JS_IF("large_image", m.LargeImage); - JS_IF("large_text", m.LargeText); - JS_IF("small_image", m.SmallImage); - JS_IF("small_text", m.SmallText); -} - -void from_json(const nlohmann::json &j, ActivitySecrets &m) { - JS_O("join", m.Join); - JS_O("spectate", m.Spectate); - JS_O("match", m.Match); -} - -void to_json(nlohmann::json &j, const ActivitySecrets &m) { - JS_IF("join", m.Join); - JS_IF("spectate", m.Spectate); - JS_IF("match", m.Match); -} - -void from_json(const nlohmann::json &j, ActivityData &m) { - JS_D("name", m.Name); - JS_D("type", m.Type); - JS_ON("url", m.URL); - JS_D("created_at", m.CreatedAt); - JS_O("timestamps", m.Timestamps); - JS_O("application_id", m.ApplicationID); - JS_ON("details", m.Details); - JS_ON("state", m.State); - JS_ON("emoji", m.Emoji); - JS_ON("party", m.Party); - JS_O("assets", m.Assets); - JS_O("secrets", m.Secrets); - JS_O("instance", m.IsInstance); - JS_O("flags", m.Flags); -} - -void to_json(nlohmann::json &j, const ActivityData &m) { - if (m.Type == ActivityType::Custom) { - j["name"] = "Custom Status"; - j["state"] = m.Name; - } else { - j["name"] = m.Name; - JS_IF("state", m.State); - } - - j["type"] = m.Type; - JS_IF("url", m.URL); - JS_IF("created_at", m.CreatedAt); - JS_IF("timestamps", m.Timestamps); - JS_IF("application_id", m.ApplicationID); - JS_IF("details", m.Details); - JS_IF("emoji", m.Emoji); - JS_IF("party", m.Party); - JS_IF("assets", m.Assets); - JS_IF("secrets", m.Secrets); - JS_IF("instance", m.IsInstance); - JS_IF("flags", m.Flags); -} - -void from_json(const nlohmann::json &j, PresenceData &m) { - JS_N("activities", m.Activities); - JS_D("status", m.Status); -} - -void to_json(nlohmann::json &j, const PresenceData &m) { - j["activities"] = m.Activities; - j["status"] = m.Status; - JS_IF("afk", m.IsAFK); - if (m.Since.has_value()) - j["since"] = *m.Since; - else - j["since"] = 0; -} diff --git a/discord/activity.hpp b/discord/activity.hpp deleted file mode 100644 index 6b8e944..0000000 --- a/discord/activity.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once -#include -#include -#include "util.hpp" -#include "json.hpp" -#include "snowflake.hpp" - -enum class PresenceStatus : uint8_t { - Online, - Offline, - Idle, - DND, -}; - -constexpr inline const char *GetPresenceString(PresenceStatus s) { - switch (s) { - case PresenceStatus::Online: - return "online"; - case PresenceStatus::Offline: - return "offline"; - case PresenceStatus::Idle: - return "idle"; - case PresenceStatus::DND: - return "dnd"; - } - return ""; -} - -constexpr inline const char* GetPresenceDisplayString(PresenceStatus s) { - switch (s) { - case PresenceStatus::Online: - return "Online"; - case PresenceStatus::Offline: - return "Offline"; - case PresenceStatus::Idle: - return "Away"; - case PresenceStatus::DND: - return "Do Not Disturb"; - } - return ""; -} - -enum class ActivityType : int { - Game = 0, - Streaming = 1, - Listening = 2, - Watching = 3, // not documented - Custom = 4, - Competing = 5, -}; - -enum class ActivityFlags { - INSTANCE = (1 << 0), - JOIN = (1 << 1), - SPECTATE = (1 << 2), - JOIN_REQUEST = (1 << 3), - SYNC = (1 << 4), - PLAY = (1 << 5), -}; -template<> -struct Bitwise { - static const bool enable = true; -}; - -struct ActivityTimestamps { - std::optional Start; - std::optional End; - - friend void from_json(const nlohmann::json &j, ActivityTimestamps &m); - friend void to_json(nlohmann::json &j, const ActivityTimestamps &m); -}; - -struct ActivityEmoji { - std::string Name; - std::optional ID; - std::optional IsAnimated; - - friend void from_json(const nlohmann::json &j, ActivityEmoji &m); - friend void to_json(nlohmann::json &j, const ActivityEmoji &m); -}; - -struct ActivityParty { - std::optional ID; - std::optional> Size; - - friend void from_json(const nlohmann::json &j, ActivityParty &m); - friend void to_json(nlohmann::json &j, const ActivityParty &m); -}; - -struct ActivityAssets { - std::optional LargeImage; - std::optional LargeText; - std::optional SmallImage; - std::optional SmallText; - - friend void from_json(const nlohmann::json &j, ActivityAssets &m); - friend void to_json(nlohmann::json &j, const ActivityAssets &m); -}; - -struct ActivitySecrets { - std::optional Join; - std::optional Spectate; - std::optional Match; - - friend void from_json(const nlohmann::json &j, ActivitySecrets &m); - friend void to_json(nlohmann::json &j, const ActivitySecrets &m); -}; - -struct ActivityData { - std::string Name; // - ActivityType Type; // - std::optional URL; // null - std::optional CreatedAt; // - std::optional Timestamps; // - std::optional ApplicationID; // - std::optional Details; // null - std::optional State; // null - std::optional Emoji; // null - std::optional Party; // - std::optional Assets; // - std::optional Secrets; // - std::optional IsInstance; // - std::optional Flags; // - - friend void from_json(const nlohmann::json &j, ActivityData &m); - friend void to_json(nlohmann::json &j, const ActivityData &m); -}; - -struct PresenceData { - std::vector Activities; // null (but never sent as such) - std::string Status; - std::optional IsAFK; - std::optional Since; - - friend void from_json(const nlohmann::json &j, PresenceData &m); - friend void to_json(nlohmann::json &j, const PresenceData &m); -}; diff --git a/discord/auditlog.cpp b/discord/auditlog.cpp deleted file mode 100644 index bfada39..0000000 --- a/discord/auditlog.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "auditlog.hpp" - -void from_json(const nlohmann::json &j, AuditLogChange &m) { - JS_D("key", m.Key); - JS_O("old_value", m.OldValue); - JS_O("new_value", m.NewValue); -} - -void from_json(const nlohmann::json &j, AuditLogOptions &m) { - JS_O("delete_member_days", m.DeleteMemberDays); - JS_O("members_removed", m.MembersRemoved); - JS_O("channel_id", m.ChannelID); - JS_O("message_id", m.MessageID); - JS_O("count", m.Count); - JS_O("id", m.ID); - JS_O("type", m.Type); - JS_O("role_name", m.RoleName); -} - -void from_json(const nlohmann::json &j, AuditLogEntry &m) { - JS_N("target_id", m.TargetID); - JS_O("changes", m.Changes); - JS_N("user_id", m.UserID); - JS_D("id", m.ID); - JS_D("action_type", m.Type); - JS_O("options", m.Options); - JS_O("reason", m.Reason); -} - -void from_json(const nlohmann::json &j, AuditLogData &m) { - JS_D("audit_log_entries", m.Entries); - JS_D("users", m.Users); - JS_D("webhooks", m.Webhooks); -} diff --git a/discord/auditlog.hpp b/discord/auditlog.hpp deleted file mode 100644 index 3a902d1..0000000 --- a/discord/auditlog.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -#include "snowflake.hpp" -#include "user.hpp" -#include "json.hpp" -#include "webhook.hpp" - -enum class AuditLogActionType { - GUILD_UPDATE = 1, - CHANNEL_CREATE = 10, - CHANNEL_UPDATE = 11, - CHANNEL_DELETE = 12, - CHANNEL_OVERWRITE_CREATE = 13, - CHANNEL_OVERWRITE_UPDATE = 14, - CHANNEL_OVERWRITE_DELETE = 15, - MEMBER_KICK = 20, - MEMBER_PRUNE = 21, - MEMBER_BAN_ADD = 22, - MEMBER_BAN_REMOVE = 23, - MEMBER_UPDATE = 24, - MEMBER_ROLE_UPDATE = 25, - MEMBER_MOVE = 26, - MEMBER_DISCONNECT = 27, - BOT_ADD = 28, - ROLE_CREATE = 30, - ROLE_UPDATE = 31, - ROLE_DELETE = 32, - INVITE_CREATE = 40, - INVITE_UPDATE = 41, - INVITE_DELETE = 42, - WEBHOOK_CREATE = 50, - WEBHOOK_UPDATE = 51, - WEBHOOK_DELETE = 52, - EMOJI_CREATE = 60, - EMOJI_UPDATE = 61, - EMOJI_DELETE = 62, - MESSAGE_DELETE = 72, - MESSAGE_BULK_DELETE = 73, - MESSAGE_PIN = 74, - MESSAGE_UNPIN = 75, - INTEGRATION_CREATE = 80, - INTEGRATION_UPDATE = 81, - INTEGRATION_DELETE = 82, - STAGE_INSTANCE_CREATE = 83, - STAGE_INSTANCE_UPDATE = 84, - STAGE_INSTANCE_DELETE = 85, - STICKER_CREATE = 90, - STICKER_UPDATE = 91, - STICKER_DELETE = 92, - THREAD_CREATE = 110, - THREAD_UPDATE = 111, - THREAD_DELETE = 112, -}; - -struct AuditLogChange { - std::string Key; - std::optional OldValue; - std::optional NewValue; - - friend void from_json(const nlohmann::json &j, AuditLogChange &m); -}; - -struct AuditLogOptions { - std::optional DeleteMemberDays; // MEMBER_PRUNE - std::optional MembersRemoved; // MEMBER_PRUNE - std::optional ChannelID; // MEMBER_MOVE, MESSAGE_PIN, MESSAGE_UNPIN, MESSAGE_DELETE - std::optional MessageID; // MESSAGE_PIN, MESSAGE_UNPIN, - std::optional Count; // MESSAGE_DELETE, MESSAGE_BULK_DELETE, MEMBER_DISCONNECT, MEMBER_MOVE - std::optional ID; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE - std::optional Type; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE - std::optional RoleName; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE - - friend void from_json(const nlohmann::json &j, AuditLogOptions &m); -}; - -struct AuditLogEntry { - Snowflake ID; - std::string TargetID; // null - std::optional UserID; - AuditLogActionType Type; - std::optional Reason; - std::optional> Changes; - std::optional Options; - - friend void from_json(const nlohmann::json &j, AuditLogEntry &m); - - template - std::optional GetOldFromKey(const std::string &key) const; - - template - std::optional GetNewFromKey(const std::string &key) const; -}; - -struct AuditLogData { - std::vector Entries; - std::vector Users; - std::vector Webhooks; - // std::vector Integrations; - - friend void from_json(const nlohmann::json &j, AuditLogData &m); -}; - -template -inline std::optional AuditLogEntry::GetOldFromKey(const std::string &key) const { - if (!Changes.has_value()) return std::nullopt; - for (const auto &change : *Changes) - if (change.Key == key && change.OldValue.has_value()) - return change.OldValue->get(); - - return std::nullopt; -} - -template -inline std::optional AuditLogEntry::GetNewFromKey(const std::string &key) const { - if (!Changes.has_value()) return std::nullopt; - for (const auto &change : *Changes) - if (change.Key == key && change.NewValue.has_value()) - return change.NewValue->get(); - - return std::nullopt; -} diff --git a/discord/ban.cpp b/discord/ban.cpp deleted file mode 100644 index a354c15..0000000 --- a/discord/ban.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "ban.hpp" - -void from_json(const nlohmann::json &j, BanData &m) { - JS_N("reason", m.Reason); - JS_D("user", m.User); -} diff --git a/discord/ban.hpp b/discord/ban.hpp deleted file mode 100644 index d417ce3..0000000 --- a/discord/ban.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include -#include "user.hpp" - -struct BanData { - std::string Reason; // null - UserData User; // access id - - friend void from_json(const nlohmann::json &j, BanData &m); -}; diff --git a/discord/channel.cpp b/discord/channel.cpp deleted file mode 100644 index 80b1760..0000000 --- a/discord/channel.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "abaddon.hpp" -#include "channel.hpp" - -void from_json(const nlohmann::json &j, ThreadMetadataData &m) { - JS_D("archived", m.IsArchived); - JS_D("auto_archive_duration", m.AutoArchiveDuration); - JS_D("archive_timestamp", m.ArchiveTimestamp); - JS_O("locked", m.IsLocked); -} - -void from_json(const nlohmann::json &j, ThreadMemberObject &m) { - JS_O("id", m.ThreadID); - JS_O("user_id", m.UserID); - JS_D("join_timestamp", m.JoinTimestamp); - JS_D("flags", m.Flags); -} - -void from_json(const nlohmann::json &j, ChannelData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_O("guild_id", m.GuildID); - JS_O("position", m.Position); - JS_O("permission_overwrites", m.PermissionOverwrites); - JS_ON("name", m.Name); - JS_ON("topic", m.Topic); - JS_O("nsfw", m.IsNSFW); - JS_ON("last_message_id", m.LastMessageID); - JS_O("bitrate", m.Bitrate); - JS_O("user_limit", m.UserLimit); - JS_O("rate_limit_per_user", m.RateLimitPerUser); - JS_O("recipients", m.Recipients); - JS_O("recipient_ids", m.RecipientIDs); - JS_ON("icon", m.Icon); - JS_O("owner_id", m.OwnerID); - JS_O("application_id", m.ApplicationID); - JS_ON("parent_id", m.ParentID); - JS_ON("last_pin_timestamp", m.LastPinTimestamp); - JS_O("thread_metadata", m.ThreadMetadata); - JS_O("member", m.ThreadMember); -} - -void ChannelData::update_from_json(const nlohmann::json &j) { - JS_RD("type", Type); - JS_RD("guild_id", GuildID); - JS_RV("position", Position, -1); - JS_RD("permission_overwrites", PermissionOverwrites); - JS_RD("name", Name); - JS_RD("topic", Topic); - JS_RD("nsfw", IsNSFW); - JS_RD("last_message_id", LastMessageID); - JS_RD("bitrate", Bitrate); - JS_RD("user_limit", UserLimit); - JS_RD("rate_limit_per_user", RateLimitPerUser); - JS_RD("recipients", Recipients); - JS_RD("icon", Icon); - JS_RD("owner_id", OwnerID); - JS_RD("application_id", ApplicationID); - JS_RD("parent_id", ParentID); - JS_RD("last_pin_timestamp", LastPinTimestamp); -} - -bool ChannelData::NSFW() const { - return IsNSFW.has_value() && *IsNSFW; -} - -bool ChannelData::IsThread() const noexcept { - return Type == ChannelType::GUILD_PUBLIC_THREAD || - Type == ChannelType::GUILD_PRIVATE_THREAD || - Type == ChannelType::GUILD_NEWS_THREAD; -} - -bool ChannelData::IsJoinedThread() const { - return Abaddon::Get().GetDiscordClient().IsThreadJoined(ID); -} - -std::optional ChannelData::GetOverwrite(Snowflake id) const { - return Abaddon::Get().GetDiscordClient().GetPermissionOverwrite(ID, id); -} - -std::vector ChannelData::GetDMRecipients() const { - const auto &discord = Abaddon::Get().GetDiscordClient(); - if (Recipients.has_value()) - return *Recipients; - - if (RecipientIDs.has_value()) { - std::vector ret; - for (const auto &id : *RecipientIDs) { - auto user = discord.GetUser(id); - if (user.has_value()) - ret.push_back(std::move(*user)); - } - - return ret; - } - - return std::vector(); -} diff --git a/discord/channel.hpp b/discord/channel.hpp deleted file mode 100644 index 942d555..0000000 --- a/discord/channel.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once -#include "snowflake.hpp" -#include "json.hpp" -#include "user.hpp" -#include "permissions.hpp" -#include -#include - -enum class ChannelType : int { - GUILD_TEXT = 0, - DM = 1, - GUILD_VOICE = 2, - GROUP_DM = 3, - GUILD_CATEGORY = 4, - GUILD_NEWS = 5, - GUILD_STORE = 6, - /* 7 and 8 were used for LFG */ - /* 9 was used for threads */ - GUILD_NEWS_THREAD = 10, - GUILD_PUBLIC_THREAD = 11, - GUILD_PRIVATE_THREAD = 12, - GUILD_STAGE_VOICE = 13, -}; - -enum class StagePrivacy { - PUBLIC = 1, - GUILD_ONLY = 2, -}; - -constexpr const char *GetStagePrivacyDisplayString(StagePrivacy e) { - switch (e) { - case StagePrivacy::PUBLIC: - return "Public"; - case StagePrivacy::GUILD_ONLY: - return "Guild Only"; - default: - return "Unknown"; - } -} - -// should be moved somewhere? - -struct ThreadMetadataData { - bool IsArchived; - int AutoArchiveDuration; - std::string ArchiveTimestamp; - std::optional IsLocked; - - friend void from_json(const nlohmann::json &j, ThreadMetadataData &m); -}; - -struct ThreadMemberObject { - std::optional ThreadID; - std::optional UserID; - std::string JoinTimestamp; - int Flags; - - friend void from_json(const nlohmann::json &j, ThreadMemberObject &m); -}; - -struct ChannelData { - Snowflake ID; - ChannelType Type; - std::optional GuildID; - std::optional Position; - std::optional> PermissionOverwrites; // shouldnt be accessed - std::optional Name; // null for dm's - std::optional Topic; // null - std::optional IsNSFW; - std::optional LastMessageID; // null - std::optional Bitrate; - std::optional UserLimit; - std::optional RateLimitPerUser; - std::optional> Recipients; // only access id - std::optional> RecipientIDs; - std::optional Icon; // null - std::optional OwnerID; - std::optional ApplicationID; - std::optional ParentID; // null - std::optional LastPinTimestamp; // null - std::optional ThreadMetadata; - std::optional ThreadMember; - - friend void from_json(const nlohmann::json &j, ChannelData &m); - void update_from_json(const nlohmann::json &j); - - bool NSFW() const; - bool IsThread() const noexcept; - bool IsJoinedThread() const; - std::optional GetOverwrite(Snowflake id) const; - std::vector GetDMRecipients() const; -}; diff --git a/discord/discord.cpp b/discord/discord.cpp deleted file mode 100644 index bed959f..0000000 --- a/discord/discord.cpp +++ /dev/null @@ -1,2310 +0,0 @@ -#include "discord.hpp" -#include -#include -#include "util.hpp" -#include "abaddon.hpp" - -DiscordClient::DiscordClient(bool mem_store) - : m_decompress_buf(InflateChunkSize) - , m_store(mem_store) { - m_msg_dispatch.connect(sigc::mem_fun(*this, &DiscordClient::MessageDispatch)); - auto dispatch_cb = [this]() { - m_generic_mutex.lock(); - auto func = m_generic_queue.front(); - m_generic_queue.pop(); - m_generic_mutex.unlock(); - func(); - }; - m_generic_dispatch.connect(dispatch_cb); - - m_websocket.signal_message().connect(sigc::mem_fun(*this, &DiscordClient::HandleGatewayMessageRaw)); - m_websocket.signal_open().connect(sigc::mem_fun(*this, &DiscordClient::HandleSocketOpen)); - m_websocket.signal_close().connect(sigc::mem_fun(*this, &DiscordClient::HandleSocketClose)); - - LoadEventMap(); -} - -void DiscordClient::Start() { - if (m_client_started) return; - - m_http.SetBase(GetAPIURL()); - - std::memset(&m_zstream, 0, sizeof(m_zstream)); - inflateInit2(&m_zstream, MAX_WBITS + 32); - - m_last_sequence = -1; - m_heartbeat_acked = true; - m_client_connected = true; - m_client_started = true; - m_websocket.StartConnection(GetGatewayURL()); -} - -void DiscordClient::Stop() { - if (m_client_started) { - inflateEnd(&m_zstream); - m_compressed_buf.clear(); - - m_heartbeat_waiter.kill(); - if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); - m_client_connected = false; - m_reconnecting = false; - - m_store.ClearAll(); - m_guild_to_users.clear(); - - m_websocket.Stop(); - } - - m_client_started = false; -} - -bool DiscordClient::IsStarted() const { - return m_client_started; -} - -bool DiscordClient::IsStoreValid() const { - return m_store.IsValid(); -} - -const UserSettings &DiscordClient::GetUserSettings() const { - return m_user_settings; -} - -std::unordered_set DiscordClient::GetGuilds() const { - return m_store.GetGuilds(); -} - -const UserData &DiscordClient::GetUserData() const { - return m_user_data; -} - -std::vector DiscordClient::GetUserSortedGuilds() const { - // sort order is unfolder'd guilds sorted by id descending, then guilds in folders in array order - // todo: make sure folder'd guilds are sorted properly - std::vector folder_order; - auto guilds = GetGuilds(); - for (const auto &entry : m_user_settings.GuildFolders) { // can contain guilds not a part of - for (const auto &id : entry.GuildIDs) { - if (std::find(guilds.begin(), guilds.end(), id) != guilds.end()) - folder_order.push_back(id); - } - } - - std::vector ret; - for (const auto &gid : guilds) { - if (std::find(folder_order.begin(), folder_order.end(), gid) == folder_order.end()) { - ret.push_back(gid); - } - } - - std::sort(ret.rbegin(), ret.rend()); - - for (const auto &gid : folder_order) - ret.push_back(gid); - - return ret; -} - -std::vector DiscordClient::GetMessagesForChannel(Snowflake id, size_t limit) const { - return m_store.GetLastMessages(id, limit); -} - -std::vector DiscordClient::GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const { - return m_store.GetMessagesBefore(channel_id, message_id, limit); -} - -void DiscordClient::FetchInvite(std::string code, sigc::slot)> callback) { - m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](http::response_type r) { - if (!CheckCode(r)) { - if (r.status_code == 404) - callback(std::nullopt); - return; - }; - - callback(nlohmann::json::parse(r.text).get()); - }); -} - -void DiscordClient::FetchMessagesInChannel(Snowflake id, sigc::slot &)> cb) { - std::string path = "/channels/" + std::to_string(id) + "/messages?limit=50"; - m_http.MakeGET(path, [this, id, cb](const http::response_type &r) { - if (!CheckCode(r)) { - // fake a thread delete event if the requested channel is a thread and we get a 404 - - if (r.status_code == http::NotFound) { - const auto channel = m_store.GetChannel(id); - if (channel.has_value() && channel->IsThread()) { - ThreadDeleteData data; - data.GuildID = *channel->GuildID; - data.ID = id; - data.ParentID = *channel->ParentID; - data.Type = channel->Type; - m_signal_thread_delete.emit(data); - } - } - - return; - } - - std::vector msgs; - - nlohmann::json::parse(r.text).get_to(msgs); - - m_store.BeginTransaction(); - for (auto &msg : msgs) { - StoreMessageData(msg); - if (msg.GuildID.has_value()) - AddUserToGuild(msg.Author.ID, *msg.GuildID); - } - m_store.EndTransaction(); - - cb(msgs); - }); -} - -void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, sigc::slot &)> cb) { - std::string path = "/channels/" + std::to_string(channel_id) + "/messages?limit=50&before=" + std::to_string(before_id); - m_http.MakeGET(path, [this, channel_id, cb](http::response_type r) { - if (!CheckCode(r)) return; - - std::vector msgs; - - nlohmann::json::parse(r.text).get_to(msgs); - - m_store.BeginTransaction(); - for (auto &msg : msgs) { - StoreMessageData(msg); - if (msg.GuildID.has_value()) - AddUserToGuild(msg.Author.ID, *msg.GuildID); - } - m_store.EndTransaction(); - - std::sort(msgs.begin(), msgs.end(), [](const Message &a, const Message &b) { return a.ID < b.ID; }); - cb(msgs); - }); -} - -std::optional DiscordClient::GetMessage(Snowflake id) const { - return m_store.GetMessage(id); -} - -std::optional DiscordClient::GetChannel(Snowflake id) const { - return m_store.GetChannel(id); -} - -std::optional DiscordClient::GetUser(Snowflake id) const { - return m_store.GetUser(id); -} - -std::optional DiscordClient::GetRole(Snowflake id) const { - return m_store.GetRole(id); -} - -std::optional DiscordClient::GetGuild(Snowflake id) const { - return m_store.GetGuild(id); -} - -std::optional DiscordClient::GetMember(Snowflake user_id, Snowflake guild_id) const { - return m_store.GetGuildMember(guild_id, user_id); -} - -std::optional DiscordClient::GetBan(Snowflake guild_id, Snowflake user_id) const { - return m_store.GetBan(guild_id, user_id); -} - -std::optional DiscordClient::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const { - return m_store.GetPermissionOverwrite(channel_id, id); -} - -std::optional DiscordClient::GetEmoji(Snowflake id) const { - return m_store.GetEmoji(id); -} - -Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color) const { - const auto data = GetMember(user_id, guild_id); - if (!data.has_value()) return Snowflake::Invalid; - - std::optional top_role; - for (const auto &id : data->Roles) { - const auto role = GetRole(id); - if (role.has_value()) { - if ((with_color && role->Color != 0x000000) || (!with_color && role->IsHoisted)) - if (!top_role.has_value() || top_role->Position < role->Position) - top_role = role; - } - } - - return top_role.has_value() ? top_role->ID : Snowflake::Invalid; -} - -std::optional DiscordClient::GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const { - const auto data = GetMember(user_id, guild_id); - if (!data.has_value()) return std::nullopt; - - if (data->Roles.size() == 0) return std::nullopt; - if (data->Roles.size() == 1) return GetRole(data->Roles[0]); - - std::vector roles; - for (const auto id : data->Roles) - roles.push_back(*GetRole(id)); - - return *std::max_element(roles.begin(), roles.end(), [this](const auto &a, const auto &b) -> bool { - return a.Position < b.Position; - }); -} - -std::set DiscordClient::GetUsersInGuild(Snowflake id) const { - auto it = m_guild_to_users.find(id); - if (it != m_guild_to_users.end()) - return it->second; - - return {}; -} - -std::set DiscordClient::GetChannelsInGuild(Snowflake id) const { - auto it = m_guild_to_channels.find(id); - if (it != m_guild_to_channels.end()) - return it->second; - return {}; -} - -std::vector DiscordClient::GetUsersInThread(Snowflake id) const { - if (auto it = m_thread_members.find(id); it != m_thread_members.end()) - return it->second; - return {}; -} - -// there is an endpoint for this but it should be synced before this is called anyways -std::vector DiscordClient::GetActiveThreads(Snowflake channel_id) const { - return m_store.GetActiveThreads(channel_id); -} - -void DiscordClient::GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback) { - m_http.MakeGET("/channels/" + std::to_string(channel_id) + "/threads/archived/public", [this, callback](const http::response_type &r) { - if (CheckCode(r)) { - const auto data = nlohmann::json::parse(r.text).get(); - for (const auto &thread : data.Threads) - m_store.SetChannel(thread.ID, thread); - callback(DiscordError::NONE, data); - } else { - callback(GetCodeFromResponse(r), {}); - } - }); -} - -bool DiscordClient::IsThreadJoined(Snowflake thread_id) const { - return std::find(m_joined_threads.begin(), m_joined_threads.end(), thread_id) != m_joined_threads.end(); -} - -bool DiscordClient::HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const { - const auto base = ComputePermissions(user_id, guild_id); - return (base & perm) == perm; -} - -bool DiscordClient::HasAnyChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const { - const auto channel = m_store.GetChannel(channel_id); - if (!channel.has_value() || !channel->GuildID.has_value()) return false; - const auto base = ComputePermissions(user_id, *channel->GuildID); - const auto overwrites = ComputeOverwrites(base, user_id, channel_id); - return (overwrites & perm) != Permission::NONE; -} - -bool DiscordClient::HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const { - const auto channel = m_store.GetChannel(channel_id); - if (!channel.has_value()) return false; - const auto base = ComputePermissions(user_id, *channel->GuildID); - const auto overwrites = ComputeOverwrites(base, user_id, channel_id); - return (overwrites & perm) == perm; -} - -Permission DiscordClient::ComputePermissions(Snowflake member_id, Snowflake guild_id) const { - const auto member = GetMember(member_id, guild_id); - const auto guild = GetGuild(guild_id); - if (!member.has_value() || !guild.has_value()) - return Permission::NONE; - - if (guild->OwnerID == member_id) - return Permission::ALL; - - const auto everyone = GetRole(guild_id); - if (!everyone.has_value()) - return Permission::NONE; - - Permission perms = everyone->Permissions; - for (const auto role_id : member->Roles) { - const auto role = GetRole(role_id); - if (role.has_value()) - perms |= role->Permissions; - } - - if ((perms & Permission::ADMINISTRATOR) == Permission::ADMINISTRATOR) - return Permission::ALL; - - return perms; -} - -Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const { - if ((base & Permission::ADMINISTRATOR) == Permission::ADMINISTRATOR) - return Permission::ALL; - - const auto channel = GetChannel(channel_id); - const auto member = GetMember(member_id, *channel->GuildID); - if (!member.has_value() || !channel.has_value()) - return Permission::NONE; - - Permission perms = base; - const auto overwrite_everyone = GetPermissionOverwrite(channel_id, *channel->GuildID); - if (overwrite_everyone.has_value()) { - perms &= ~overwrite_everyone->Deny; - perms |= overwrite_everyone->Allow; - } - - Permission allow = Permission::NONE; - Permission deny = Permission::NONE; - for (const auto role_id : member->Roles) { - const auto overwrite = GetPermissionOverwrite(channel_id, role_id); - if (overwrite.has_value()) { - allow |= overwrite->Allow; - deny |= overwrite->Deny; - } - } - - perms &= ~deny; - perms |= allow; - - const auto member_overwrite = GetPermissionOverwrite(channel_id, member_id); - if (member_overwrite.has_value()) { - perms &= ~member_overwrite->Deny; - perms |= member_overwrite->Allow; - } - - return perms; -} - -bool DiscordClient::CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const { - const auto guild = GetGuild(guild_id); - if (guild.has_value() && guild->OwnerID == target) return false; - const auto actor_highest = GetMemberHighestRole(guild_id, actor); - const auto target_highest = GetMemberHighestRole(guild_id, target); - if (!actor_highest.has_value()) return false; - if (!target_highest.has_value()) return true; - return actor_highest->Position > target_highest->Position; -} - -void DiscordClient::ChatMessageCallback(std::string nonce, const http::response_type &response) { - if (!CheckCode(response)) { - if (response.status_code == http::TooManyRequests) { - try { // not sure if this body is guaranteed - RateLimitedResponse r = nlohmann::json::parse(response.text); - m_signal_message_send_fail.emit(nonce, r.RetryAfter); - } catch (...) { - m_signal_message_send_fail.emit(nonce, 0); - } - } else { - m_signal_message_send_fail.emit(nonce, 0); - } - } -} - -void DiscordClient::SendChatMessage(const std::string &content, Snowflake channel) { - // @([^@#]{1,32})#(\\d{4}) - const auto nonce = std::to_string(Snowflake::FromNow()); - CreateMessageObject obj; - obj.Content = content; - obj.Nonce = nonce; - m_http.MakePOST("/channels/" + std::to_string(channel) + "/messages", nlohmann::json(obj).dump(), sigc::bind<0>(sigc::mem_fun(*this, &DiscordClient::ChatMessageCallback), nonce)); - // dummy data so the content can be shown while waiting for MESSAGE_CREATE - Message tmp; - tmp.Content = content; - tmp.ID = nonce; - tmp.ChannelID = channel; - tmp.Author = GetUserData(); - tmp.IsTTS = false; - tmp.DoesMentionEveryone = false; - tmp.Type = MessageType::DEFAULT; - tmp.IsPinned = false; - tmp.Timestamp = "2000-01-01T00:00:00.000000+00:00"; - tmp.Nonce = obj.Nonce; - tmp.IsPending = true; - m_store.SetMessage(tmp.ID, tmp); - m_signal_message_sent.emit(tmp); -} - -void DiscordClient::SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message) { - const auto nonce = std::to_string(Snowflake::FromNow()); - CreateMessageObject obj; - obj.Content = content; - obj.Nonce = nonce; - obj.MessageReference.emplace().MessageID = referenced_message; - m_http.MakePOST("/channels/" + std::to_string(channel) + "/messages", nlohmann::json(obj).dump(), sigc::bind<0>(sigc::mem_fun(*this, &DiscordClient::ChatMessageCallback), nonce)); - Message tmp; - tmp.Content = content; - tmp.ID = nonce; - tmp.ChannelID = channel; - tmp.Author = GetUserData(); - tmp.IsTTS = false; - tmp.DoesMentionEveryone = false; - tmp.Type = MessageType::DEFAULT; - tmp.IsPinned = false; - tmp.Timestamp = "2000-01-01T00:00:00.000000+00:00"; - tmp.Nonce = obj.Nonce; - tmp.IsPending = true; - m_store.SetMessage(tmp.ID, tmp); - m_signal_message_sent.emit(tmp); -} - -void DiscordClient::DeleteMessage(Snowflake channel_id, Snowflake id) { - std::string path = "/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id); - m_http.MakeDELETE(path, [](auto) {}); -} - -void DiscordClient::EditMessage(Snowflake channel_id, Snowflake id, std::string content) { - std::string path = "/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id); - MessageEditObject obj; - obj.Content = content; - nlohmann::json j = obj; - m_http.MakePATCH(path, j.dump(), [](auto) {}); -} - -void DiscordClient::SendLazyLoad(Snowflake id) { - LazyLoadRequestMessage msg; - msg.Channels.emplace(); - msg.Channels.value()[id] = { - std::make_pair(0, 99), - std::make_pair(100, 199) - }; - msg.GuildID = *GetChannel(id)->GuildID; - msg.ShouldGetActivities = true; - msg.ShouldGetTyping = true; - msg.ShouldGetThreads = true; - - m_websocket.Send(msg); - - m_channels_lazy_loaded.insert(id); -} - -void DiscordClient::SendThreadLazyLoad(Snowflake id) { - auto thread = GetChannel(id); - if (thread.has_value()) - if (m_channels_lazy_loaded.find(*thread->ParentID) == m_channels_lazy_loaded.end()) - SendLazyLoad(*thread->ParentID); - - LazyLoadRequestMessage msg; - msg.GuildID = *GetChannel(id)->GuildID; - msg.ThreadIDs.emplace().push_back(id); - - m_websocket.Send(msg); -} - -void DiscordClient::JoinGuild(std::string code) { - m_http.MakePOST("/invites/" + code, "{}", [](auto) {}); -} - -void DiscordClient::LeaveGuild(Snowflake id) { - m_http.MakeDELETE("/users/@me/guilds/" + std::to_string(id), [](auto) {}); -} - -void DiscordClient::KickUser(Snowflake user_id, Snowflake guild_id) { - m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/members/" + std::to_string(user_id), [](auto) {}); -} - -void DiscordClient::BanUser(Snowflake user_id, Snowflake guild_id) { - m_http.MakePUT("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), "{}", [](auto) {}); -} - -void DiscordClient::UpdateStatus(PresenceStatus status, bool is_afk) { - UpdateStatusMessage msg; - msg.Status = status; - msg.IsAFK = is_afk; - - m_websocket.Send(nlohmann::json(msg)); - // fake message cuz we dont receive messages for ourself - m_user_to_status[m_user_data.ID] = status; - m_signal_presence_update.emit(GetUserData(), status); -} - -void DiscordClient::UpdateStatus(PresenceStatus status, bool is_afk, const ActivityData &obj) { - UpdateStatusMessage msg; - msg.Status = status; - msg.IsAFK = is_afk; - msg.Activities.push_back(obj); - - m_websocket.Send(nlohmann::json(msg)); - m_user_to_status[m_user_data.ID] = status; - m_signal_presence_update.emit(GetUserData(), status); -} - -void DiscordClient::CreateDM(Snowflake user_id) { - CreateDM(user_id, [](...) {}); -} - -void DiscordClient::CreateDM(Snowflake user_id, sigc::slot callback) { - CreateDMObject obj; - obj.Recipients.push_back(user_id); - m_http.MakePOST("/users/@me/channels", nlohmann::json(obj).dump(), [this, callback](const http::response &response) { - if (!CheckCode(response)) { - callback(DiscordError::NONE, Snowflake::Invalid); - return; - } - auto channel = nlohmann::json::parse(response.text).get(); - callback(GetCodeFromResponse(response), channel.ID); - }); -} - -void DiscordClient::CloseDM(Snowflake channel_id) { - m_http.MakeDELETE("/channels/" + std::to_string(channel_id), [this](const http::response &response) { - CheckCode(response); - }); -} - -std::optional DiscordClient::FindDM(Snowflake user_id) { - const auto &channels = m_store.GetChannels(); - for (const auto &id : channels) { - const auto channel = m_store.GetChannel(id); - const auto recipients = channel->GetDMRecipients(); - if (recipients.size() == 1 && recipients[0].ID == user_id) - return id; - } - - return std::nullopt; -} - -void DiscordClient::AddReaction(Snowflake id, Glib::ustring param) { - if (!param.is_ascii()) // means unicode param - param = Glib::uri_escape_string(param, "", false); - else { - const auto &tmp = m_store.GetEmoji(param); - if (tmp.has_value()) - param = tmp->Name + ":" + std::to_string(tmp->ID); - else - return; - } - Snowflake channel_id = m_store.GetMessage(id)->ChannelID; - m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", "", [](auto) {}); -} - -void DiscordClient::RemoveReaction(Snowflake id, Glib::ustring param) { - if (!param.is_ascii()) // means unicode param - param = Glib::uri_escape_string(param, "", false); - else { - const auto &tmp = m_store.GetEmoji(param); - if (tmp.has_value()) - param = tmp->Name + ":" + std::to_string(tmp->ID); - else - return; - } - Snowflake channel_id = m_store.GetMessage(id)->ChannelID; - m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/messages/" + std::to_string(id) + "/reactions/" + param + "/@me", [](auto) {}); -} - -void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name) { - SetGuildName(id, name, [](auto) {}); -} - -void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot callback) { - ModifyGuildObject obj; - obj.Name = name; - m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &r) { - if (CheckCode(r)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(r)); - }); -} - -void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data) { - SetGuildIcon(id, data, [](auto) {}); -} - -void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::slot callback) { - ModifyGuildObject obj; - obj.IconData = data; - m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &r) { - if (CheckCode(r)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(r)); - }); -} - -void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id) { - UnbanUser(guild_id, user_id, [](const auto) {}); -} - -void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot callback) { - m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::DeleteInvite(const std::string &code) { - DeleteInvite(code, [](const auto) {}); -} - -void DiscordClient::DeleteInvite(const std::string &code, sigc::slot callback) { - m_http.MakeDELETE("/invites/" + code, [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id) { - m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), "", [this](const http::response_type &response) { - CheckCode(response); - }); -} - -void DiscordClient::RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id) { - m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/recipients/" + std::to_string(user_id), [this](const http::response_type &response) { - CheckCode(response); - }); -} - -void DiscordClient::ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, sigc::slot callback) { - ModifyGuildRoleObject obj; - obj.Permissions = permissions; - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, sigc::slot callback) { - ModifyGuildRoleObject obj; - obj.Name = name; - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, sigc::slot callback) { - ModifyGuildRoleObject obj; - obj.Color = color; - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles/" + std::to_string(role_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::RGBA color, sigc::slot callback) { - uint32_t int_color = 0; - int_color |= static_cast(color.get_blue() * 255.0) << 0; - int_color |= static_cast(color.get_green() * 255.0) << 8; - int_color |= static_cast(color.get_red() * 255.0) << 16; - ModifyRoleColor(guild_id, role_id, int_color, callback); -} - -void DiscordClient::ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot callback) { - const auto roles = GetGuild(guild_id)->FetchRoles(); - if (static_cast(position) > roles.size()) return; - // gay and makes you send every role in between new and old position - constexpr auto IDX_MAX = ~size_t { 0 }; - size_t index_from = IDX_MAX, index_to = IDX_MAX; - for (size_t i = 0; i < roles.size(); i++) { - const auto &role = roles[i]; - if (role.ID == role_id) - index_from = i; - else if (role.Position == position) - index_to = i; - if (index_from != IDX_MAX && index_to != IDX_MAX) break; - } - - if (index_from == IDX_MAX || index_to == IDX_MAX) return; - - int dir; - size_t range_from, range_to; - if (index_to > index_from) { - dir = 1; - range_from = index_from + 1; - range_to = index_to + 1; - } else { - dir = -1; - range_from = index_to; - range_to = index_from; - } - - ModifyGuildRolePositionsObject obj; - - obj.Positions.push_back({ roles[index_from].ID, position }); - for (size_t i = range_from; i < range_to; i++) - obj.Positions.push_back({ roles[i].ID, roles[i].Position + dir }); - - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/roles", nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot callback) { - ModifyGuildEmojiObject obj; - obj.Name = name; - - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot callback) { - m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/emojis/" + std::to_string(emoji_id), [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -std::optional DiscordClient::GetGuildApplication(Snowflake guild_id) const { - const auto it = m_guild_join_requests.find(guild_id); - if (it == m_guild_join_requests.end()) return std::nullopt; - return it->second; -} - -void DiscordClient::RemoveRelationship(Snowflake id, sigc::slot callback) { - m_http.MakeDELETE("/users/@me/relationships/" + std::to_string(id), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::SendFriendRequest(const Glib::ustring &username, int discriminator, sigc::slot callback) { - FriendRequestObject obj; - obj.Username = username; - obj.Discriminator = discriminator; - m_http.MakePOST("/users/@me/relationships", nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::PutRelationship(Snowflake id, sigc::slot callback) { - m_http.MakePUT("/users/@me/relationships/" + std::to_string(id), "{}", [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::Pin(Snowflake channel_id, Snowflake message_id, sigc::slot callback) { - m_http.MakePUT("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), "", [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot callback) { - m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/pins/" + std::to_string(message_id), [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -// i dont know if the location parameter is necessary at all but discord's thread implementation is extremely strange -// so its here just in case -void DiscordClient::LeaveThread(Snowflake channel_id, const std::string &location, sigc::slot callback) { - m_http.MakeDELETE("/channels/" + std::to_string(channel_id) + "/thread-members/@me?location=" + location, [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::ArchiveThread(Snowflake channel_id, sigc::slot callback) { - ModifyChannelObject obj; - obj.Archived = true; - obj.Locked = true; - m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::UnArchiveThread(Snowflake channel_id, sigc::slot callback) { - ModifyChannelObject obj; - obj.Archived = false; - obj.Locked = false; - m_http.MakePATCH("/channels/" + std::to_string(channel_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::FetchPinned(Snowflake id, sigc::slot, DiscordError code)> callback) { - // return from db if we know the pins have already been requested - if (m_channels_pinned_requested.find(id) != m_channels_pinned_requested.end()) { - callback(m_store.GetPinnedMessages(id), DiscordError::NONE); - return; - } - m_channels_pinned_requested.insert(id); - - m_http.MakeGET("/channels/" + std::to_string(id) + "/pins", [this, callback](const http::response_type &response) { - if (!CheckCode(response)) { - callback({}, GetCodeFromResponse(response)); - return; - } - - auto data = nlohmann::json::parse(response.text).get>(); - std::sort(data.begin(), data.end(), [](const Message &a, const Message &b) { return a.ID < b.ID; }); - for (auto &msg : data) - StoreMessageData(msg); - callback(std::move(data), DiscordError::NONE); - }); -} - -bool DiscordClient::CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const { - const auto guild = *GetGuild(guild_id); - if (guild.OwnerID == user_id) return true; - const auto role = *GetRole(role_id); - const auto has_modify = HasGuildPermission(user_id, guild_id, Permission::MANAGE_CHANNELS); - const auto highest = GetMemberHighestRole(guild_id, user_id); - return has_modify && highest.has_value() && highest->Position > role.Position; -} - -bool DiscordClient::CanModifyRole(Snowflake guild_id, Snowflake role_id) const { - return CanModifyRole(guild_id, role_id, GetUserData().ID); -} - -std::vector DiscordClient::GetBansInGuild(Snowflake guild_id) { - return m_store.GetBans(guild_id); -} - -void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const http::response_type &response) { - if (!CheckCode(response)) return; - auto ban = nlohmann::json::parse(response.text).get(); - m_store.SetBan(guild_id, ban.User.ID, ban); - m_store.SetUser(ban.User.ID, ban.User); - callback(ban); - }); -} - -void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot)> callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const http::response_type &response) { - if (!CheckCode(response)) return; - auto bans = nlohmann::json::parse(response.text).get>(); - m_store.BeginTransaction(); - for (const auto &ban : bans) { - m_store.SetBan(guild_id, ban.User.ID, ban); - m_store.SetUser(ban.User.ID, ban.User); - } - m_store.EndTransaction(); - callback(bans); - }); -} - -void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot)> callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const http::response_type &response) { - // store? - if (!CheckCode(response)) return; - auto invites = nlohmann::json::parse(response.text).get>(); - - m_store.BeginTransaction(); - for (const auto &invite : invites) - if (invite.Inviter.has_value()) - m_store.SetUser(invite.Inviter->ID, *invite.Inviter); - m_store.EndTransaction(); - - callback(invites); - }); -} - -void DiscordClient::FetchAuditLog(Snowflake guild_id, sigc::slot callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/audit-logs", [this, callback](const http::response &response) { - if (!CheckCode(response)) return; - auto data = nlohmann::json::parse(response.text).get(); - - m_store.BeginTransaction(); - for (const auto &user : data.Users) - m_store.SetUser(user.ID, user); - m_store.EndTransaction(); - - callback(data); - }); -} - -void DiscordClient::FetchGuildEmojis(Snowflake guild_id, sigc::slot)> callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/emojis", [this, callback](const http::response_type &response) { - if (!CheckCode(response)) return; - auto emojis = nlohmann::json::parse(response.text).get>(); - m_store.BeginTransaction(); - for (const auto &emoji : emojis) - m_store.SetEmoji(emoji.ID, emoji); - m_store.EndTransaction(); - callback(std::move(emojis)); - }); -} - -void DiscordClient::FetchUserProfile(Snowflake user_id, sigc::slot callback) { - m_http.MakeGET("/users/" + std::to_string(user_id) + "/profile", [this, callback](const http::response_type &response) { - if (!CheckCode(response)) return; - callback(nlohmann::json::parse(response.text).get()); - }); -} - -void DiscordClient::FetchUserNote(Snowflake user_id, sigc::slot callback) { - m_http.MakeGET("/users/@me/notes/" + std::to_string(user_id), [this, callback](const http::response_type &response) { - if (response.status_code == 404) return; - if (!CheckCode(response)) return; - const auto note = nlohmann::json::parse(response.text).get().Note; - if (note.has_value()) - callback(*note); - }); -} - -void DiscordClient::SetUserNote(Snowflake user_id, std::string note) { - SetUserNote(user_id, note, [](auto) {}); -} - -void DiscordClient::SetUserNote(Snowflake user_id, std::string note, sigc::slot callback) { - UserSetNoteObject obj; - obj.Note = note; - m_http.MakePUT("/users/@me/notes/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response, 204)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::FetchUserRelationships(Snowflake user_id, sigc::slot)> callback) { - m_http.MakeGET("/users/" + std::to_string(user_id) + "/relationships", [this, callback](const http::response_type &response) { - if (!CheckCode(response)) return; - RelationshipsData data = nlohmann::json::parse(response.text); - for (const auto &user : data.Users) - m_store.SetUser(user.ID, user); - callback(data.Users); - }); -} - -bool DiscordClient::IsVerificationRequired(Snowflake guild_id) { - const auto member = GetMember(GetUserData().ID, guild_id); - if (member.has_value() && member->IsPending.has_value()) - return *member->IsPending; - return false; -} - -void DiscordClient::GetVerificationGateInfo(Snowflake guild_id, sigc::slot)> callback) { - m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/member-verification", [this, callback](const http::response_type &response) { - if (!CheckCode(response)) return; - if (response.status_code == 204) callback(std::nullopt); - callback(nlohmann::json::parse(response.text).get()); - }); -} - -void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot callback) { - if (info.VerificationFields.has_value()) - for (auto &field : *info.VerificationFields) - field.Response = true; - m_http.MakePUT("/guilds/" + std::to_string(guild_id) + "/requests/@me", nlohmann::json(info).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); -} - -void DiscordClient::UpdateToken(std::string token) { - if (!IsStarted()) { - m_token = token; - m_http.SetAuth(token); - } -} - -void DiscordClient::SetUserAgent(std::string agent) { - m_http.SetUserAgent(agent); - m_websocket.SetUserAgent(agent); -} - -PresenceStatus DiscordClient::GetUserStatus(Snowflake id) const { - auto it = m_user_to_status.find(id); - if (it != m_user_to_status.end()) - return it->second; - - return PresenceStatus::Offline; -} - -std::map DiscordClient::GetRelationships() const { - return m_user_relationships; -} - -std::set DiscordClient::GetRelationships(RelationshipType type) const { - std::set ret; - for (const auto &[id, rtype] : m_user_relationships) - if (rtype == type) - ret.insert(id); - return ret; -} - -std::optional DiscordClient::GetRelationship(Snowflake id) const { - if (auto it = m_user_relationships.find(id); it != m_user_relationships.end()) - return it->second; - return std::nullopt; -} - -void DiscordClient::HandleGatewayMessageRaw(std::string str) { - // handles multiple zlib compressed messages, calling HandleGatewayMessage when a full message is received - std::vector buf(str.begin(), str.end()); - int len = static_cast(buf.size()); - bool has_suffix = buf[len - 4] == 0x00 && buf[len - 3] == 0x00 && buf[len - 2] == 0xFF && buf[len - 1] == 0xFF; - - m_compressed_buf.insert(m_compressed_buf.end(), buf.begin(), buf.end()); - - if (!has_suffix) return; - - m_zstream.next_in = m_compressed_buf.data(); - m_zstream.avail_in = static_cast(m_compressed_buf.size()); - m_zstream.total_in = m_zstream.total_out = 0; - - // loop in case of really big messages (e.g. READY) - while (true) { - m_zstream.next_out = m_decompress_buf.data() + m_zstream.total_out; - m_zstream.avail_out = static_cast(m_decompress_buf.size() - m_zstream.total_out); - - int err = inflate(&m_zstream, Z_SYNC_FLUSH); - if ((err == Z_OK || err == Z_BUF_ERROR) && m_zstream.avail_in > 0) { - m_decompress_buf.resize(m_decompress_buf.size() + InflateChunkSize); - } else { - if (err != Z_OK) { - fprintf(stderr, "Error decompressing input buffer %d (%d/%d)\n", err, m_zstream.avail_in, m_zstream.avail_out); - } else { - m_msg_mutex.lock(); - m_msg_queue.push(std::string(m_decompress_buf.begin(), m_decompress_buf.begin() + m_zstream.total_out)); - m_msg_dispatch.emit(); - m_msg_mutex.unlock(); - if (m_decompress_buf.size() > InflateChunkSize) - m_decompress_buf.resize(InflateChunkSize); - } - break; - } - } - - m_compressed_buf.clear(); -} - -void DiscordClient::MessageDispatch() { - m_msg_mutex.lock(); - auto msg = m_msg_queue.front(); - m_msg_queue.pop(); - m_msg_mutex.unlock(); - HandleGatewayMessage(msg); -} - -void DiscordClient::HandleGatewayMessage(std::string str) { - GatewayMessage m; - try { - m = nlohmann::json::parse(str); - } catch (std::exception &e) { - printf("Error decoding JSON. Discarding message: %s\n", e.what()); - return; - } - - if (m.Sequence != -1) - m_last_sequence = m.Sequence; - - try { - switch (m.Opcode) { - case GatewayOp::Hello: { - HandleGatewayHello(m); - } break; - case GatewayOp::HeartbeatAck: { - m_heartbeat_acked = true; - } break; - case GatewayOp::Reconnect: { - HandleGatewayReconnect(m); - } break; - case GatewayOp::InvalidSession: { - HandleGatewayInvalidSession(m); - } break; - case GatewayOp::Event: { - auto iter = m_event_map.find(m.Type); - if (iter == m_event_map.end()) { - printf("Unknown event %s\n", m.Type.c_str()); - break; - } - switch (iter->second) { - case GatewayEvent::READY: { - HandleGatewayReady(m); - } break; - case GatewayEvent::MESSAGE_CREATE: { - HandleGatewayMessageCreate(m); - } break; - case GatewayEvent::MESSAGE_DELETE: { - HandleGatewayMessageDelete(m); - } break; - case GatewayEvent::MESSAGE_UPDATE: { - HandleGatewayMessageUpdate(m); - } break; - case GatewayEvent::GUILD_MEMBER_LIST_UPDATE: { - HandleGatewayGuildMemberListUpdate(m); - } break; - case GatewayEvent::GUILD_CREATE: { - HandleGatewayGuildCreate(m); - } break; - case GatewayEvent::GUILD_DELETE: { - HandleGatewayGuildDelete(m); - } break; - case GatewayEvent::MESSAGE_DELETE_BULK: { - HandleGatewayMessageDeleteBulk(m); - } break; - case GatewayEvent::GUILD_MEMBER_UPDATE: { - HandleGatewayGuildMemberUpdate(m); - } break; - case GatewayEvent::PRESENCE_UPDATE: { - HandleGatewayPresenceUpdate(m); - } break; - case GatewayEvent::CHANNEL_DELETE: { - HandleGatewayChannelDelete(m); - } break; - case GatewayEvent::CHANNEL_UPDATE: { - HandleGatewayChannelUpdate(m); - } break; - case GatewayEvent::CHANNEL_CREATE: { - HandleGatewayChannelCreate(m); - } break; - case GatewayEvent::GUILD_UPDATE: { - HandleGatewayGuildUpdate(m); - } break; - case GatewayEvent::GUILD_ROLE_UPDATE: { - HandleGatewayGuildRoleUpdate(m); - } break; - case GatewayEvent::GUILD_ROLE_CREATE: { - HandleGatewayGuildRoleCreate(m); - } break; - case GatewayEvent::GUILD_ROLE_DELETE: { - HandleGatewayGuildRoleDelete(m); - } break; - case GatewayEvent::MESSAGE_REACTION_ADD: { - HandleGatewayMessageReactionAdd(m); - } break; - case GatewayEvent::MESSAGE_REACTION_REMOVE: { - HandleGatewayMessageReactionRemove(m); - } break; - case GatewayEvent::CHANNEL_RECIPIENT_ADD: { - HandleGatewayChannelRecipientAdd(m); - } break; - case GatewayEvent::CHANNEL_RECIPIENT_REMOVE: { - HandleGatewayChannelRecipientRemove(m); - } break; - case GatewayEvent::TYPING_START: { - HandleGatewayTypingStart(m); - } break; - case GatewayEvent::GUILD_BAN_REMOVE: { - HandleGatewayGuildBanRemove(m); - } break; - case GatewayEvent::GUILD_BAN_ADD: { - HandleGatewayGuildBanAdd(m); - } break; - case GatewayEvent::INVITE_CREATE: { - HandleGatewayInviteCreate(m); - } break; - case GatewayEvent::INVITE_DELETE: { - HandleGatewayInviteDelete(m); - } break; - case GatewayEvent::USER_NOTE_UPDATE: { - HandleGatewayUserNoteUpdate(m); - } break; - case GatewayEvent::READY_SUPPLEMENTAL: { - HandleGatewayReadySupplemental(m); - } break; - case GatewayEvent::GUILD_EMOJIS_UPDATE: { - HandleGatewayGuildEmojisUpdate(m); - } break; - case GatewayEvent::GUILD_JOIN_REQUEST_CREATE: { - HandleGatewayGuildJoinRequestCreate(m); - } break; - case GatewayEvent::GUILD_JOIN_REQUEST_UPDATE: { - HandleGatewayGuildJoinRequestUpdate(m); - } break; - case GatewayEvent::GUILD_JOIN_REQUEST_DELETE: { - HandleGatewayGuildJoinRequestDelete(m); - } break; - case GatewayEvent::RELATIONSHIP_REMOVE: { - HandleGatewayRelationshipRemove(m); - } break; - case GatewayEvent::RELATIONSHIP_ADD: { - HandleGatewayRelationshipAdd(m); - } break; - case GatewayEvent::THREAD_CREATE: { - HandleGatewayThreadCreate(m); - } break; - case GatewayEvent::THREAD_DELETE: { - HandleGatewayThreadDelete(m); - } break; - case GatewayEvent::THREAD_LIST_SYNC: { - HandleGatewayThreadListSync(m); - } break; - case GatewayEvent::THREAD_MEMBERS_UPDATE: { - HandleGatewayThreadMembersUpdate(m); - } break; - case GatewayEvent::THREAD_MEMBER_UPDATE: { - HandleGatewayThreadMemberUpdate(m); - } break; - case GatewayEvent::THREAD_UPDATE: { - HandleGatewayThreadUpdate(m); - } break; - case GatewayEvent::THREAD_MEMBER_LIST_UPDATE: { - HandleGatewayThreadMemberListUpdate(m); - } break; - } - } break; - default: - printf("Unknown opcode %d\n", static_cast(m.Opcode)); - break; - } - } catch (std::exception &e) { - fprintf(stderr, "error handling message (opcode %d): %s\n", static_cast(m.Opcode), e.what()); - } -} - -void DiscordClient::HandleGatewayHello(const GatewayMessage &msg) { - m_client_connected = true; - HelloMessageData d = msg.Data; - m_heartbeat_msec = d.HeartbeatInterval; - m_heartbeat_waiter.revive(); - m_heartbeat_thread = std::thread(std::bind(&DiscordClient::HeartbeatThread, this)); - m_signal_connected.emit(); // socket is connected before this but emitting here should b fine - m_reconnecting = false; // maybe should go elsewhere? - if (m_wants_resume) { - m_wants_resume = false; - SendResume(); - } else - SendIdentify(); -} - -// perhaps this should be set by the main class -std::string DiscordClient::GetAPIURL() { - static const auto url = Abaddon::Get().GetSettings().GetAPIBaseURL(); - return url; -} - -std::string DiscordClient::GetGatewayURL() { - static const auto url = Abaddon::Get().GetSettings().GetGatewayURL(); - return url; -} - -DiscordError DiscordClient::GetCodeFromResponse(const http::response_type &response) { - try { - const auto data = nlohmann::json::parse(response.text); - return data.at("code").get(); - } catch (...) {} - return DiscordError::GENERIC; -} - -void DiscordClient::ProcessNewGuild(GuildData &guild) { - if (guild.IsUnavailable) { - printf("guild (%" PRIu64 ") unavailable\n", static_cast(guild.ID)); - return; - } - - m_store.BeginTransaction(); - - m_store.SetGuild(guild.ID, guild); - if (guild.Channels.has_value()) { - for (auto &c : *guild.Channels) { - c.GuildID = guild.ID; - m_store.SetChannel(c.ID, c); - m_guild_to_channels[guild.ID].insert(c.ID); - for (auto &p : *c.PermissionOverwrites) { - m_store.SetPermissionOverwrite(c.ID, p.ID, p); - } - } - } - - if (guild.Threads.has_value()) { - for (auto &c : *guild.Threads) { - m_joined_threads.insert(c.ID); - c.GuildID = guild.ID; - m_store.SetChannel(c.ID, c); - } - } - - for (auto &r : *guild.Roles) - m_store.SetRole(guild.ID, r); - - for (auto &e : *guild.Emojis) - m_store.SetEmoji(e.ID, e); - - m_store.EndTransaction(); -} - -void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) { - m_ready_received = true; - ReadyEventData data = msg.Data; - for (auto &g : data.Guilds) - ProcessNewGuild(g); - - m_store.BeginTransaction(); - - for (const auto &dm : data.PrivateChannels) { - m_store.SetChannel(dm.ID, dm); - if (dm.Recipients.has_value()) - for (const auto &recipient : *dm.Recipients) - m_store.SetUser(recipient.ID, recipient); - } - - if (data.Users.has_value()) - for (const auto &user : *data.Users) - m_store.SetUser(user.ID, user); - - if (data.MergedMembers.has_value()) { - for (size_t i = 0; i < data.MergedMembers->size(); i++) { - const auto guild_id = data.Guilds[i].ID; - for (const auto &member : data.MergedMembers.value()[i]) { - m_store.SetGuildMember(guild_id, *member.UserID, member); - } - } - } - - if (data.Relationships.has_value()) - for (const auto &relationship : *data.Relationships) - m_user_relationships[relationship.ID] = relationship.Type; - - if (data.GuildJoinRequests.has_value()) - for (const auto &request : *data.GuildJoinRequests) - m_guild_join_requests[request.GuildID] = request; - - m_store.EndTransaction(); - - m_session_id = data.SessionID; - m_user_data = data.SelfUser; - m_user_settings = data.Settings; - m_signal_gateway_ready.emit(); -} - -void DiscordClient::HandleGatewayMessageCreate(const GatewayMessage &msg) { - Message data = msg.Data; - StoreMessageData(data); - if (data.GuildID.has_value()) - AddUserToGuild(data.Author.ID, *data.GuildID); - m_signal_message_create.emit(data); -} - -void DiscordClient::HandleGatewayMessageDelete(const GatewayMessage &msg) { - MessageDeleteData data = msg.Data; - auto cur = m_store.GetMessage(data.ID); - if (!cur.has_value()) - return; - - cur->SetDeleted(); - m_store.SetMessage(data.ID, *cur); - m_signal_message_delete.emit(data.ID, data.ChannelID); -} - -void DiscordClient::HandleGatewayMessageDeleteBulk(const GatewayMessage &msg) { - MessageDeleteBulkData data = msg.Data; - m_store.BeginTransaction(); - for (const auto &id : data.IDs) { - auto cur = m_store.GetMessage(id); - if (!cur.has_value()) - continue; - - cur->SetDeleted(); - m_store.SetMessage(id, *cur); - m_signal_message_delete.emit(id, data.ChannelID); - } - m_store.EndTransaction(); -} - -void DiscordClient::HandleGatewayGuildMemberUpdate(const GatewayMessage &msg) { - GuildMemberUpdateMessage data = msg.Data; - auto cur = m_store.GetGuildMember(data.GuildID, data.User.ID); - if (cur.has_value()) { - cur->update_from_json(msg.Data); - m_store.SetGuildMember(data.GuildID, data.User.ID, *cur); - } - m_signal_guild_member_update.emit(data.GuildID, data.User.ID); -} - -void DiscordClient::HandleGatewayPresenceUpdate(const GatewayMessage &msg) { - PresenceUpdateMessage data = msg.Data; - const auto user_id = data.User.at("id").get(); - - auto cur = m_store.GetUser(user_id); - if (cur.has_value()) { - cur->update_from_json(data.User); - m_store.SetUser(cur->ID, *cur); - } else - return; - - PresenceStatus e; - if (data.StatusMessage == "online") - e = PresenceStatus::Online; - else if (data.StatusMessage == "offline") - e = PresenceStatus::Offline; - else if (data.StatusMessage == "idle") - e = PresenceStatus::Idle; - else if (data.StatusMessage == "dnd") - e = PresenceStatus::DND; - - m_user_to_status[user_id] = e; - - m_signal_presence_update.emit(*cur, e); -} - -void DiscordClient::HandleGatewayChannelDelete(const GatewayMessage &msg) { - const auto id = msg.Data.at("id").get(); - const auto channel = GetChannel(id); - auto it = m_guild_to_channels.find(*channel->GuildID); - if (it != m_guild_to_channels.end()) - it->second.erase(id); - m_store.ClearChannel(id); - m_signal_channel_delete.emit(id); -} - -void DiscordClient::HandleGatewayChannelUpdate(const GatewayMessage &msg) { - const auto id = msg.Data.at("id").get(); - auto cur = m_store.GetChannel(id); - if (cur.has_value()) { - cur->update_from_json(msg.Data); - m_store.SetChannel(id, *cur); - if (cur->PermissionOverwrites.has_value()) - for (const auto &p : *cur->PermissionOverwrites) - m_store.SetPermissionOverwrite(id, p.ID, p); - m_signal_channel_update.emit(id); - } -} - -void DiscordClient::HandleGatewayChannelCreate(const GatewayMessage &msg) { - ChannelData data = msg.Data; - m_store.BeginTransaction(); - m_store.SetChannel(data.ID, data); - m_guild_to_channels[*data.GuildID].insert(data.ID); - if (data.PermissionOverwrites.has_value()) - for (const auto &p : *data.PermissionOverwrites) - m_store.SetPermissionOverwrite(data.ID, p.ID, p); - m_store.EndTransaction(); - m_signal_channel_create.emit(data); -} - -void DiscordClient::HandleGatewayGuildUpdate(const GatewayMessage &msg) { - Snowflake id = msg.Data.at("id"); - auto current = m_store.GetGuild(id); - if (!current.has_value()) return; - current->update_from_json(msg.Data); - m_store.SetGuild(id, *current); - m_signal_guild_update.emit(id); -} - -void DiscordClient::HandleGatewayGuildRoleUpdate(const GatewayMessage &msg) { - GuildRoleUpdateObject data = msg.Data; - m_store.SetRole(data.GuildID, data.Role); - m_signal_role_update.emit(data.GuildID, data.Role.ID); -} - -void DiscordClient::HandleGatewayGuildRoleCreate(const GatewayMessage &msg) { - GuildRoleCreateObject data = msg.Data; - auto guild = *m_store.GetGuild(data.GuildID); - guild.Roles->push_back(data.Role); - m_store.BeginTransaction(); - m_store.SetRole(guild.ID, data.Role); - m_store.SetGuild(guild.ID, guild); - m_store.EndTransaction(); - m_signal_role_create.emit(data.GuildID, data.Role.ID); -} - -void DiscordClient::HandleGatewayGuildRoleDelete(const GatewayMessage &msg) { - GuildRoleDeleteObject data = msg.Data; - auto guild = *m_store.GetGuild(data.GuildID); - const auto pred = [this, id = data.RoleID](const RoleData &role) -> bool { - return role.ID == id; - }; - guild.Roles->erase(std::remove_if(guild.Roles->begin(), guild.Roles->end(), pred), guild.Roles->end()); - m_store.SetGuild(guild.ID, guild); - m_signal_role_delete.emit(data.GuildID, data.RoleID); -} - -void DiscordClient::HandleGatewayMessageReactionAdd(const GatewayMessage &msg) { - MessageReactionAddObject data = msg.Data; - - m_store.AddReaction(data, data.UserID == GetUserData().ID); - if (data.Emoji.ID.IsValid()) - m_signal_reaction_add.emit(data.MessageID, std::to_string(data.Emoji.ID)); - else - m_signal_reaction_add.emit(data.MessageID, data.Emoji.Name); -} - -void DiscordClient::HandleGatewayMessageReactionRemove(const GatewayMessage &msg) { - MessageReactionRemoveObject data = msg.Data; - - m_store.RemoveReaction(data, data.UserID == GetUserData().ID); - if (data.Emoji.ID.IsValid()) - m_signal_reaction_remove.emit(data.MessageID, std::to_string(data.Emoji.ID)); - else - m_signal_reaction_remove.emit(data.MessageID, data.Emoji.Name); -} - -// todo: update channel list item and member list -void DiscordClient::HandleGatewayChannelRecipientAdd(const GatewayMessage &msg) { - ChannelRecipientAdd data = msg.Data; - auto cur = m_store.GetChannel(data.ChannelID); - if (!cur.has_value() || !cur->RecipientIDs.has_value()) return; - if (std::find(cur->RecipientIDs->begin(), cur->RecipientIDs->end(), data.User.ID) == cur->RecipientIDs->end()) - cur->RecipientIDs->push_back(data.User.ID); - m_store.SetUser(data.User.ID, data.User); - m_store.SetChannel(cur->ID, *cur); -} - -void DiscordClient::HandleGatewayChannelRecipientRemove(const GatewayMessage &msg) { - ChannelRecipientRemove data = msg.Data; - m_store.ClearRecipient(data.ChannelID, data.User.ID); -} - -void DiscordClient::HandleGatewayTypingStart(const GatewayMessage &msg) { - TypingStartObject data = msg.Data; - Snowflake guild_id; - if (data.GuildID.has_value()) { - guild_id = *data.GuildID; - } else { - auto chan = m_store.GetChannel(data.ChannelID); - if (chan.has_value() && chan->GuildID.has_value()) - guild_id = *chan->GuildID; - } - if (guild_id.IsValid() && data.Member.has_value()) { - auto cur = m_store.GetGuildMember(guild_id, data.UserID); - if (!cur.has_value()) { - AddUserToGuild(data.UserID, guild_id); - m_store.SetGuildMember(guild_id, data.UserID, *data.Member); - } - if (data.Member->User.has_value()) - m_store.SetUser(data.UserID, *data.Member->User); - } - m_signal_typing_start.emit(data.UserID, data.ChannelID); -} - -void DiscordClient::HandleGatewayGuildBanRemove(const GatewayMessage &msg) { - GuildBanRemoveObject data = msg.Data; - m_store.SetUser(data.User.ID, data.User); - m_store.ClearBan(data.GuildID, data.User.ID); - m_signal_guild_ban_remove.emit(data.GuildID, data.User.ID); -} - -void DiscordClient::HandleGatewayGuildBanAdd(const GatewayMessage &msg) { - GuildBanAddObject data = msg.Data; - BanData ban; - ban.Reason = ""; - ban.User = data.User; - m_store.SetUser(data.User.ID, data.User); - m_store.SetBan(data.GuildID, data.User.ID, ban); - m_signal_guild_ban_add.emit(data.GuildID, data.User.ID); -} - -void DiscordClient::HandleGatewayInviteCreate(const GatewayMessage &msg) { - InviteCreateObject data = msg.Data; - InviteData invite; - invite.Code = std::move(data.Code); - invite.CreatedAt = std::move(data.CreatedAt); - invite.Channel = *m_store.GetChannel(data.ChannelID); - invite.Inviter = std::move(data.Inviter); - invite.IsTemporary = std::move(data.IsTemporary); - invite.MaxAge = std::move(data.MaxAge); - invite.MaxUses = std::move(data.MaxUses); - invite.TargetUser = std::move(data.TargetUser); - invite.TargetUserType = std::move(data.TargetUserType); - invite.Uses = std::move(data.Uses); - if (data.GuildID.has_value()) - invite.Guild = m_store.GetGuild(*data.GuildID); - m_signal_invite_create.emit(invite); -} - -void DiscordClient::HandleGatewayInviteDelete(const GatewayMessage &msg) { - InviteDeleteObject data = msg.Data; - if (!data.GuildID.has_value()) { - const auto chan = GetChannel(data.ChannelID); - data.GuildID = chan->ID; - } - m_signal_invite_delete.emit(data); -} - -void DiscordClient::HandleGatewayUserNoteUpdate(const GatewayMessage &msg) { - UserNoteUpdateMessage data = msg.Data; - m_signal_note_update.emit(data.ID, data.Note); -} - -void DiscordClient::HandleGatewayGuildEmojisUpdate(const GatewayMessage &msg) { - // like the real client, the emoji data sent in this message is ignored - // we just use it as a signal to re-request all emojis - GuildEmojisUpdateObject data = msg.Data; - const auto cb = [this, id = data.GuildID](const std::vector &emojis) { - m_store.BeginTransaction(); - for (const auto &emoji : emojis) - m_store.SetEmoji(emoji.ID, emoji); - m_store.EndTransaction(); - m_signal_guild_emojis_update.emit(id, emojis); - }; - FetchGuildEmojis(data.GuildID, cb); -} - -void DiscordClient::HandleGatewayGuildJoinRequestCreate(const GatewayMessage &msg) { - GuildJoinRequestCreateData data = msg.Data; - m_guild_join_requests[data.GuildID] = data.Request; - m_signal_guild_join_request_create.emit(data); -} - -void DiscordClient::HandleGatewayGuildJoinRequestUpdate(const GatewayMessage &msg) { - GuildJoinRequestUpdateData data = msg.Data; - m_guild_join_requests[data.GuildID] = data.Request; - m_signal_guild_join_request_update.emit(data); -} - -void DiscordClient::HandleGatewayGuildJoinRequestDelete(const GatewayMessage &msg) { - GuildJoinRequestDeleteData data = msg.Data; - m_guild_join_requests.erase(data.GuildID); - m_signal_guild_join_request_delete.emit(data); -} - -void DiscordClient::HandleGatewayRelationshipRemove(const GatewayMessage &msg) { - RelationshipRemoveData data = msg.Data; - m_user_relationships.erase(data.ID); - m_signal_relationship_remove.emit(data.ID, data.Type); -} - -void DiscordClient::HandleGatewayRelationshipAdd(const GatewayMessage &msg) { - RelationshipAddData data = msg.Data; - m_store.SetUser(data.ID, data.User); - m_user_relationships[data.ID] = data.Type; - m_signal_relationship_add.emit(std::move(data)); -} - -// remarkably this doesnt actually mean a thread was created -// it can also mean you gained access to a thread. yay ... -// except sometimes it doesnt??? i dont know whats going on -void DiscordClient::HandleGatewayThreadCreate(const GatewayMessage &msg) { - ThreadCreateData data = msg.Data; - m_store.SetChannel(data.Channel.ID, data.Channel); - m_signal_thread_create.emit(data.Channel); - if (data.Channel.ThreadMember.has_value()) { - m_signal_added_to_thread.emit(data.Channel.ID); - } -} - -void DiscordClient::HandleGatewayThreadDelete(const GatewayMessage &msg) { - ThreadDeleteData data = msg.Data; - m_store.ClearChannel(data.ID); - m_signal_thread_delete.emit(data); -} - -// this message is received when you load a channel as part of the lazy load request -// so the ui will only update thread when you load a channel in some guild -// which is rather annoying but oh well -void DiscordClient::HandleGatewayThreadListSync(const GatewayMessage &msg) { - ThreadListSyncData data = msg.Data; - for (const auto &thread : data.Threads) - m_store.SetChannel(thread.ID, thread); - m_signal_thread_list_sync.emit(data); -} - -void DiscordClient::HandleGatewayThreadMembersUpdate(const GatewayMessage &msg) { - ThreadMembersUpdateData data = msg.Data; - if (data.AddedMembers.has_value() && - std::find_if(data.AddedMembers->begin(), data.AddedMembers->end(), [this](const auto &x) { - return *x.UserID == m_user_data.ID; // safe to assume UserID is present here - }) != data.AddedMembers->end()) { - m_joined_threads.insert(data.ID); - m_signal_added_to_thread.emit(data.ID); - } else if (data.RemovedMemberIDs.has_value() && - std::find(data.RemovedMemberIDs->begin(), data.RemovedMemberIDs->end(), m_user_data.ID) != data.RemovedMemberIDs->end()) { - m_joined_threads.erase(data.ID); - m_signal_removed_from_thread.emit(data.ID); - } - m_signal_thread_members_update.emit(data); -} - -void DiscordClient::HandleGatewayThreadMemberUpdate(const GatewayMessage &msg) { - ThreadMemberUpdateData data = msg.Data; - m_joined_threads.insert(*data.Member.ThreadID); - if (*data.Member.UserID == GetUserData().ID) - m_signal_added_to_thread.emit(*data.Member.ThreadID); -} - -void DiscordClient::HandleGatewayThreadUpdate(const GatewayMessage &msg) { - ThreadUpdateData data = msg.Data; - m_store.SetChannel(data.Thread.ID, data.Thread); - m_signal_thread_update.emit(data); -} - -void DiscordClient::HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg) { - ThreadMemberListUpdateData data = msg.Data; - m_store.BeginTransaction(); - for (const auto &entry : data.Members) { - m_thread_members[data.ThreadID].push_back(entry.UserID); - if (entry.Member.User.has_value()) - m_store.SetUser(entry.Member.User->ID, *entry.Member.User); - m_store.SetGuildMember(data.GuildID, entry.Member.User->ID, entry.Member); - } - m_store.EndTransaction(); - m_signal_thread_member_list_update.emit(data); -} - -void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) { - ReadySupplementalData data = msg.Data; - for (const auto &p : data.MergedPresences.Friends) { - const auto user = GetUser(p.UserID); - if (!user.has_value()) return; // should be sent in READY's `users` - const auto s = p.Presence.Status; - if (s == "online") - m_user_to_status[p.UserID] = PresenceStatus::Online; - else if (s == "offline") - m_user_to_status[p.UserID] = PresenceStatus::Offline; - else if (s == "idle") - m_user_to_status[p.UserID] = PresenceStatus::Idle; - else if (s == "dnd") - m_user_to_status[p.UserID] = PresenceStatus::DND; - m_signal_presence_update.emit(*user, m_user_to_status.at(p.UserID)); - } -} - -void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) { - printf("received reconnect\n"); - inflateEnd(&m_zstream); - m_compressed_buf.clear(); - - m_heartbeat_waiter.kill(); - if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); - - m_reconnecting = true; - m_wants_resume = true; - m_heartbeat_acked = true; - - m_websocket.Stop(1012); // 1000 (kNormalClosureCode) and 1001 will invalidate the session id - - std::memset(&m_zstream, 0, sizeof(m_zstream)); - inflateInit2(&m_zstream, MAX_WBITS + 32); - - m_websocket.StartConnection(GetGatewayURL()); -} - -void DiscordClient::HandleGatewayInvalidSession(const GatewayMessage &msg) { - printf("invalid session! re-identifying\n"); - - inflateEnd(&m_zstream); - m_compressed_buf.clear(); - - std::memset(&m_zstream, 0, sizeof(m_zstream)); - inflateInit2(&m_zstream, MAX_WBITS + 32); - - m_heartbeat_acked = true; - m_wants_resume = false; - m_reconnecting = true; - - m_heartbeat_waiter.kill(); - if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); - - m_websocket.Stop(1000); - - if (m_client_started) - Glib::signal_timeout().connect_once([this] { if (m_client_started) m_websocket.StartConnection(GetGatewayURL()); }, 1000); -} - -bool IsCompleteMessageObject(const nlohmann::json &j) { - const auto required = { "id", "channel_id", "author", "content", "timestamp", "edited_timestamp", "tts", "mention_everyone", "mention_roles", "attachments", "embeds", "pinned", "type" }; - for (const auto &str : required) { - if (!j.contains(str)) return false; - } - return true; -} - -void DiscordClient::HandleGatewayMessageUpdate(const GatewayMessage &msg) { - Snowflake id = msg.Data.at("id"); - - auto current = m_store.GetMessage(id); - if (!current.has_value()) { - // im not sure how the client determines if a MESSAGE_UPDATE is suitable to be stored as a full message but i guess its something like this - if (IsCompleteMessageObject(msg.Data)) { - current = msg.Data; - m_store.SetMessage(id, *current); - // this doesnt mean a message is newly pinned when called here - // it just means theres an (old) message that the client is now aware of that is also pinned - m_signal_message_pinned.emit(*current); - } else - return; - } else { - const bool old_pinned = current->IsPinned; - - current->from_json_edited(msg.Data); - m_store.SetMessage(id, *current); - - if (old_pinned && !current->IsPinned) - m_signal_message_unpinned.emit(*current); - else if (!old_pinned && current->IsPinned) - m_signal_message_pinned.emit(*current); - } - - m_signal_message_update.emit(id, current->ChannelID); -} - -void DiscordClient::HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg) { - GuildMemberListUpdateMessage data = msg.Data; - - m_store.BeginTransaction(); - - bool has_sync = false; - for (const auto &op : data.Ops) { - if (op.Op == "SYNC") { - has_sync = true; - for (const auto &item : *op.Items) { - if (item->Type == "member") { - auto member = static_cast(item.get()); - m_store.SetUser(member->User.ID, member->User); - AddUserToGuild(member->User.ID, data.GuildID); - m_store.SetGuildMember(data.GuildID, member->User.ID, member->GetAsMemberData()); - if (member->Presence.has_value()) { - const auto &s = member->Presence->Status; - if (s == "online") - m_user_to_status[member->User.ID] = PresenceStatus::Online; - else if (s == "offline") - m_user_to_status[member->User.ID] = PresenceStatus::Offline; - else if (s == "idle") - m_user_to_status[member->User.ID] = PresenceStatus::Idle; - else if (s == "dnd") - m_user_to_status[member->User.ID] = PresenceStatus::DND; - } - } - } - } else if (op.Op == "UPDATE") { - if (op.OpItem.has_value() && op.OpItem.value()->Type == "member") { - const auto &m = static_cast(op.OpItem.value().get())->GetAsMemberData(); - m_store.SetGuildMember(data.GuildID, m.User->ID, m); - m_signal_guild_member_update.emit(data.GuildID, m.User->ID); // cheeky - } - } - } - - m_store.EndTransaction(); - - // todo: manage this event a little better - if (has_sync) - m_signal_guild_member_list_update.emit(data.GuildID); -} - -void DiscordClient::HandleGatewayGuildCreate(const GatewayMessage &msg) { - GuildData data = msg.Data; - ProcessNewGuild(data); - - m_signal_guild_create.emit(data); -} - -void DiscordClient::HandleGatewayGuildDelete(const GatewayMessage &msg) { - Snowflake id = msg.Data.at("id"); - bool unavailable = msg.Data.contains("unavilable") && msg.Data.at("unavailable").get(); - - if (unavailable) - printf("guild %" PRIu64 " became unavailable\n", static_cast(id)); - - const auto guild = m_store.GetGuild(id); - if (!guild.has_value()) { - m_store.ClearGuild(id); - m_signal_guild_delete.emit(id); - return; - } - - m_store.ClearGuild(id); - if (guild->Channels.has_value()) - for (const auto &c : *guild->Channels) - m_store.ClearChannel(c.ID); - - m_signal_guild_delete.emit(id); -} - -void DiscordClient::AddUserToGuild(Snowflake user_id, Snowflake guild_id) { - m_guild_to_users[guild_id].insert(user_id); -} - -std::set DiscordClient::GetPrivateChannels() const { - auto ret = std::set(); - - for (const auto &id : m_store.GetChannels()) { - const auto chan = m_store.GetChannel(id); - if (chan->Type == ChannelType::DM || chan->Type == ChannelType::GROUP_DM) - ret.insert(id); - } - - return ret; -} - -EPremiumType DiscordClient::GetSelfPremiumType() const { - const auto &data = GetUserData(); - if (data.PremiumType.has_value()) - return *data.PremiumType; - return EPremiumType::None; -} - -void DiscordClient::HeartbeatThread() { - while (m_client_connected) { - if (!m_heartbeat_acked) { - printf("wow! a heartbeat wasn't acked! how could this happen?"); - } - - m_heartbeat_acked = false; - - HeartbeatMessage msg; - msg.Sequence = m_last_sequence; - nlohmann::json j = msg; - m_websocket.Send(j); - - if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) - break; - } -} - -void DiscordClient::SendIdentify() { - IdentifyMessage msg; - msg.Token = m_token; - msg.Capabilities = 125; // no idea what this is - msg.Properties.OS = "Windows"; - msg.Properties.Browser = "Chrome"; - msg.Properties.Device = ""; - msg.Properties.SystemLocale = "en-US"; - msg.Properties.BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36"; - msg.Properties.BrowserVersion = "67.0.3396.87"; - msg.Properties.OSVersion = "10"; - msg.Properties.Referrer = ""; - msg.Properties.ReferringDomain = ""; - msg.Properties.ReferrerCurrent = ""; - msg.Properties.ReferringDomainCurrent = ""; - msg.Properties.ReleaseChannel = "stable"; - msg.Properties.ClientBuildNumber = 91734; - msg.Properties.ClientEventSource = ""; - msg.Presence.Status = "online"; - msg.Presence.Since = 0; - msg.Presence.IsAFK = false; - msg.DoesSupportCompression = false; - msg.ClientState.HighestLastMessageID = "0"; - msg.ClientState.ReadStateVersion = 0; - msg.ClientState.UserGuildSettingsVersion = -1; - m_websocket.Send(msg); -} - -void DiscordClient::SendResume() { - ResumeMessage msg; - msg.Sequence = m_last_sequence; - msg.SessionID = m_session_id; - msg.Token = m_token; - m_websocket.Send(msg); -} - -void DiscordClient::HandleSocketOpen() { -} - -void DiscordClient::HandleSocketClose(uint16_t code) { - printf("got socket close code: %d\n", code); - auto close_code = static_cast(code); - auto cb = [this, close_code]() { - m_heartbeat_waiter.kill(); - if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); - m_client_connected = false; - - if (m_client_started && !m_reconnecting && close_code == GatewayCloseCode::Abnormal) { - Glib::signal_timeout().connect_once([this] { if (m_client_started) HandleGatewayReconnect(GatewayMessage()); }, 1000); - m_reconnecting = true; - } - - m_signal_disconnected.emit(m_reconnecting, close_code); - }; - m_generic_mutex.lock(); - m_generic_queue.push(cb); - m_generic_dispatch.emit(); - m_generic_mutex.unlock(); -} - -bool DiscordClient::CheckCode(const http::response_type &r) { - if (r.status_code >= 300 || r.error) { - fprintf(stderr, "api request to %s failed with status code %d: %s\n", r.url.c_str(), r.status_code, r.error_string.c_str()); - return false; - } - - return true; -} - -bool DiscordClient::CheckCode(const http::response_type &r, int expected) { - if (!CheckCode(r)) return false; - if (r.status_code != expected) { - fprintf(stderr, "api request to %s returned %d, expected %d\n", r.url.c_str(), r.status_code, expected); - return false; - } - return true; -} - -void DiscordClient::StoreMessageData(Message &msg) { - const auto chan = m_store.GetChannel(msg.ChannelID); - if (chan.has_value() && chan->GuildID.has_value()) - msg.GuildID = *chan->GuildID; - - m_store.BeginTransaction(); - - m_store.SetMessage(msg.ID, msg); - m_store.SetUser(msg.Author.ID, msg.Author); - if (msg.Reactions.has_value()) - for (const auto &r : *msg.Reactions) { - if (!r.Emoji.ID.IsValid()) continue; - const auto cur = m_store.GetEmoji(r.Emoji.ID); - if (!cur.has_value()) - m_store.SetEmoji(r.Emoji.ID, r.Emoji); - } - - for (const auto &user : msg.Mentions) - m_store.SetUser(user.ID, user); - - if (msg.Member.has_value()) - m_store.SetGuildMember(*msg.GuildID, msg.Author.ID, *msg.Member); - - if (msg.Interaction.has_value() && msg.Interaction->Member.has_value()) { - m_store.SetUser(msg.Interaction->User.ID, msg.Interaction->User); - m_store.SetGuildMember(*msg.GuildID, msg.Interaction->User.ID, *msg.Interaction->Member); - } - - m_store.EndTransaction(); - - if (msg.ReferencedMessage.has_value() && msg.MessageReference.has_value() && msg.MessageReference->ChannelID.has_value()) - if (msg.ReferencedMessage.value() != nullptr) - StoreMessageData(**msg.ReferencedMessage); -} - -void DiscordClient::LoadEventMap() { - m_event_map["READY"] = GatewayEvent::READY; - m_event_map["MESSAGE_CREATE"] = GatewayEvent::MESSAGE_CREATE; - m_event_map["MESSAGE_DELETE"] = GatewayEvent::MESSAGE_DELETE; - m_event_map["MESSAGE_UPDATE"] = GatewayEvent::MESSAGE_UPDATE; - m_event_map["GUILD_MEMBER_LIST_UPDATE"] = GatewayEvent::GUILD_MEMBER_LIST_UPDATE; - m_event_map["GUILD_CREATE"] = GatewayEvent::GUILD_CREATE; - m_event_map["GUILD_DELETE"] = GatewayEvent::GUILD_DELETE; - m_event_map["MESSAGE_DELETE_BULK"] = GatewayEvent::MESSAGE_DELETE_BULK; - m_event_map["GUILD_MEMBER_UPDATE"] = GatewayEvent::GUILD_MEMBER_UPDATE; - m_event_map["PRESENCE_UPDATE"] = GatewayEvent::PRESENCE_UPDATE; - m_event_map["CHANNEL_DELETE"] = GatewayEvent::CHANNEL_DELETE; - m_event_map["CHANNEL_UPDATE"] = GatewayEvent::CHANNEL_UPDATE; - m_event_map["CHANNEL_CREATE"] = GatewayEvent::CHANNEL_CREATE; - m_event_map["GUILD_UPDATE"] = GatewayEvent::GUILD_UPDATE; - m_event_map["GUILD_ROLE_UPDATE"] = GatewayEvent::GUILD_ROLE_UPDATE; - m_event_map["GUILD_ROLE_CREATE"] = GatewayEvent::GUILD_ROLE_CREATE; - m_event_map["GUILD_ROLE_DELETE"] = GatewayEvent::GUILD_ROLE_DELETE; - m_event_map["MESSAGE_REACTION_ADD"] = GatewayEvent::MESSAGE_REACTION_ADD; - m_event_map["MESSAGE_REACTION_REMOVE"] = GatewayEvent::MESSAGE_REACTION_REMOVE; - m_event_map["CHANNEL_RECIPIENT_ADD"] = GatewayEvent::CHANNEL_RECIPIENT_ADD; - m_event_map["CHANNEL_RECIPIENT_REMOVE"] = GatewayEvent::CHANNEL_RECIPIENT_REMOVE; - m_event_map["TYPING_START"] = GatewayEvent::TYPING_START; - m_event_map["GUILD_BAN_REMOVE"] = GatewayEvent::GUILD_BAN_REMOVE; - m_event_map["GUILD_BAN_ADD"] = GatewayEvent::GUILD_BAN_ADD; - m_event_map["INVITE_CREATE"] = GatewayEvent::INVITE_CREATE; - m_event_map["INVITE_DELETE"] = GatewayEvent::INVITE_DELETE; - m_event_map["USER_NOTE_UPDATE"] = GatewayEvent::USER_NOTE_UPDATE; - m_event_map["READY_SUPPLEMENTAL"] = GatewayEvent::READY_SUPPLEMENTAL; - m_event_map["GUILD_EMOJIS_UPDATE"] = GatewayEvent::GUILD_EMOJIS_UPDATE; - m_event_map["GUILD_JOIN_REQUEST_CREATE"] = GatewayEvent::GUILD_JOIN_REQUEST_CREATE; - m_event_map["GUILD_JOIN_REQUEST_UPDATE"] = GatewayEvent::GUILD_JOIN_REQUEST_UPDATE; - m_event_map["GUILD_JOIN_REQUEST_DELETE"] = GatewayEvent::GUILD_JOIN_REQUEST_DELETE; - m_event_map["RELATIONSHIP_REMOVE"] = GatewayEvent::RELATIONSHIP_REMOVE; - m_event_map["RELATIONSHIP_ADD"] = GatewayEvent::RELATIONSHIP_ADD; - m_event_map["THREAD_CREATE"] = GatewayEvent::THREAD_CREATE; - m_event_map["THREAD_DELETE"] = GatewayEvent::THREAD_DELETE; - m_event_map["THREAD_LIST_SYNC"] = GatewayEvent::THREAD_LIST_SYNC; - m_event_map["THREAD_MEMBERS_UPDATE"] = GatewayEvent::THREAD_MEMBERS_UPDATE; - m_event_map["THREAD_MEMBER_UPDATE"] = GatewayEvent::THREAD_MEMBER_UPDATE; - m_event_map["THREAD_UPDATE"] = GatewayEvent::THREAD_UPDATE; - m_event_map["THREAD_MEMBER_LIST_UPDATE"] = GatewayEvent::THREAD_MEMBER_LIST_UPDATE; -} - -DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() { - return m_signal_gateway_ready; -} - -DiscordClient::type_signal_message_create DiscordClient::signal_message_create() { - return m_signal_message_create; -} - -DiscordClient::type_signal_message_delete DiscordClient::signal_message_delete() { - return m_signal_message_delete; -} - -DiscordClient::type_signal_message_update DiscordClient::signal_message_update() { - return m_signal_message_update; -} - -DiscordClient::type_signal_guild_member_list_update DiscordClient::signal_guild_member_list_update() { - return m_signal_guild_member_list_update; -} - -DiscordClient::type_signal_guild_create DiscordClient::signal_guild_create() { - return m_signal_guild_create; -} - -DiscordClient::type_signal_guild_delete DiscordClient::signal_guild_delete() { - return m_signal_guild_delete; -} - -DiscordClient::type_signal_channel_delete DiscordClient::signal_channel_delete() { - return m_signal_channel_delete; -} - -DiscordClient::type_signal_channel_update DiscordClient::signal_channel_update() { - return m_signal_channel_update; -} - -DiscordClient::type_signal_channel_create DiscordClient::signal_channel_create() { - return m_signal_channel_create; -} - -DiscordClient::type_signal_guild_update DiscordClient::signal_guild_update() { - return m_signal_guild_update; -} - -DiscordClient::type_signal_disconnected DiscordClient::signal_disconnected() { - return m_signal_disconnected; -} - -DiscordClient::type_signal_connected DiscordClient::signal_connected() { - return m_signal_connected; -} - -DiscordClient::type_signal_role_update DiscordClient::signal_role_update() { - return m_signal_role_update; -} - -DiscordClient::type_signal_role_create DiscordClient::signal_role_create() { - return m_signal_role_create; -} - -DiscordClient::type_signal_role_delete DiscordClient::signal_role_delete() { - return m_signal_role_delete; -} - -DiscordClient::type_signal_reaction_add DiscordClient::signal_reaction_add() { - return m_signal_reaction_add; -} - -DiscordClient::type_signal_reaction_remove DiscordClient::signal_reaction_remove() { - return m_signal_reaction_remove; -} - -DiscordClient::type_signal_typing_start DiscordClient::signal_typing_start() { - return m_signal_typing_start; -} - -DiscordClient::type_signal_guild_member_update DiscordClient::signal_guild_member_update() { - return m_signal_guild_member_update; -} - -DiscordClient::type_signal_guild_ban_remove DiscordClient::signal_guild_ban_remove() { - return m_signal_guild_ban_remove; -} - -DiscordClient::type_signal_guild_ban_add DiscordClient::signal_guild_ban_add() { - return m_signal_guild_ban_add; -} - -DiscordClient::type_signal_invite_create DiscordClient::signal_invite_create() { - return m_signal_invite_create; -} - -DiscordClient::type_signal_invite_delete DiscordClient::signal_invite_delete() { - return m_signal_invite_delete; -} - -DiscordClient::type_signal_presence_update DiscordClient::signal_presence_update() { - return m_signal_presence_update; -} - -DiscordClient::type_signal_note_update DiscordClient::signal_note_update() { - return m_signal_note_update; -} - -DiscordClient::type_signal_guild_emojis_update DiscordClient::signal_guild_emojis_update() { - return m_signal_guild_emojis_update; -} - -DiscordClient::type_signal_guild_join_request_create DiscordClient::signal_guild_join_request_create() { - return m_signal_guild_join_request_create; -} - -DiscordClient::type_signal_guild_join_request_update DiscordClient::signal_guild_join_request_update() { - return m_signal_guild_join_request_update; -} - -DiscordClient::type_signal_guild_join_request_delete DiscordClient::signal_guild_join_request_delete() { - return m_signal_guild_join_request_delete; -} - -DiscordClient::type_signal_relationship_remove DiscordClient::signal_relationship_remove() { - return m_signal_relationship_remove; -} - -DiscordClient::type_signal_relationship_add DiscordClient::signal_relationship_add() { - return m_signal_relationship_add; -} - -DiscordClient::type_signal_message_unpinned DiscordClient::signal_message_unpinned() { - return m_signal_message_unpinned; -} - -DiscordClient::type_signal_message_pinned DiscordClient::signal_message_pinned() { - return m_signal_message_pinned; -} - -DiscordClient::type_signal_thread_create DiscordClient::signal_thread_create() { - return m_signal_thread_create; -} - -DiscordClient::type_signal_thread_delete DiscordClient::signal_thread_delete() { - return m_signal_thread_delete; -} - -DiscordClient::type_signal_thread_list_sync DiscordClient::signal_thread_list_sync() { - return m_signal_thread_list_sync; -} - -DiscordClient::type_signal_thread_members_update DiscordClient::signal_thread_members_update() { - return m_signal_thread_members_update; -} - -DiscordClient::type_signal_thread_update DiscordClient::signal_thread_update() { - return m_signal_thread_update; -} - -DiscordClient::type_signal_thread_member_list_update DiscordClient::signal_thread_member_list_update() { - return m_signal_thread_member_list_update; -} - -DiscordClient::type_signal_added_to_thread DiscordClient::signal_added_to_thread() { - return m_signal_added_to_thread; -} - -DiscordClient::type_signal_removed_from_thread DiscordClient::signal_removed_from_thread() { - return m_signal_removed_from_thread; -} - -DiscordClient::type_signal_message_sent DiscordClient::signal_message_sent() { - return m_signal_message_sent; -} - -DiscordClient::type_signal_message_send_fail DiscordClient::signal_message_send_fail() { - return m_signal_message_send_fail; -} diff --git a/discord/discord.hpp b/discord/discord.hpp deleted file mode 100644 index 4b9bc82..0000000 --- a/discord/discord.hpp +++ /dev/null @@ -1,449 +0,0 @@ -#pragma once -#include "websocket.hpp" -#include "httpclient.hpp" -#include "objects.hpp" -#include "store.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef GetMessage - #undef GetMessage -#endif - -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(); - } - - void revive() { - std::unique_lock lock(m); - terminate = false; - } - -private: - mutable std::condition_variable cv; - mutable std::mutex m; - bool terminate = false; -}; - -class Abaddon; -class DiscordClient { - friend class Abaddon; - -public: - DiscordClient(bool mem_store = false); - void Start(); - void Stop(); - bool IsStarted() const; - bool IsStoreValid() const; - - std::unordered_set GetGuilds() const; - const UserData &GetUserData() const; - const UserSettings &GetUserSettings() const; - std::vector GetUserSortedGuilds() const; - std::vector GetMessagesForChannel(Snowflake id, size_t limit = 50) const; - std::vector GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const; - std::set GetPrivateChannels() const; - - EPremiumType GetSelfPremiumType() const; - - void FetchMessagesInChannel(Snowflake id, sigc::slot &)> cb); - void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, sigc::slot &)> cb); - std::optional GetMessage(Snowflake id) const; - std::optional GetChannel(Snowflake id) const; - std::optional GetEmoji(Snowflake id) const; - std::optional GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const; - std::optional GetUser(Snowflake id) const; - std::optional GetRole(Snowflake id) const; - std::optional GetGuild(Snowflake id) const; - std::optional GetMember(Snowflake user_id, Snowflake guild_id) const; - std::optional GetBan(Snowflake guild_id, Snowflake user_id) const; - Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const; - std::optional GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const; - std::set GetUsersInGuild(Snowflake id) const; - std::set GetChannelsInGuild(Snowflake id) const; - std::vector GetUsersInThread(Snowflake id) const; - std::vector GetActiveThreads(Snowflake channel_id) const; - void GetArchivedPublicThreads(Snowflake channel_id, sigc::slot callback); - - bool IsThreadJoined(Snowflake thread_id) const; - bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const; - - bool HasAnyChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const; - bool HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const; - Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const; - Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const; - bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name) - - void ChatMessageCallback(std::string nonce, const http::response_type &response); - - void SendChatMessage(const std::string &content, Snowflake channel); - void SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message); - void DeleteMessage(Snowflake channel_id, Snowflake id); - void EditMessage(Snowflake channel_id, Snowflake id, std::string content); - void SendLazyLoad(Snowflake id); - void SendThreadLazyLoad(Snowflake id); - void JoinGuild(std::string code); - void LeaveGuild(Snowflake id); - void KickUser(Snowflake user_id, Snowflake guild_id); - void BanUser(Snowflake user_id, Snowflake guild_id); // todo: reason, delete messages - void UpdateStatus(PresenceStatus status, bool is_afk); - void UpdateStatus(PresenceStatus status, bool is_afk, const ActivityData &obj); - void CreateDM(Snowflake user_id); - void CreateDM(Snowflake user_id, sigc::slot callback); - void CloseDM(Snowflake channel_id); - std::optional FindDM(Snowflake user_id); // wont find group dms - void AddReaction(Snowflake id, Glib::ustring param); - void RemoveReaction(Snowflake id, Glib::ustring param); - void SetGuildName(Snowflake id, const Glib::ustring &name); - void SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot callback); - void SetGuildIcon(Snowflake id, const std::string &data); - void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot callback); - void UnbanUser(Snowflake guild_id, Snowflake user_id); - void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot callback); - void DeleteInvite(const std::string &code); - void DeleteInvite(const std::string &code, sigc::slot callback); - void AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id); - void RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id); - void ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, sigc::slot callback); - void ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, sigc::slot callback); - void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, sigc::slot callback); - void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::RGBA color, sigc::slot callback); - void ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot callback); - void ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot callback); - void DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot callback); - std::optional GetGuildApplication(Snowflake guild_id) const; - void RemoveRelationship(Snowflake id, sigc::slot callback); - void SendFriendRequest(const Glib::ustring &username, int discriminator, sigc::slot callback); - void PutRelationship(Snowflake id, sigc::slot callback); // send fr by id, accept incoming - void Pin(Snowflake channel_id, Snowflake message_id, sigc::slot callback); - void Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot callback); - void LeaveThread(Snowflake channel_id, const std::string &location, sigc::slot callback); - void ArchiveThread(Snowflake channel_id, sigc::slot callback); - void UnArchiveThread(Snowflake channel_id, sigc::slot callback); - - bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const; - bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const; - - // real client doesn't seem to use the single role endpoints so neither do we - template - auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot callback) { - ModifyGuildMemberObject obj; - obj.Roles = { begin, end }; - m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/members/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) { - if (CheckCode(response)) - callback(DiscordError::NONE); - else - callback(GetCodeFromResponse(response)); - }); - } - - // FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data) - std::vector GetBansInGuild(Snowflake guild_id); - void FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot callback); - void FetchGuildBans(Snowflake guild_id, sigc::slot)> callback); - - void FetchInvite(std::string code, sigc::slot)> callback); - void FetchGuildInvites(Snowflake guild_id, sigc::slot)> callback); - - void FetchAuditLog(Snowflake guild_id, sigc::slot callback); - - void FetchGuildEmojis(Snowflake guild_id, sigc::slot)> callback); - - void FetchUserProfile(Snowflake user_id, sigc::slot callback); - void FetchUserNote(Snowflake user_id, sigc::slot callback); - void SetUserNote(Snowflake user_id, std::string note); - void SetUserNote(Snowflake user_id, std::string note, sigc::slot callback); - void FetchUserRelationships(Snowflake user_id, sigc::slot)> callback); - - void FetchPinned(Snowflake id, sigc::slot, DiscordError code)> callback); - - bool IsVerificationRequired(Snowflake guild_id); - void GetVerificationGateInfo(Snowflake guild_id, sigc::slot)> callback); - void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot callback); - - void UpdateToken(std::string token); - void SetUserAgent(std::string agent); - - PresenceStatus GetUserStatus(Snowflake id) const; - - std::map GetRelationships() const; - std::set GetRelationships(RelationshipType type) const; - std::optional GetRelationship(Snowflake id) const; - -private: - static const constexpr int InflateChunkSize = 0x10000; - std::vector m_compressed_buf; - std::vector m_decompress_buf; - z_stream m_zstream; - - std::string GetAPIURL(); - std::string GetGatewayURL(); - - static DiscordError GetCodeFromResponse(const http::response_type &response); - - void ProcessNewGuild(GuildData &guild); - - void HandleGatewayMessageRaw(std::string str); - void HandleGatewayMessage(std::string str); - void HandleGatewayHello(const GatewayMessage &msg); - 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 HandleGatewayGuildCreate(const GatewayMessage &msg); - void HandleGatewayGuildDelete(const GatewayMessage &msg); - void HandleGatewayMessageDeleteBulk(const GatewayMessage &msg); - void HandleGatewayGuildMemberUpdate(const GatewayMessage &msg); - void HandleGatewayPresenceUpdate(const GatewayMessage &msg); - void HandleGatewayChannelDelete(const GatewayMessage &msg); - void HandleGatewayChannelUpdate(const GatewayMessage &msg); - void HandleGatewayChannelCreate(const GatewayMessage &msg); - void HandleGatewayGuildUpdate(const GatewayMessage &msg); - void HandleGatewayGuildRoleUpdate(const GatewayMessage &msg); - void HandleGatewayGuildRoleCreate(const GatewayMessage &msg); - void HandleGatewayGuildRoleDelete(const GatewayMessage &msg); - void HandleGatewayMessageReactionAdd(const GatewayMessage &msg); - void HandleGatewayMessageReactionRemove(const GatewayMessage &msg); - void HandleGatewayChannelRecipientAdd(const GatewayMessage &msg); - void HandleGatewayChannelRecipientRemove(const GatewayMessage &msg); - void HandleGatewayTypingStart(const GatewayMessage &msg); - void HandleGatewayGuildBanRemove(const GatewayMessage &msg); - void HandleGatewayGuildBanAdd(const GatewayMessage &msg); - void HandleGatewayInviteCreate(const GatewayMessage &msg); - void HandleGatewayInviteDelete(const GatewayMessage &msg); - void HandleGatewayUserNoteUpdate(const GatewayMessage &msg); - void HandleGatewayGuildEmojisUpdate(const GatewayMessage &msg); - void HandleGatewayGuildJoinRequestCreate(const GatewayMessage &msg); - void HandleGatewayGuildJoinRequestUpdate(const GatewayMessage &msg); - void HandleGatewayGuildJoinRequestDelete(const GatewayMessage &msg); - void HandleGatewayRelationshipRemove(const GatewayMessage &msg); - void HandleGatewayRelationshipAdd(const GatewayMessage &msg); - void HandleGatewayThreadCreate(const GatewayMessage &msg); - void HandleGatewayThreadDelete(const GatewayMessage &msg); - void HandleGatewayThreadListSync(const GatewayMessage &msg); - void HandleGatewayThreadMembersUpdate(const GatewayMessage &msg); - void HandleGatewayThreadMemberUpdate(const GatewayMessage &msg); - void HandleGatewayThreadUpdate(const GatewayMessage &msg); - void HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg); - void HandleGatewayReadySupplemental(const GatewayMessage &msg); - void HandleGatewayReconnect(const GatewayMessage &msg); - void HandleGatewayInvalidSession(const GatewayMessage &msg); - void HeartbeatThread(); - void SendIdentify(); - void SendResume(); - - void HandleSocketOpen(); - void HandleSocketClose(uint16_t code); - - bool CheckCode(const http::response_type &r); - bool CheckCode(const http::response_type &r, int expected); - - void StoreMessageData(Message &msg); - - std::string m_token; - - void AddUserToGuild(Snowflake user_id, Snowflake guild_id); - std::map> m_guild_to_users; - std::map> m_guild_to_channels; - std::map m_guild_join_requests; - std::map m_user_to_status; - std::map m_user_relationships; - std::set m_joined_threads; - std::map> m_thread_members; - - UserData m_user_data; - UserSettings m_user_settings; - - Store m_store; - HTTPClient m_http; - Websocket m_websocket; - std::atomic m_client_connected = false; - std::atomic m_ready_received = false; - bool m_client_started = false; - - std::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; - - bool m_reconnecting = false; // reconnecting either to resume or reidentify - bool m_wants_resume = false; // reconnecting specifically to resume - std::string m_session_id; - - mutable std::mutex m_msg_mutex; - Glib::Dispatcher m_msg_dispatch; - std::queue m_msg_queue; - void MessageDispatch(); - - mutable std::mutex m_generic_mutex; - Glib::Dispatcher m_generic_dispatch; - std::queue> m_generic_queue; - - std::set m_channels_pinned_requested; - std::set m_channels_lazy_loaded; - - // signals -public: - typedef sigc::signal type_signal_gateway_ready; - 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; - typedef sigc::signal type_signal_guild_create; - typedef sigc::signal type_signal_guild_delete; - typedef sigc::signal type_signal_channel_delete; - typedef sigc::signal type_signal_channel_update; - typedef sigc::signal type_signal_channel_create; - typedef sigc::signal type_signal_guild_update; - typedef sigc::signal type_signal_role_update; // guild id, role id - typedef sigc::signal type_signal_role_create; // guild id, role id - typedef sigc::signal type_signal_role_delete; // guild id, role id - typedef sigc::signal type_signal_reaction_add; - typedef sigc::signal type_signal_reaction_remove; - typedef sigc::signal type_signal_typing_start; // user id, channel id - typedef sigc::signal type_signal_guild_member_update; // guild id, user id - typedef sigc::signal type_signal_guild_ban_remove; // guild id, user id - typedef sigc::signal type_signal_guild_ban_add; // guild id, user id - typedef sigc::signal type_signal_invite_create; - typedef sigc::signal type_signal_invite_delete; - typedef sigc::signal type_signal_presence_update; - typedef sigc::signal type_signal_note_update; - typedef sigc::signal> type_signal_guild_emojis_update; // guild id - typedef sigc::signal type_signal_guild_join_request_create; - typedef sigc::signal type_signal_guild_join_request_update; - typedef sigc::signal type_signal_guild_join_request_delete; - typedef sigc::signal type_signal_relationship_remove; - typedef sigc::signal type_signal_relationship_add; - typedef sigc::signal type_signal_thread_create; - typedef sigc::signal type_signal_thread_delete; - typedef sigc::signal type_signal_thread_list_sync; - typedef sigc::signal type_signal_thread_members_update; - typedef sigc::signal type_signal_thread_update; - typedef sigc::signal type_signal_thread_member_list_update; - - // not discord dispatch events - typedef sigc::signal type_signal_added_to_thread; - typedef sigc::signal type_signal_removed_from_thread; - typedef sigc::signal type_signal_message_unpinned; - typedef sigc::signal type_signal_message_pinned; - typedef sigc::signal type_signal_message_sent; - - typedef sigc::signal type_signal_message_send_fail; // retry after param will be 0 if it failed for a reason that isnt slowmode - typedef sigc::signal type_signal_disconnected; // bool true if reconnecting - typedef sigc::signal type_signal_connected; - - type_signal_gateway_ready signal_gateway_ready(); - 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(); - type_signal_guild_create signal_guild_create(); // structs are complete in this signal - type_signal_guild_delete signal_guild_delete(); - type_signal_channel_delete signal_channel_delete(); - type_signal_channel_update signal_channel_update(); - type_signal_channel_create signal_channel_create(); - type_signal_guild_update signal_guild_update(); - type_signal_role_update signal_role_update(); - type_signal_role_create signal_role_create(); - type_signal_role_delete signal_role_delete(); - type_signal_reaction_add signal_reaction_add(); - type_signal_reaction_remove signal_reaction_remove(); - type_signal_typing_start signal_typing_start(); - type_signal_guild_member_update signal_guild_member_update(); - type_signal_guild_ban_remove signal_guild_ban_remove(); - type_signal_guild_ban_add signal_guild_ban_add(); - type_signal_invite_create signal_invite_create(); - type_signal_invite_delete signal_invite_delete(); // safe to assume guild id is set - type_signal_presence_update signal_presence_update(); - type_signal_note_update signal_note_update(); - type_signal_guild_emojis_update signal_guild_emojis_update(); - type_signal_guild_join_request_create signal_guild_join_request_create(); - type_signal_guild_join_request_update signal_guild_join_request_update(); - type_signal_guild_join_request_delete signal_guild_join_request_delete(); - type_signal_relationship_remove signal_relationship_remove(); - type_signal_relationship_add signal_relationship_add(); - type_signal_message_unpinned signal_message_unpinned(); - type_signal_message_pinned signal_message_pinned(); - type_signal_thread_create signal_thread_create(); - type_signal_thread_delete signal_thread_delete(); - type_signal_thread_list_sync signal_thread_list_sync(); - type_signal_thread_members_update signal_thread_members_update(); - type_signal_thread_update signal_thread_update(); - type_signal_thread_member_list_update signal_thread_member_list_update(); - - type_signal_added_to_thread signal_added_to_thread(); - type_signal_removed_from_thread signal_removed_from_thread(); - type_signal_message_sent signal_message_sent(); - type_signal_message_send_fail signal_message_send_fail(); - type_signal_disconnected signal_disconnected(); - type_signal_connected signal_connected(); - -protected: - type_signal_gateway_ready m_signal_gateway_ready; - 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; - type_signal_guild_create m_signal_guild_create; - type_signal_guild_delete m_signal_guild_delete; - type_signal_channel_delete m_signal_channel_delete; - type_signal_channel_update m_signal_channel_update; - type_signal_channel_create m_signal_channel_create; - type_signal_guild_update m_signal_guild_update; - type_signal_role_update m_signal_role_update; - type_signal_role_create m_signal_role_create; - type_signal_role_delete m_signal_role_delete; - type_signal_reaction_add m_signal_reaction_add; - type_signal_reaction_remove m_signal_reaction_remove; - type_signal_typing_start m_signal_typing_start; - type_signal_guild_member_update m_signal_guild_member_update; - type_signal_guild_ban_remove m_signal_guild_ban_remove; - type_signal_guild_ban_add m_signal_guild_ban_add; - type_signal_invite_create m_signal_invite_create; - type_signal_invite_delete m_signal_invite_delete; - type_signal_presence_update m_signal_presence_update; - type_signal_note_update m_signal_note_update; - type_signal_guild_emojis_update m_signal_guild_emojis_update; - type_signal_guild_join_request_create m_signal_guild_join_request_create; - type_signal_guild_join_request_update m_signal_guild_join_request_update; - type_signal_guild_join_request_delete m_signal_guild_join_request_delete; - type_signal_relationship_remove m_signal_relationship_remove; - type_signal_relationship_add m_signal_relationship_add; - type_signal_message_unpinned m_signal_message_unpinned; - type_signal_message_pinned m_signal_message_pinned; - type_signal_thread_create m_signal_thread_create; - type_signal_thread_delete m_signal_thread_delete; - type_signal_thread_list_sync m_signal_thread_list_sync; - type_signal_thread_members_update m_signal_thread_members_update; - type_signal_thread_update m_signal_thread_update; - type_signal_thread_member_list_update m_signal_thread_member_list_update; - - type_signal_removed_from_thread m_signal_removed_from_thread; - type_signal_added_to_thread m_signal_added_to_thread; - type_signal_message_sent m_signal_message_sent; - type_signal_message_send_fail m_signal_message_send_fail; - type_signal_disconnected m_signal_disconnected; - type_signal_connected m_signal_connected; -}; diff --git a/discord/emoji.cpp b/discord/emoji.cpp deleted file mode 100644 index 1a97eb8..0000000 --- a/discord/emoji.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "emoji.hpp" - -void from_json(const nlohmann::json &j, EmojiData &m) { - JS_N("id", m.ID); - JS_N("name", m.Name); - JS_O("roles", m.Roles); - JS_O("user", m.Creator); - JS_O("require_colons", m.NeedsColons); - JS_O("managed", m.IsManaged); - JS_O("animated", m.IsAnimated); - JS_O("available", m.IsAvailable); -} - -void to_json(nlohmann::json &j, const EmojiData &m) { - if (m.ID.IsValid()) - j["id"] = m.ID; - else - j["id"] = nullptr; - if (m.Name != "") - j["name"] = m.Name; - else - j["name"] = nullptr; - JS_IF("roles", m.Roles); - JS_IF("user", m.Creator); - JS_IF("require_colons", m.NeedsColons); - JS_IF("managed", m.IsManaged); - JS_IF("animated", m.IsAnimated); - JS_IF("available", m.IsAvailable); -} - -std::string EmojiData::GetURL(const char *ext, const char *size) const { - if (size != nullptr) - return "https://cdn.discordapp.com/emojis/" + std::to_string(ID) + "." + ext + "?size=" + size; - else - return "https://cdn.discordapp.com/emojis/" + std::to_string(ID) + "." + ext; -} - -std::string EmojiData::URLFromID(const std::string &emoji_id, const char *ext, const char *size) { - if (size != nullptr) - return "https://cdn.discordapp.com/emojis/" + emoji_id + "." + ext + "?size=" + size; - else - return "https://cdn.discordapp.com/emojis/" + emoji_id + "." + ext; -} - -std::string EmojiData::URLFromID(Snowflake emoji_id, const char *ext, const char *size) { - return URLFromID(std::to_string(emoji_id), ext, size); -} - -std::string EmojiData::URLFromID(const Glib::ustring &emoji_id, const char *ext, const char *size) { - return URLFromID(emoji_id.raw(), ext, size); -} diff --git a/discord/emoji.hpp b/discord/emoji.hpp deleted file mode 100644 index 156e127..0000000 --- a/discord/emoji.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include "json.hpp" -#include "snowflake.hpp" -#include "user.hpp" - -struct EmojiData { - Snowflake ID; // null - std::string Name; // null (in reactions) - std::optional> Roles; - std::optional Creator; // only reliable to access ID - std::optional NeedsColons; - std::optional IsManaged; - std::optional IsAnimated; - std::optional IsAvailable; - - friend void from_json(const nlohmann::json &j, EmojiData &m); - friend void to_json(nlohmann::json &j, const EmojiData &m); - - std::string GetURL(const char *ext = "png", const char *size = nullptr) const; - static std::string URLFromID(const std::string &emoji_id, const char *ext = "png", const char *size = nullptr); - static std::string URLFromID(Snowflake emoji_id, const char *ext = "png", const char *size = nullptr); - static std::string URLFromID(const Glib::ustring &emoji_id, const char *ext = "png", const char *size = nullptr); -}; diff --git a/discord/errors.hpp b/discord/errors.hpp deleted file mode 100644 index 4579563..0000000 --- a/discord/errors.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -enum class DiscordError { - GENERIC = 0, - INVALID_FORM_BODY = 50035, - RELATIONSHIP_INCOMING_DISABLED = 80000, - RELATIONSHIP_INCOMING_BLOCKED = 80001, - RELATIONSHIP_INVALID_USER_BOT = 80002, // this is misspelled in discord's source lul - RELATIONSHIP_INVALID_SELF = 80003, - RELATIONSHIP_INVALID_DISCORD_TAG = 80004, - RELATIONSHIP_ALREADY_FRIENDS = 80007, - - NONE = -1, -}; - -constexpr const char *GetDiscordErrorDisplayString(DiscordError error) { - switch (error) { - case DiscordError::INVALID_FORM_BODY: - return "Something's wrong with your input"; - case DiscordError::RELATIONSHIP_INCOMING_DISABLED: - return "This user isn't accepting friend requests"; - case DiscordError::RELATIONSHIP_INCOMING_BLOCKED: - return "You are blocked by this user"; - case DiscordError::RELATIONSHIP_INVALID_USER_BOT: - return "You can't send a request to a bot"; - case DiscordError::RELATIONSHIP_INVALID_SELF: - return "You can't send a request to yourself"; - case DiscordError::RELATIONSHIP_INVALID_DISCORD_TAG: - return "No users with that tag exist"; - case DiscordError::RELATIONSHIP_ALREADY_FRIENDS: - return "You are already friends with that user"; - case DiscordError::GENERIC: - default: - return "An error occurred"; - } -} diff --git a/discord/guild.cpp b/discord/guild.cpp deleted file mode 100644 index a02b896..0000000 --- a/discord/guild.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "guild.hpp" -#include "abaddon.hpp" - -void from_json(const nlohmann::json &j, GuildData &m) { - JS_D("id", m.ID); - if (j.contains("unavailable")) { - m.IsUnavailable = true; - return; - } - - JS_D("name", m.Name); - JS_N("icon", m.Icon); - JS_N("splash", m.Splash); - JS_ON("discovery_splash", m.DiscoverySplash); - JS_O("owner", m.IsOwner); - JS_O("owner_id", m.OwnerID); - std::optional tmp; - JS_O("permissions", tmp); - if (tmp.has_value()) - m.Permissions = std::stoull(*tmp); - JS_O("region", m.VoiceRegion); - JS_ON("afk_channel_id", m.AFKChannelID); - JS_O("afk_timeout", m.AFKTimeout); - JS_O("embed_enabled", m.IsEmbedEnabled); - JS_ON("embed_channel_id", m.EmbedChannelID); - JS_O("verification_level", m.VerificationLevel); - JS_O("default_message_notifications", m.DefaultMessageNotifications); - JS_O("explicit_content_filter", m.ExplicitContentFilter); - JS_O("roles", m.Roles); - JS_O("emojis", m.Emojis); - JS_O("features", m.Features); - JS_O("mfa_level", m.MFALevel); - JS_ON("application_id", m.ApplicationID); - JS_O("widget_enabled", m.IsWidgetEnabled); - JS_ON("widget_channel_id", m.WidgetChannelID); - JS_ON("system_channel_id", m.SystemChannelID); - JS_O("system_channel_flags", m.SystemChannelFlags); - JS_ON("rules_channel_id", m.RulesChannelID); - JS_O("joined_at", m.JoinedAt); - JS_O("large", m.IsLarge); - JS_O("unavailable", m.IsUnavailable); - JS_O("member_count", m.MemberCount); - // JS_O("voice_states", m.VoiceStates); - // JS_O("members", m.Members); - JS_O("channels", m.Channels); - JS_O("threads", m.Threads); - // JS_O("presences", m.Presences); - JS_ON("max_presences", m.MaxPresences); - JS_O("max_members", m.MaxMembers); - JS_ON("vanity_url_code", m.VanityURL); - JS_ON("description", m.Description); - JS_ON("banner", m.BannerHash); - JS_O("premium_tier", m.PremiumTier); - JS_O("premium_subscription_count", m.PremiumSubscriptionCount); - JS_O("preferred_locale", m.PreferredLocale); - JS_ON("public_updates_channel_id", m.PublicUpdatesChannelID); - JS_O("max_video_channel_users", m.MaxVideoChannelUsers); - JS_O("approximate_member_count", tmp); - if (tmp.has_value()) - m.ApproximateMemberCount = std::stol(*tmp); - JS_O("approximate_presence_count", tmp); - if (tmp.has_value()) - m.ApproximatePresenceCount = std::stol(*tmp); -} - -void GuildData::update_from_json(const nlohmann::json &j) { - if (j.contains("unavailable")) { - IsUnavailable = true; - return; - } - - JS_RD("name", Name); - JS_RD("icon", Icon); - JS_RD("splash", Splash); - JS_RD("discovery_splash", DiscoverySplash); - JS_RD("owner", IsOwner); - JS_RD("owner_id", OwnerID); - std::string tmp; - JS_RD("permissions", tmp); - if (tmp != "") - Permissions = std::stoull(tmp); - JS_RD("region", VoiceRegion); - JS_RD("afk_channel_id", AFKChannelID); - JS_RD("afk_timeout", AFKTimeout); - JS_RD("embed_enabled", IsEmbedEnabled); - JS_RD("embed_channel_id", EmbedChannelID); - JS_RD("verification_level", VerificationLevel); - JS_RD("default_message_notifications", DefaultMessageNotifications); - JS_RD("explicit_content_filter", ExplicitContentFilter); - JS_RD("roles", Roles); - JS_RD("emojis", Emojis); - JS_RD("features", Features); - JS_RD("mfa_level", MFALevel); - JS_RD("application_id", ApplicationID); - JS_RD("widget_enabled", IsWidgetEnabled); - JS_RD("widget_channel_id", WidgetChannelID); - JS_RD("system_channel_id", SystemChannelID); - JS_RD("system_channel_flags", SystemChannelFlags); - JS_RD("rules_channel_id", RulesChannelID); - JS_RD("joined_at", JoinedAt); - JS_RD("large", IsLarge); - JS_RD("unavailable", IsUnavailable); - JS_RD("member_count", MemberCount); - // JS_O("voice_states", m.VoiceStates); - // JS_O("members", m.Members); - JS_RD("channels", Channels); - // JS_O("presences", m.Presences); - JS_RD("max_presences", MaxPresences); - JS_RD("max_members", MaxMembers); - JS_RD("vanity_url_code", VanityURL); - JS_RD("description", Description); - JS_RD("banner", BannerHash); - JS_RD("premium_tier", PremiumTier); - JS_RD("premium_subscription_count", PremiumSubscriptionCount); - JS_RD("preferred_locale", PreferredLocale); - JS_RD("public_updates_channel_id", PublicUpdatesChannelID); - JS_RD("max_video_channel_users", MaxVideoChannelUsers); - JS_RD("approximate_member_count", ApproximateMemberCount); - JS_RD("approximate_presence_count", ApproximatePresenceCount); -} - -bool GuildData::HasFeature(const std::string &search_feature) { - if (!Features.has_value()) return false; - for (const auto &feature : *Features) - if (search_feature == feature) - return true; - return false; -} - -bool GuildData::HasIcon() const { - return Icon != ""; -} - -bool GuildData::HasAnimatedIcon() const { - return HasIcon() && Icon[0] == 'a' && Icon[1] == '_'; -} - -std::string GuildData::GetIconURL(std::string ext, std::string size) const { - return "https://cdn.discordapp.com/icons/" + std::to_string(ID) + "/" + Icon + "." + ext + "?size=" + size; -} - -std::vector GuildData::GetSortedChannels(Snowflake ignore) const { - std::vector ret; - - const auto &discord = Abaddon::Get().GetDiscordClient(); - auto channels = discord.GetChannelsInGuild(ID); - - std::unordered_map> category_to_channels; - std::map> position_to_categories; - std::map> orphan_channels; - for (const auto &channel_id : channels) { - const auto data = discord.GetChannel(channel_id); - if (!data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS)) - orphan_channels[*data->Position].push_back(*data); - else if (data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS)) - category_to_channels[*data->ParentID].push_back(*data); - else if (data->Type == ChannelType::GUILD_CATEGORY) - position_to_categories[*data->Position].push_back(*data); - } - - for (auto &[pos, channels] : orphan_channels) { - std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool { - return a.ID < b.ID; - }); - for (const auto &chan : channels) - ret.push_back(chan.ID); - } - - for (auto &[pos, categories] : position_to_categories) { - std::sort(categories.begin(), categories.end(), [&](const ChannelData &a, const ChannelData &b) -> bool { - return a.ID < b.ID; - }); - for (const auto &category : categories) { - ret.push_back(category.ID); - if (ignore == category.ID) continue; // stupid hack to save me some time - auto it = category_to_channels.find(category.ID); - if (it == category_to_channels.end()) continue; - auto &channels = it->second; - std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool { - return a.Position < b.Position; - }); - for (auto &channel : channels) { - ret.push_back(channel.ID); - } - } - } - - return ret; -} - -std::vector GuildData::FetchRoles() const { - if (!Roles.has_value()) return {}; - std::vector ret; - ret.reserve(Roles->size()); - for (const auto thing : *Roles) { - auto r = Abaddon::Get().GetDiscordClient().GetRole(thing.ID); - if (r.has_value()) - ret.push_back(*r); - } - std::sort(ret.begin(), ret.end(), [](const RoleData &a, const RoleData &b) -> bool { - return a.Position > b.Position; - }); - return ret; -} - -void from_json(const nlohmann::json &j, GuildApplicationData &m) { - JS_D("user_id", m.UserID); - JS_D("guild_id", m.GuildID); - auto tmp = j.at("application_status").get(); - if (tmp == "STARTED") - m.ApplicationStatus = GuildApplicationStatus::STARTED; - else if (tmp == "PENDING") - m.ApplicationStatus = GuildApplicationStatus::PENDING; - else if (tmp == "REJECTED") - m.ApplicationStatus = GuildApplicationStatus::REJECTED; - else if (tmp == "APPROVED") - m.ApplicationStatus = GuildApplicationStatus::APPROVED; - JS_N("rejection_reason", m.RejectionReason); - JS_N("last_seen", m.LastSeen); - JS_N("created_at", m.CreatedAt); -} diff --git a/discord/guild.hpp b/discord/guild.hpp deleted file mode 100644 index 3c3828d..0000000 --- a/discord/guild.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -#include "json.hpp" -#include "snowflake.hpp" -#include "role.hpp" -#include "channel.hpp" -#include "emoji.hpp" -#include -#include -#include - -enum class GuildApplicationStatus { - STARTED, - PENDING, - REJECTED, - APPROVED, - UNKNOWN, -}; - -struct GuildApplicationData { - Snowflake UserID; - Snowflake GuildID; - GuildApplicationStatus ApplicationStatus; - std::optional RejectionReason; - std::optional LastSeen; - std::optional CreatedAt; - - friend void from_json(const nlohmann::json &j, GuildApplicationData &m); -}; - -// a bot is apparently only supposed to receive the `id` and `unavailable` as false -// but user tokens seem to get the full objects (minus users) - -// everythings optional cuz of muh partial guild object -// anything not marked optional in https://discord.com/developers/docs/resources/guild#guild-object is guaranteed to be set when returned from Store::GetGuild -struct GuildData { - Snowflake ID; - std::string Name; - std::string Icon; // null - std::string Splash; // null - std::optional DiscoverySplash; // null - std::optional IsOwner; - std::optional OwnerID; - std::optional Permissions; - std::optional PermissionsNew; - std::optional VoiceRegion; - std::optional AFKChannelID; // null - std::optional AFKTimeout; - std::optional IsEmbedEnabled; // deprecated - std::optional EmbedChannelID; // null, deprecated - std::optional VerificationLevel; - std::optional DefaultMessageNotifications; - std::optional ExplicitContentFilter; - std::optional> Roles; // only access id - std::optional> Emojis; // only access id - std::optional> Features; - std::optional MFALevel; - std::optional ApplicationID; // null - std::optional IsWidgetEnabled; - std::optional WidgetChannelID; // null - std::optional SystemChannelID; // null - std::optional SystemChannelFlags; - std::optional RulesChannelID; // null - std::optional JoinedAt; // * - std::optional IsLarge; // * - std::optional IsUnavailable; // * - std::optional MemberCount; // * - // std::vector VoiceStates; // opt* - // std::vector Members; // opt* - incomplete anyways - std::optional> Channels; // * - // std::vector Presences; // opt* - std::optional MaxPresences; // null - std::optional MaxMembers; - std::optional VanityURL; // null - std::optional Description; // null - std::optional BannerHash; // null - std::optional PremiumTier; - std::optional PremiumSubscriptionCount; - std::optional PreferredLocale; - std::optional PublicUpdatesChannelID; // null - std::optional MaxVideoChannelUsers; - std::optional ApproximateMemberCount; - std::optional ApproximatePresenceCount; - std::optional> Threads; // only with permissions to view, id only - - // undocumented - // std::map GuildHashes; - bool IsLazy = false; - - // * - documentation says only sent in GUILD_CREATE, but these can be sent anyways in the READY event - - friend void from_json(const nlohmann::json &j, GuildData &m); - void update_from_json(const nlohmann::json &j); - - bool HasFeature(const std::string &feature); - bool HasIcon() const; - bool HasAnimatedIcon() const; - std::string GetIconURL(std::string ext = "png", std::string size = "32") const; - std::vector GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const; - std::vector FetchRoles() const; // sorted -}; diff --git a/discord/httpclient.cpp b/discord/httpclient.cpp deleted file mode 100644 index 05474df..0000000 --- a/discord/httpclient.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "httpclient.hpp" - -//#define USE_LOCAL_PROXY -HTTPClient::HTTPClient() { - m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks)); -} - -void HTTPClient::SetBase(const std::string &url) { - m_api_base = url; -} - -void HTTPClient::SetUserAgent(std::string agent) { - m_agent = agent; -} - -void HTTPClient::SetAuth(std::string auth) { - m_authorization = auth; -} - -void HTTPClient::MakeDELETE(const std::string &path, std::function cb) { - printf("DELETE %s\n", path.c_str()); - m_futures.push_back(std::async(std::launch::async, [this, path, cb] { - http::request req(http::REQUEST_DELETE, m_api_base + path); - req.set_header("Authorization", m_authorization); - req.set_user_agent(m_agent != "" ? m_agent : "Abaddon"); -#ifdef USE_LOCAL_PROXY - req.set_proxy("http://127.0.0.1:8888"); - req.set_verify_ssl(false); -#endif - - auto res = req.execute(); - - OnResponse(res, cb); - })); -} - -void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, std::function cb) { - printf("PATCH %s\n", path.c_str()); - m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] { - http::request req(http::REQUEST_PATCH, m_api_base + path); - req.set_header("Authorization", m_authorization); - req.set_header("Content-Type", "application/json"); - req.set_user_agent(m_agent != "" ? m_agent : "Abaddon"); - req.set_body(payload); -#ifdef USE_LOCAL_PROXY - req.set_proxy("http://127.0.0.1:8888"); - req.set_verify_ssl(false); -#endif - - auto res = req.execute(); - - OnResponse(res, cb); - })); -} - -void HTTPClient::MakePOST(const std::string &path, const std::string &payload, std::function cb) { - printf("POST %s\n", path.c_str()); - m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] { - http::request req(http::REQUEST_POST, m_api_base + path); - req.set_header("Authorization", m_authorization); - req.set_header("Content-Type", "application/json"); - req.set_user_agent(m_agent != "" ? m_agent : "Abaddon"); - req.set_body(payload); -#ifdef USE_LOCAL_PROXY - req.set_proxy("http://127.0.0.1:8888"); - req.set_verify_ssl(false); -#endif - - auto res = req.execute(); - - OnResponse(res, cb); - })); -} - -void HTTPClient::MakePUT(const std::string &path, const std::string &payload, std::function cb) { - printf("PUT %s\n", path.c_str()); - m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] { - http::request req(http::REQUEST_PUT, m_api_base + path); - req.set_header("Authorization", m_authorization); - if (payload != "") - req.set_header("Content-Type", "application/json"); - req.set_user_agent(m_agent != "" ? m_agent : "Abaddon"); - req.set_body(payload); -#ifdef USE_LOCAL_PROXY - req.set_proxy("http://127.0.0.1:8888"); - req.set_verify_ssl(false); -#endif - - auto res = req.execute(); - - OnResponse(res, cb); - })); -} - -void HTTPClient::MakeGET(const std::string &path, std::function cb) { - printf("GET %s\n", path.c_str()); - m_futures.push_back(std::async(std::launch::async, [this, path, cb] { - http::request req(http::REQUEST_GET, m_api_base + path); - req.set_header("Authorization", m_authorization); - req.set_header("Content-Type", "application/json"); - req.set_user_agent(m_agent != "" ? m_agent : "Abaddon"); -#ifdef USE_LOCAL_PROXY - req.set_proxy("http://127.0.0.1:8888"); - req.set_verify_ssl(false); -#endif - - auto res = req.execute(); - - OnResponse(res, cb); - })); -} - -void HTTPClient::CleanupFutures() { - for (auto it = m_futures.begin(); it != m_futures.end();) { - if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready) - it = m_futures.erase(it); - else - it++; - } -} - -void HTTPClient::RunCallbacks() { - m_mutex.lock(); - m_queue.front()(); - m_queue.pop(); - m_mutex.unlock(); -} - -void HTTPClient::OnResponse(const http::response_type &r, std::function cb) { - CleanupFutures(); - try { - m_mutex.lock(); - m_queue.push([this, r, cb] { cb(r); }); - m_dispatcher.emit(); - m_mutex.unlock(); - } catch (const std::exception &e) { - fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what()); - } -} diff --git a/discord/httpclient.hpp b/discord/httpclient.hpp deleted file mode 100644 index da8be37..0000000 --- a/discord/httpclient.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include "http.hpp" - -class HTTPClient { -public: - HTTPClient(); - - void SetBase(const std::string &url); - - void SetUserAgent(std::string agent); - void SetAuth(std::string auth); - void MakeDELETE(const std::string &path, std::function cb); - void MakeGET(const std::string &path, std::function cb); - void MakePATCH(const std::string &path, const std::string &payload, std::function cb); - void MakePOST(const std::string &path, const std::string &payload, std::function cb); - void MakePUT(const std::string &path, const std::string &payload, std::function cb); - -private: - void OnResponse(const http::response_type &r, std::function cb); - void CleanupFutures(); - - mutable std::mutex m_mutex; - Glib::Dispatcher m_dispatcher; - std::queue> m_queue; - void RunCallbacks(); - - std::vector> m_futures; - std::string m_api_base; - std::string m_authorization; - std::string m_agent; -}; diff --git a/discord/interactions.cpp b/discord/interactions.cpp deleted file mode 100644 index cc439fc..0000000 --- a/discord/interactions.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "interactions.hpp" -#include "json.hpp" -#include "abaddon.hpp" - -void from_json(const nlohmann::json &j, MessageInteractionData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_D("name", m.Name); - JS_D("user", m.User); - JS_O("member", m.Member); -} diff --git a/discord/interactions.hpp b/discord/interactions.hpp deleted file mode 100644 index c076145..0000000 --- a/discord/interactions.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include -#include -#include "member.hpp" -#include "user.hpp" -#include "snowflake.hpp" - -enum class InteractionType { - Pong = 1, // ACK a Ping - Acknowledge = 2, // DEPRECATED ACK a command without sending a message, eating the user's input - ChannelMessage = 3, // DEPRECATED respond with a message, eating the user's input - ChannelMessageWithSource = 4, // respond to an interaction with a message - DeferredChannelMessageWithSource = 5, // ACK an interaction and edit to a response later, the user sees a loading state -}; - -struct MessageInteractionData { - Snowflake ID; // id of the interaction - InteractionType Type; // the type of interaction - std::string Name; // the name of the ApplicationCommand - UserData User; // the user who invoked the interaction - // undocumented??? - std::optional Member; // the member who invoked the interaction (in a guild) - - friend void from_json(const nlohmann::json &j, MessageInteractionData &m); -}; diff --git a/discord/invite.cpp b/discord/invite.cpp deleted file mode 100644 index 63043a1..0000000 --- a/discord/invite.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "invite.hpp" - -void from_json(const nlohmann::json &j, InviteChannelData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_ON("name", m.Name); - if (j.contains("recipients") && j.at("recipients").is_null()) { - m.RecipientUsernames.emplace(); - for (const auto &x : j.at("recipients")) - m.RecipientUsernames->push_back(x.at("username").get()); - } -} - -void from_json(const nlohmann::json &j, InviteData &m) { - JS_D("code", m.Code); - JS_O("guild", m.Guild); - JS_O("channel", m.Channel); - JS_O("inviter", m.Inviter); - JS_O("target_user", m.TargetUser); - JS_O("target_user_type", m.TargetUserType); - JS_O("approximate_presence_count", m.PresenceCount); - JS_O("approximate_member_count", m.MemberCount); - JS_O("uses", m.Uses); - JS_O("max_uses", m.MaxUses); - JS_O("max_age", m.MaxAge); - JS_O("temporary", m.IsTemporary); - JS_O("created_at", m.CreatedAt); -} - -InviteChannelData::InviteChannelData(const ChannelData &c) { - ID = c.ID; - Type = c.Type; - Name = c.Name; - if (Type == ChannelType::GROUP_DM) { - RecipientUsernames.emplace(); - for (const auto &r : c.GetDMRecipients()) - RecipientUsernames->push_back(r.Username); - } -} diff --git a/discord/invite.hpp b/discord/invite.hpp deleted file mode 100644 index c4c2cf3..0000000 --- a/discord/invite.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include "json.hpp" -#include "guild.hpp" -#include - -enum class ETargetUserType { - STREAM = 1 -}; - -class InviteChannelData { -public: - InviteChannelData() = default; - InviteChannelData(const ChannelData &c); - - Snowflake ID; - ChannelType Type; - std::optional Name; - std::optional> RecipientUsernames; - // std::optional Icon; - - friend void from_json(const nlohmann::json &j, InviteChannelData &m); -}; - -class InviteData { -public: - std::string Code; - std::optional Guild; - std::optional Channel; - std::optional Inviter; - std::optional TargetUser; - std::optional TargetUserType; - std::optional PresenceCount; - std::optional MemberCount; - std::optional Uses; - std::optional MaxUses; - std::optional MaxAge; - std::optional IsTemporary; - std::optional CreatedAt; - - friend void from_json(const nlohmann::json &j, InviteData &m); -}; diff --git a/discord/json.hpp b/discord/json.hpp deleted file mode 100644 index 837080b..0000000 --- a/discord/json.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once -#include -#include -#include "util.hpp" - -namespace detail { // more or less because idk what to name this stuff -template -inline void json_direct(const ::nlohmann::json &j, const char *key, T &val) { - if constexpr (::util::is_optional::value) - val = j.at(key).get(); - else - j.at(key).get_to(val); -} - -template -inline void json_optional(const ::nlohmann::json &j, const char *key, T &val) { - if constexpr (::util::is_optional::value) { - if (j.contains(key)) - val = j.at(key).get(); - else - val = ::std::nullopt; - } else { - if (j.contains(key)) - j.at(key).get_to(val); - } -} - -template -inline void json_nullable(const ::nlohmann::json &j, const char *key, T &val) { - if constexpr (::util::is_optional::value) { - const auto &at = j.at(key); - if (!at.is_null()) - val = at.get(); - else - val = ::std::nullopt; - } else { - const auto &at = j.at(key); - if (!at.is_null()) - at.get_to(val); - } -} - -template -inline void json_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) { - if constexpr (::util::is_optional::value) { - if (j.contains(key)) { - const auto &at = j.at(key); - if (!at.is_null()) - val = at.get(); - else - val = ::std::nullopt; - } else { - val = ::std::nullopt; - } - } else { - if (j.contains(key)) { - const auto &at = j.at(key); - if (!at.is_null()) - at.get_to(val); - } - } -} - -template -inline void json_update_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) { - if constexpr (::util::is_optional::value) { - if (j.contains(key)) { - const auto &at = j.at(key); - if (!at.is_null()) - val = at.get(); - else - val = ::std::nullopt; - } - } else { - if (j.contains(key)) { - const auto &at = j.at(key); - if (!at.is_null()) - at.get_to(val); - else - val = T(); - } - } -} - -template -inline void json_update_optional_nullable_default(const ::nlohmann::json &j, const char *key, T &val, const U &fallback) { - if constexpr (::util::is_optional::value) { - if (j.contains(key)) { - const auto &at = j.at(key); - if (at.is_null()) - val = fallback; - else - val = at.get(); - } - } else { - if (j.contains(key)) { - const auto &at = j.at(key); - if (at.is_null()) - val = fallback; - else - at.get_to(val); - } - } -} -} // namespace detail - -// get a json value that is guaranteed to be present and non-null -#define JS_D(k, t) \ - do { \ - detail::json_direct(j, k, t); \ - } while (0) - -// get a json value that may not be present -#define JS_O(k, t) \ - do { \ - detail::json_optional(j, k, t); \ - } while (0) - -// get a json value that may be null -#define JS_N(k, t) \ - do { \ - detail::json_nullable(j, k, t); \ - } while (0) - -// get a json value that may not be present or may be null -#define JS_ON(k, t) \ - do { \ - detail::json_optional_nullable(j, k, t); \ - } while (0) - -// set from a json value only if it is present. null will assign default-constructed value -#define JS_RD(k, t) \ - do { \ - detail::json_update_optional_nullable(j, k, t); \ - } while (0) - -// set from a json value only if it is present. null will assign the given default -#define JS_RV(k, t, d) \ - do { \ - detail::json_update_optional_nullable_default(j, k, t, d); \ - } while (0) - -// set a json value from a std::optional only if it has a value -#define JS_IF(k, v) \ - do { \ - if (v.has_value()) \ - j[k] = *v; \ - } while (0) diff --git a/discord/member.cpp b/discord/member.cpp deleted file mode 100644 index 29c4fae..0000000 --- a/discord/member.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "member.hpp" -#include "abaddon.hpp" - -void from_json(const nlohmann::json &j, GuildMember &m) { - JS_O("user", m.User); - JS_ON("nick", m.Nickname); - JS_D("roles", m.Roles); - JS_D("joined_at", m.JoinedAt); - JS_ON("premium_since", m.PremiumSince); - JS_D("deaf", m.IsDeafened); - JS_D("mute", m.IsMuted); - JS_O("user_id", m.UserID); - JS_ON("avatar", m.Avatar); - JS_O("pending", m.IsPending); -} - -std::vector GuildMember::GetSortedRoles() const { - std::vector roles; - for (const auto role_id : Roles) { - const auto role = Abaddon::Get().GetDiscordClient().GetRole(role_id); - if (!role.has_value()) continue; - roles.push_back(std::move(*role)); - } - - std::sort(roles.begin(), roles.end(), [](const RoleData &a, const RoleData &b) { - return a.Position > b.Position; - }); - - return roles; -} - -void GuildMember::update_from_json(const nlohmann::json &j) { - JS_RD("roles", Roles); - JS_RD("user", User); - JS_RD("nick", Nickname); - JS_RD("joined_at", JoinedAt); - JS_RD("premium_since", PremiumSince); - JS_RD("avatar", Avatar); - JS_RD("pending", IsPending); -} diff --git a/discord/member.hpp b/discord/member.hpp deleted file mode 100644 index e17da05..0000000 --- a/discord/member.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "snowflake.hpp" -#include "json.hpp" -#include "user.hpp" -#include "role.hpp" -#include -#include - -struct GuildMember { - std::optional User; // only reliable to access id. only opt in MESSAGE_* - std::string Nickname; - std::vector Roles; - std::string JoinedAt; - std::optional PremiumSince; // null - bool IsDeafened; - bool IsMuted; - std::optional UserID; // present in merged_members - std::optional IsPending; // this uses `pending` not `is_pending` - - // undocuemtned moment !!!1 - std::optional Avatar; - - std::vector GetSortedRoles() const; - - void update_from_json(const nlohmann::json &j); - friend void from_json(const nlohmann::json &j, GuildMember &m); -}; diff --git a/discord/message.cpp b/discord/message.cpp deleted file mode 100644 index 70c557d..0000000 --- a/discord/message.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include "message.hpp" - -void to_json(nlohmann::json &j, const EmbedFooterData &m) { - j["text"] = m.Text; - JS_IF("icon_url", m.IconURL); - JS_IF("proxy_icon_url", m.ProxyIconURL); -} - -void from_json(const nlohmann::json &j, EmbedFooterData &m) { - JS_D("text", m.Text); - JS_O("icon_url", m.IconURL); - JS_O("proxy_icon_url", m.ProxyIconURL); -} - -void to_json(nlohmann::json &j, const EmbedImageData &m) { - JS_IF("url", m.URL); - JS_IF("proxy_url", m.ProxyURL); - JS_IF("height", m.Height); - JS_IF("width", m.Width); -} - -void from_json(const nlohmann::json &j, EmbedImageData &m) { - JS_O("url", m.URL); - JS_O("proxy_url", m.ProxyURL); - JS_O("height", m.Height); - JS_O("width", m.Width); -} - -void to_json(nlohmann::json &j, const EmbedThumbnailData &m) { - JS_IF("url", m.URL); - JS_IF("proxy_url", m.ProxyURL); - JS_IF("height", m.Height); - JS_IF("width", m.Width); -} - -void from_json(const nlohmann::json &j, EmbedThumbnailData &m) { - JS_O("url", m.URL); - JS_O("proxy_url", m.ProxyURL); - JS_O("height", m.Height); - JS_O("width", m.Width); -} - -void to_json(nlohmann::json &j, const EmbedVideoData &m) { - JS_IF("url", m.URL); - JS_IF("height", m.Height); - JS_IF("width", m.Width); -} - -void from_json(const nlohmann::json &j, EmbedVideoData &m) { - JS_O("url", m.URL); - JS_O("height", m.Height); - JS_O("width", m.Width); -} - -void to_json(nlohmann::json &j, const EmbedProviderData &m) { - JS_IF("name", m.Name); - JS_IF("url", m.URL); -} - -void from_json(const nlohmann::json &j, EmbedProviderData &m) { - JS_O("name", m.Name); - JS_ON("url", m.URL); -} - -void to_json(nlohmann::json &j, const EmbedAuthorData &m) { - JS_IF("name", m.Name); - JS_IF("url", m.URL); - JS_IF("icon_url", m.IconURL); - JS_IF("proxy_icon_url", m.ProxyIconURL); -} - -void from_json(const nlohmann::json &j, EmbedAuthorData &m) { - JS_O("name", m.Name); - JS_O("url", m.URL); - JS_O("icon_url", m.IconURL); - JS_O("proxy_icon_url", m.ProxyIconURL); -} - -void to_json(nlohmann::json &j, const EmbedFieldData &m) { - j["name"] = m.Name; - j["value"] = m.Value; - JS_IF("inline", m.Inline); -} - -void from_json(const nlohmann::json &j, EmbedFieldData &m) { - JS_D("name", m.Name); - JS_D("value", m.Value); - JS_O("inline", m.Inline); -} - -void to_json(nlohmann::json &j, const EmbedData &m) { - JS_IF("title", m.Title); - JS_IF("type", m.Type); - JS_IF("description", m.Description); - JS_IF("url", m.URL); - JS_IF("timestamp", m.Timestamp); - JS_IF("color", m.Color); - JS_IF("footer", m.Footer); - JS_IF("image", m.Image); - JS_IF("thumbnail", m.Thumbnail); - JS_IF("video", m.Video); - JS_IF("provider", m.Provider); - JS_IF("author", m.Author); - JS_IF("fields", m.Fields); -} - -void from_json(const nlohmann::json &j, EmbedData &m) { - JS_O("title", m.Title); - JS_O("type", m.Type); - JS_O("description", m.Description); - JS_O("url", m.URL); - JS_O("timestamp", m.Timestamp); - JS_O("color", m.Color); - JS_O("footer", m.Footer); - JS_O("image", m.Image); - JS_O("thumbnail", m.Thumbnail); - JS_O("video", m.Video); - JS_O("provider", m.Provider); - JS_O("author", m.Author); - JS_O("fields", m.Fields); -} - -void to_json(nlohmann::json &j, const AttachmentData &m) { - j["id"] = m.ID; - j["filename"] = m.Filename; - j["size"] = m.Bytes; - j["url"] = m.URL; - j["proxy_url"] = m.ProxyURL; - JS_IF("height", m.Height); - JS_IF("width", m.Width); -} - -void from_json(const nlohmann::json &j, AttachmentData &m) { - JS_D("id", m.ID); - JS_D("filename", m.Filename); - JS_D("size", m.Bytes); - JS_D("url", m.URL); - JS_D("proxy_url", m.ProxyURL); - JS_ON("height", m.Height); - JS_ON("width", m.Width); -} - -void from_json(const nlohmann::json &j, MessageReferenceData &m) { - JS_O("message_id", m.MessageID); - JS_O("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); -} - -void to_json(nlohmann::json &j, const MessageReferenceData &m) { - JS_IF("message_id", m.MessageID); - JS_IF("channel_id", m.ChannelID); - JS_IF("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, ReactionData &m) { - JS_D("count", m.Count); - JS_D("me", m.HasReactedWith); - JS_D("emoji", m.Emoji); -} - -void to_json(nlohmann::json &j, const ReactionData &m) { - j["count"] = m.Count; - j["me"] = m.HasReactedWith; - j["emoji"] = m.Emoji; -} - -void from_json(const nlohmann::json &j, MessageApplicationData &m) { - JS_D("id", m.ID); - JS_O("cover_image", m.CoverImage); - JS_D("description", m.Description); - JS_N("icon", m.Icon); - JS_D("name", m.Name); -} - -void to_json(nlohmann::json &j, const MessageApplicationData &m) { - j["id"] = m.ID; - JS_IF("cover_image", m.CoverImage); - j["description"] = m.Description; - if (m.Icon == "") - j["icon"] = nullptr; - else - j["icon"] = m.Icon; - j["name"] = m.Name; -} - -void from_json(const nlohmann::json &j, Message &m) { - JS_D("id", m.ID); - JS_D("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); - JS_D("author", m.Author); - JS_O("member", m.Member); - JS_D("content", m.Content); - JS_D("timestamp", m.Timestamp); - JS_N("edited_timestamp", m.EditedTimestamp); - if (!j.at("edited_timestamp").is_null()) - m.SetEdited(); - JS_D("tts", m.IsTTS); - JS_D("mention_everyone", m.DoesMentionEveryone); - JS_D("mentions", m.Mentions); - // JS_D("mention_roles", m.MentionRoles); - // JS_O("mention_channels", m.MentionChannels); - JS_D("attachments", m.Attachments); - JS_D("embeds", m.Embeds); - JS_O("reactions", m.Reactions); - JS_O("nonce", m.Nonce); - JS_D("pinned", m.IsPinned); - JS_O("webhook_id", m.WebhookID); - JS_D("type", m.Type); - // JS_O("activity", m.Activity); - JS_O("application", m.Application); - JS_O("message_reference", m.MessageReference); - JS_O("flags", m.Flags); - JS_O("stickers", m.Stickers); - if (j.contains("referenced_message")) { - if (!j.at("referenced_message").is_null()) { - m.ReferencedMessage = std::make_shared(j.at("referenced_message").get()); - } else - m.ReferencedMessage = nullptr; - } - JS_O("interaction", m.Interaction); - JS_O("sticker_items", m.StickerItems); -} - -void Message::from_json_edited(const nlohmann::json &j) { - JS_D("id", ID); - JS_D("channel_id", ChannelID); - JS_O("guild_id", GuildID); - JS_O("author", Author); - JS_O("member", Member); - JS_O("content", Content); - JS_O("timestamp", Timestamp); - JS_ON("edited_timestamp", EditedTimestamp); - if (EditedTimestamp.size() > 0) - SetEdited(); - JS_O("tts", IsTTS); - JS_O("mention_everyone", DoesMentionEveryone); - JS_O("mentions", Mentions); - JS_O("embeds", Embeds); - JS_O("nonce", Nonce); - JS_O("pinned", IsPinned); - JS_O("webhook_id", WebhookID); - JS_O("type", Type); - JS_O("application", Application); - JS_O("message_reference", MessageReference); - JS_O("flags", Flags); - JS_O("stickers", Stickers); - JS_O("interaction", Interaction); - JS_O("sticker_items", StickerItems); -} - -void Message::SetDeleted() { - m_deleted = true; -} - -void Message::SetEdited() { - m_edited = true; -} - -bool Message::IsDeleted() const { - return m_deleted; -} - -bool Message::IsEdited() const { - return m_edited; -} diff --git a/discord/message.hpp b/discord/message.hpp deleted file mode 100644 index 56f4c0f..0000000 --- a/discord/message.hpp +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once -#include -#include -#include "snowflake.hpp" -#include "json.hpp" -#include "user.hpp" -#include "sticker.hpp" -#include "emoji.hpp" -#include "member.hpp" -#include "interactions.hpp" - -enum class MessageType { - DEFAULT = 0, // yep - RECIPIENT_ADD = 1, // yep - RECIPIENT_REMOVE = 2, // yep - CALL = 3, // yep (sorta) - CHANNEL_NAME_CHANGE = 4, // yep - CHANNEL_ICON_CHANGE = 5, // yep - CHANNEL_PINNED_MESSAGE = 6, // yep - GUILD_MEMBER_JOIN = 7, // yep - USER_PREMIUM_GUILD_SUBSCRIPTION = 8, // yep - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, // yep - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, // yep - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, // yep - CHANNEL_FOLLOW_ADD = 12, // yep - GUILD_DISCOVERY_DISQUALIFIED = 14, // yep - GUILD_DISCOVERY_REQUALIFIED = 15, // yep - GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16, // yep - GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17, // yep - THREAD_CREATED = 18, // yep - INLINE_REPLY = 19, // yep - APPLICATION_COMMAND = 20, // yep - THREAD_STARTER_MESSAGE = 21, // nope -}; - -enum class MessageFlags { - NONE = 0, - CROSSPOSTED = 1 << 0, // this message has been published to subscribed channels (via Channel Following) - IS_CROSSPOST = 1 << 1, // this message originated from a message in another channel (via Channel Following) - SUPPRESS_EMBEDS = 1 << 2, // do not include any embeds when serializing this message - SOURCE_MESSAGE_DELETE = 1 << 3, // the source message for this crosspost has been deleted (via Channel Following) - URGENT = 1 << 4, // this message came from the urgent message system - HAS_THREAD = 1 << 5, // this message has an associated thread, with the same id as the message - EPHEMERAL = 1 << 6, // this message is only visible to the user who invoked the Interaction - LOADING = 1 << 7, // this message is an Interaction Response and the bot is "thinking" -}; - -struct EmbedFooterData { - std::string Text; - std::optional IconURL; - std::optional ProxyIconURL; - - friend void to_json(nlohmann::json &j, const EmbedFooterData &m); - friend void from_json(const nlohmann::json &j, EmbedFooterData &m); -}; - -struct EmbedImageData { - std::optional URL; - std::optional ProxyURL; - std::optional Height; - std::optional Width; - - friend void to_json(nlohmann::json &j, const EmbedImageData &m); - friend void from_json(const nlohmann::json &j, EmbedImageData &m); -}; - -struct EmbedThumbnailData { - std::optional URL; - std::optional ProxyURL; - std::optional Height; - std::optional Width; - - friend void to_json(nlohmann::json &j, const EmbedThumbnailData &m); - friend void from_json(const nlohmann::json &j, EmbedThumbnailData &m); -}; - -struct EmbedVideoData { - std::optional URL; - std::optional Height; - std::optional Width; - - friend void to_json(nlohmann::json &j, const EmbedVideoData &m); - friend void from_json(const nlohmann::json &j, EmbedVideoData &m); -}; - -struct EmbedProviderData { - std::optional Name; - std::optional URL; // null - - friend void to_json(nlohmann::json &j, const EmbedProviderData &m); - friend void from_json(const nlohmann::json &j, EmbedProviderData &m); -}; - -struct EmbedAuthorData { - std::optional Name; - std::optional URL; - std::optional IconURL; - std::optional ProxyIconURL; - - friend void to_json(nlohmann::json &j, const EmbedAuthorData &m); - friend void from_json(const nlohmann::json &j, EmbedAuthorData &m); -}; - -struct EmbedFieldData { - std::string Name; - std::string Value; - std::optional Inline; - - friend void to_json(nlohmann::json &j, const EmbedFieldData &m); - friend void from_json(const nlohmann::json &j, EmbedFieldData &m); -}; - -struct EmbedData { - std::optional Title; - std::optional Type; - std::optional Description; - std::optional URL; - std::optional Timestamp; - std::optional Color; - std::optional Footer; - std::optional Image; - std::optional Thumbnail; - std::optional Video; - std::optional Provider; - std::optional Author; - std::optional> Fields; - - friend void to_json(nlohmann::json &j, const EmbedData &m); - friend void from_json(const nlohmann::json &j, EmbedData &m); -}; - -struct AttachmentData { - Snowflake ID; - std::string Filename; - int Bytes; - std::string URL; - std::string ProxyURL; - std::optional Height; // null - std::optional Width; // null - - friend void to_json(nlohmann::json &j, const AttachmentData &m); - friend void from_json(const nlohmann::json &j, AttachmentData &m); -}; - -struct MessageReferenceData { - std::optional MessageID; - std::optional ChannelID; - std::optional GuildID; - - friend void from_json(const nlohmann::json &j, MessageReferenceData &m); - friend void to_json(nlohmann::json &j, const MessageReferenceData &m); -}; - -struct ReactionData { - int Count; - bool HasReactedWith; - EmojiData Emoji; - - friend void from_json(const nlohmann::json &j, ReactionData &m); - friend void to_json(nlohmann::json &j, const ReactionData &m); -}; - -struct MessageApplicationData { - Snowflake ID; - std::optional CoverImage; - std::string Description; - std::string Icon; // null - std::string Name; - - friend void from_json(const nlohmann::json &j, MessageApplicationData &m); - friend void to_json(nlohmann::json &j, const MessageApplicationData &m); -}; - -struct Message { - Snowflake ID; - Snowflake ChannelID; - std::optional GuildID; - UserData Author; - std::optional Member; - std::string Content; - std::string Timestamp; - std::string EditedTimestamp; // null - bool IsTTS; - bool DoesMentionEveryone; - std::vector Mentions; // full user accessible - // std::vector MentionRoles; - // std::optional> MentionChannels; - std::vector Attachments; - std::vector Embeds; - std::optional> Reactions; - std::optional Nonce; - bool IsPinned; - std::optional WebhookID; - MessageType Type; - // std::optional ActivityData; - std::optional Application; - std::optional MessageReference; - std::optional Flags = MessageFlags::NONE; - std::optional> Stickers; - std::optional> ReferencedMessage; // has_value && null means deleted - std::optional Interaction; - std::optional> StickerItems; - - friend void from_json(const nlohmann::json &j, Message &m); - void from_json_edited(const nlohmann::json &j); // for MESSAGE_UPDATE - - // custom fields to track changes - bool IsPending = false; // for user-sent messages yet to be received in a MESSAGE_CREATE - - void SetDeleted(); - void SetEdited(); - bool IsDeleted() const; - bool IsEdited() const; - -private: - bool m_deleted = false; - bool m_edited = false; -}; diff --git a/discord/objects.cpp b/discord/objects.cpp deleted file mode 100644 index c6de2ce..0000000 --- a/discord/objects.cpp +++ /dev/null @@ -1,534 +0,0 @@ -#include "objects.hpp" - -void from_json(const nlohmann::json &j, GatewayMessage &m) { - JS_D("op", m.Opcode); - m.Data = j.at("d"); - - JS_ON("t", m.Type); - JS_ON("s", m.Sequence); -} - -void from_json(const nlohmann::json &j, HelloMessageData &m) { - JS_D("heartbeat_interval", m.HeartbeatInterval); -} - -void from_json(const nlohmann::json &j, MessageDeleteData &m) { - JS_D("id", m.ID); - JS_D("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, MessageDeleteBulkData &m) { - JS_D("ids", m.IDs); - JS_D("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::GroupItem &m) { - m.Type = "group"; - JS_D("id", m.ID); - JS_D("count", m.Count); -} - -GuildMember GuildMemberListUpdateMessage::MemberItem::GetAsMemberData() const { - return m_member_data; -} - -void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::MemberItem &m) { - m.Type = "member"; - JS_D("user", m.User); - JS_D("roles", m.Roles); - JS_D("mute", m.IsMuted); - JS_D("joined_at", m.JoinedAt); - JS_D("deaf", m.IsDefeaned); - JS_N("hoisted_role", m.HoistedRole); - JS_ON("premium_since", m.PremiumSince); - JS_ON("nick", m.Nickname); - JS_ON("presence", m.Presence); - m.m_member_data = j; -} - -void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::OpObject &m) { - JS_D("op", m.Op); - if (m.Op == "SYNC") { - m.Items.emplace(); - JS_D("range", m.Range); - for (const auto &ij : j.at("items")) { - if (ij.contains("group")) - m.Items->push_back(std::make_unique(ij.at("group"))); - else if (ij.contains("member")) - m.Items->push_back(std::make_unique(ij.at("member"))); - } - } else if (m.Op == "UPDATE") { - JS_D("index", m.Index); - const auto &ij = j.at("item"); - if (ij.contains("member")) - m.OpItem = std::make_unique(ij.at("member")); - } -} - -void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m) { - JS_D("online_count", m.OnlineCount); - JS_D("member_count", m.MemberCount); - JS_D("id", m.ListIDHash); - JS_D("guild_id", m.GuildID); - JS_D("groups", m.Groups); - JS_D("ops", m.Ops); -} - -void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) { - j["op"] = GatewayOp::LazyLoadRequest; - j["d"] = nlohmann::json::object(); - j["d"]["guild_id"] = m.GuildID; - if (m.Channels.has_value()) { - j["d"]["channels"] = nlohmann::json::object(); - for (const auto &[key, chans] : *m.Channels) - j["d"]["channels"][std::to_string(key)] = chans; - } - if (m.ShouldGetTyping) - j["d"]["typing"] = *m.ShouldGetTyping; - if (m.ShouldGetActivities) - j["d"]["activities"] = *m.ShouldGetActivities; - if (m.ShouldGetThreads) - j["d"]["threads"] = *m.ShouldGetThreads; - if (m.Members.has_value()) - j["d"]["members"] = *m.Members; - if (m.ThreadIDs.has_value()) - j["d"]["thread_member_lists"] = *m.ThreadIDs; -} - -void to_json(nlohmann::json &j, const UpdateStatusMessage &m) { - j["op"] = GatewayOp::UpdateStatus; - j["d"] = nlohmann::json::object(); - j["d"]["since"] = m.Since; - j["d"]["activities"] = m.Activities; - j["d"]["afk"] = m.IsAFK; - switch (m.Status) { - case PresenceStatus::Online: - j["d"]["status"] = "online"; - break; - case PresenceStatus::Offline: - j["d"]["status"] = "invisible"; - break; - case PresenceStatus::Idle: - j["d"]["status"] = "idle"; - break; - case PresenceStatus::DND: - j["d"]["status"] = "dnd"; - break; - } -} - -void from_json(const nlohmann::json &j, ReadyEventData &m) { - JS_D("v", m.GatewayVersion); - JS_D("user", m.SelfUser); - JS_D("guilds", m.Guilds); - JS_D("session_id", m.SessionID); - JS_O("analytics_token", m.AnalyticsToken); - JS_O("friend_suggestion_count", m.FriendSuggestionCount); - JS_D("user_settings", m.Settings); - JS_D("private_channels", m.PrivateChannels); - JS_O("users", m.Users); - JS_ON("merged_members", m.MergedMembers); - JS_O("relationships", m.Relationships); - JS_O("guild_join_requests", m.GuildJoinRequests); -} - -void from_json(const nlohmann::json &j, MergedPresence &m) { - JS_D("user_id", m.UserID); - JS_O("last_modified", m.LastModified); - m.Presence = j; -} - -void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m) { - JS_D("guilds", m.Guilds); - JS_D("friends", m.Friends); -} - -void from_json(const nlohmann::json &j, ReadySupplementalData &m) { - JS_D("merged_presences", m.MergedPresences); -} - -void to_json(nlohmann::json &j, const IdentifyProperties &m) { - j["os"] = m.OS; - j["browser"] = m.Browser; - j["device"] = m.Device; - j["system_locale"] = m.SystemLocale; - j["browser_user_agent"] = m.BrowserUserAgent; - j["browser_version"] = m.BrowserVersion; - j["os_version"] = m.OSVersion; - j["referrer"] = m.Referrer; - j["referring_domain"] = m.ReferringDomain; - j["referrer_current"] = m.ReferrerCurrent; - j["referring_domain_current"] = m.ReferringDomainCurrent; - j["release_channel"] = m.ReleaseChannel; - j["client_build_number"] = m.ClientBuildNumber; - if (m.ClientEventSource == "") - j["client_event_source"] = nullptr; - else - j["client_event_source"] = m.ClientEventSource; -} - -void to_json(nlohmann::json &j, const ClientStateProperties &m) { - j["guild_hashes"] = m.GuildHashes; - j["highest_last_message_id"] = m.HighestLastMessageID; - j["read_state_version"] = m.ReadStateVersion; - j["user_guild_settings_version"] = m.UserGuildSettingsVersion; -} - -void to_json(nlohmann::json &j, const IdentifyMessage &m) { - j["op"] = GatewayOp::Identify; - j["d"] = nlohmann::json::object(); - j["d"]["token"] = m.Token; - j["d"]["capabilities"] = m.Capabilities; - j["d"]["properties"] = m.Properties; - j["d"]["presence"] = m.Presence; - j["d"]["compress"] = m.DoesSupportCompression; - j["d"]["client_state"] = m.ClientState; -} - -void to_json(nlohmann::json &j, const HeartbeatMessage &m) { - j["op"] = GatewayOp::Heartbeat; - if (m.Sequence == -1) - j["d"] = nullptr; - else - j["d"] = m.Sequence; -} - -void to_json(nlohmann::json &j, const CreateMessageObject &m) { - j["content"] = m.Content; - JS_IF("message_reference", m.MessageReference); - JS_IF("nonce", m.Nonce); -} - -void to_json(nlohmann::json &j, const MessageEditObject &m) { - if (m.Content.size() > 0) - j["content"] = m.Content; - - // todo EmbedData to_json - // if (m.Embeds.size() > 0) - // j["embeds"] = m.Embeds; - - if (m.Flags != -1) - j["flags"] = m.Flags; -} - -void from_json(const nlohmann::json &j, GuildMemberUpdateMessage &m) { - JS_D("guild_id", m.GuildID); - JS_D("roles", m.Roles); - JS_D("user", m.User); - JS_ON("nick", m.Nick); - JS_D("joined_at", m.JoinedAt); -} - -void from_json(const nlohmann::json &j, ClientStatusData &m) { - JS_O("desktop", m.Desktop); - JS_O("mobile", m.Mobile); - JS_O("web", m.Web); -} - -void from_json(const nlohmann::json &j, PresenceUpdateMessage &m) { - m.User = j.at("user"); - JS_O("guild_id", m.GuildID); - JS_D("status", m.StatusMessage); - JS_D("activities", m.Activities); - JS_D("client_status", m.ClientStatus); -} - -void to_json(nlohmann::json &j, const CreateDMObject &m) { - std::vector conv; - for (const auto &id : m.Recipients) - conv.push_back(std::to_string(id)); - j["recipients"] = conv; -} - -void to_json(nlohmann::json &j, const ResumeMessage &m) { - j["op"] = GatewayOp::Resume; - j["d"] = nlohmann::json::object(); - j["d"]["token"] = m.Token; - j["d"]["session_id"] = m.SessionID; - j["d"]["seq"] = m.Sequence; -} - -void from_json(const nlohmann::json &j, GuildRoleUpdateObject &m) { - JS_D("guild_id", m.GuildID); - JS_D("role", m.Role); -} - -void from_json(const nlohmann::json &j, GuildRoleCreateObject &m) { - JS_D("guild_id", m.GuildID); - JS_D("role", m.Role); -} - -void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m) { - JS_D("guild_id", m.GuildID); - JS_D("role_id", m.RoleID); -} - -void from_json(const nlohmann::json &j, MessageReactionAddObject &m) { - JS_D("user_id", m.UserID); - JS_D("channel_id", m.ChannelID); - JS_D("message_id", m.MessageID); - JS_O("guild_id", m.GuildID); - JS_O("member", m.Member); - JS_D("emoji", m.Emoji); -} - -void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m) { - JS_D("user_id", m.UserID); - JS_D("channel_id", m.ChannelID); - JS_D("message_id", m.MessageID); - JS_O("guild_id", m.GuildID); - JS_D("emoji", m.Emoji); -} - -void from_json(const nlohmann::json &j, ChannelRecipientAdd &m) { - JS_D("user", m.User); - JS_D("channel_id", m.ChannelID); -} - -void from_json(const nlohmann::json &j, ChannelRecipientRemove &m) { - JS_D("user", m.User); - JS_D("channel_id", m.ChannelID); -} - -void from_json(const nlohmann::json &j, TypingStartObject &m) { - JS_D("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); - JS_D("user_id", m.UserID); - JS_D("timestamp", m.Timestamp); - JS_O("member", m.Member); -} - -void to_json(nlohmann::json &j, const ModifyGuildObject &m) { - JS_IF("name", m.Name); - JS_IF("icon", m.IconData); -} - -void from_json(const nlohmann::json &j, GuildBanRemoveObject &m) { - JS_D("guild_id", m.GuildID); - JS_D("user", m.User); -} - -void from_json(const nlohmann::json &j, GuildBanAddObject &m) { - JS_D("guild_id", m.GuildID); - JS_D("user", m.User); -} - -void from_json(const nlohmann::json &j, InviteCreateObject &m) { - JS_D("channel_id", m.ChannelID); - JS_D("code", m.Code); - JS_D("created_at", m.CreatedAt); - JS_O("guild_id", m.GuildID); - JS_O("inviter", m.Inviter); - JS_D("max_age", m.MaxAge); - JS_D("max_uses", m.MaxUses); - JS_O("target_user", m.TargetUser); - JS_O("target_user_type", m.TargetUserType); - JS_D("temporary", m.IsTemporary); - JS_D("uses", m.Uses); -} - -void from_json(const nlohmann::json &j, InviteDeleteObject &m) { - JS_D("channel_id", m.ChannelID); - JS_O("guild_id", m.GuildID); - JS_D("code", m.Code); -} - -void from_json(const nlohmann::json &j, ConnectionData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_D("name", m.Name); - JS_D("verified", m.IsVerified); -} - -void from_json(const nlohmann::json &j, MutualGuildData &m) { - JS_D("id", m.ID); - JS_ON("nick", m.Nick); -} - -void from_json(const nlohmann::json &j, UserProfileData &m) { - JS_D("connected_accounts", m.ConnectedAccounts); - JS_D("mutual_guilds", m.MutualGuilds); - JS_ON("premium_guild_since", m.PremiumGuildSince); - JS_ON("premium_since", m.PremiumSince); - JS_D("user", m.User); -} - -void from_json(const nlohmann::json &j, UserNoteObject &m) { - JS_ON("note", m.Note); - JS_ON("note_user_id", m.NoteUserID); - JS_ON("user_id", m.UserID); -} - -void to_json(nlohmann::json &j, const UserSetNoteObject &m) { - j["note"] = m.Note; -} - -void from_json(const nlohmann::json &j, UserNoteUpdateMessage &m) { - JS_D("note", m.Note); - JS_D("id", m.ID); -} - -void from_json(const nlohmann::json &j, RelationshipsData &m) { - j.get_to(m.Users); -} - -void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m) { - JS_IF("roles", m.Roles); -} - -void to_json(nlohmann::json &j, const ModifyGuildRoleObject &m) { - JS_IF("name", m.Name); - JS_IF("color", m.Color); - JS_IF("hoist", m.IsHoisted); - JS_IF("mentionable", m.Mentionable); - if (m.Permissions.has_value()) - j["permissions"] = std::to_string(static_cast(*m.Permissions)); -} - -void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject::PositionParam &m) { - j["id"] = m.ID; - JS_IF("position", m.Position); -} - -void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject &m) { - j = m.Positions; -} - -void from_json(const nlohmann::json &j, GuildEmojisUpdateObject &m) { - JS_D("guild_id", m.GuildID); -} - -void to_json(nlohmann::json &j, const ModifyGuildEmojiObject &m) { - JS_IF("name", m.Name); -} - -void from_json(const nlohmann::json &j, GuildJoinRequestCreateData &m) { - auto tmp = j.at("status").get(); - if (tmp == "STARTED") - m.Status = GuildApplicationStatus::STARTED; - else if (tmp == "PENDING") - m.Status = GuildApplicationStatus::PENDING; - else if (tmp == "REJECTED") - m.Status = GuildApplicationStatus::REJECTED; - else if (tmp == "APPROVED") - m.Status = GuildApplicationStatus::APPROVED; - JS_D("request", m.Request); - JS_D("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, GuildJoinRequestDeleteData &m) { - JS_D("user_id", m.UserID); - JS_D("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, VerificationFieldObject &m) { - JS_D("field_type", m.Type); - JS_D("label", m.Label); - JS_D("required", m.Required); - JS_D("values", m.Values); -} - -void from_json(const nlohmann::json &j, VerificationGateInfoObject &m) { - JS_O("description", m.Description); - JS_O("form_fields", m.VerificationFields); - JS_O("version", m.Version); - JS_O("enabled", m.Enabled); -} - -void to_json(nlohmann::json &j, const VerificationFieldObject &m) { - j["field_type"] = m.Type; - j["label"] = m.Label; - j["required"] = m.Required; - j["values"] = m.Values; - JS_IF("response", m.Response); -} - -void to_json(nlohmann::json &j, const VerificationGateInfoObject &m) { - JS_IF("description", m.Description); - JS_IF("form_fields", m.VerificationFields); - JS_IF("version", m.Version); - JS_IF("enabled", m.Enabled); -} - -void from_json(const nlohmann::json &j, RateLimitedResponse &m) { - JS_D("code", m.Code); - JS_D("global", m.Global); - JS_O("message", m.Message); - JS_D("retry_after", m.RetryAfter); -} - -void from_json(const nlohmann::json &j, RelationshipRemoveData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); -} - -void from_json(const nlohmann::json &j, RelationshipAddData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_D("user", m.User); -} - -void to_json(nlohmann::json &j, const FriendRequestObject &m) { - j["username"] = m.Username; - j["discriminator"] = m.Discriminator; -} - -void to_json(nlohmann::json &j, const PutRelationshipObject &m) { - JS_IF("type", m.Type); -} - -void from_json(const nlohmann::json &j, ThreadCreateData &m) { - j.get_to(m.Channel); -} - -void from_json(const nlohmann::json &j, ThreadDeleteData &m) { - JS_D("id", m.ID); - JS_D("guild_id", m.GuildID); - JS_D("parent_id", m.ParentID); - JS_D("type", m.Type); -} - -void from_json(const nlohmann::json &j, ThreadListSyncData &m) { - JS_D("threads", m.Threads); - JS_D("guild_id", m.GuildID); -} - -void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m) { - JS_D("id", m.ID); - JS_D("guild_id", m.GuildID); - JS_D("member_count", m.MemberCount); - JS_O("added_members", m.AddedMembers); - JS_O("removed_member_ids", m.RemovedMemberIDs); -} - -void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m) { - JS_D("threads", m.Threads); - JS_D("members", m.Members); - JS_D("has_more", m.HasMore); -} - -void from_json(const nlohmann::json &j, ThreadMemberUpdateData &m) { - m.Member = j; -} - -void from_json(const nlohmann::json &j, ThreadUpdateData &m) { - m.Thread = j; -} - -void from_json(const nlohmann::json &j, ThreadMemberListUpdateData::UserEntry &m) { - JS_D("user_id", m.UserID); - JS_D("member", m.Member); -} - -void from_json(const nlohmann::json &j, ThreadMemberListUpdateData &m) { - JS_D("thread_id", m.ThreadID); - JS_D("guild_id", m.GuildID); - JS_D("members", m.Members); -} - -void to_json(nlohmann::json &j, const ModifyChannelObject &m) { - JS_IF("archived", m.Archived); - JS_IF("locked", m.Locked); -} diff --git a/discord/objects.hpp b/discord/objects.hpp deleted file mode 100644 index 7084efb..0000000 --- a/discord/objects.hpp +++ /dev/null @@ -1,747 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "snowflake.hpp" -#include "user.hpp" -#include "role.hpp" -#include "member.hpp" -#include "channel.hpp" -#include "guild.hpp" -#include "usersettings.hpp" -#include "message.hpp" -#include "invite.hpp" -#include "permissions.hpp" -#include "emoji.hpp" -#include "activity.hpp" -#include "sticker.hpp" -#include "ban.hpp" -#include "auditlog.hpp" -#include "relationship.hpp" -#include "errors.hpp" - -// most stuff below should just be objects that get processed and thrown away immediately - -enum class GatewayOp : int { - Event = 0, - Heartbeat = 1, - Identify = 2, - UpdateStatus = 3, - Resume = 6, - Reconnect = 7, - InvalidSession = 9, - Hello = 10, - HeartbeatAck = 11, - LazyLoadRequest = 14, -}; - -enum class GatewayEvent : int { - READY, - MESSAGE_CREATE, - MESSAGE_DELETE, - MESSAGE_UPDATE, - GUILD_MEMBER_LIST_UPDATE, - GUILD_CREATE, - GUILD_DELETE, - MESSAGE_DELETE_BULK, - GUILD_MEMBER_UPDATE, - PRESENCE_UPDATE, - CHANNEL_DELETE, - CHANNEL_UPDATE, - CHANNEL_CREATE, - GUILD_UPDATE, - GUILD_ROLE_UPDATE, - GUILD_ROLE_CREATE, - GUILD_ROLE_DELETE, - MESSAGE_REACTION_ADD, - MESSAGE_REACTION_REMOVE, - CHANNEL_RECIPIENT_ADD, - CHANNEL_RECIPIENT_REMOVE, - TYPING_START, - GUILD_BAN_REMOVE, - GUILD_BAN_ADD, - INVITE_CREATE, - INVITE_DELETE, - USER_NOTE_UPDATE, - READY_SUPPLEMENTAL, - GUILD_EMOJIS_UPDATE, - GUILD_JOIN_REQUEST_CREATE, - GUILD_JOIN_REQUEST_UPDATE, - GUILD_JOIN_REQUEST_DELETE, - RELATIONSHIP_REMOVE, - RELATIONSHIP_ADD, - THREAD_CREATE, - THREAD_UPDATE, - THREAD_DELETE, - THREAD_LIST_SYNC, - THREAD_MEMBER_UPDATE, - THREAD_MEMBERS_UPDATE, - THREAD_MEMBER_LIST_UPDATE, -}; - -enum class GatewayCloseCode : uint16_t { - // standard - Normal = 1000, - GoingAway = 1001, - ProtocolError = 1002, - Unsupported = 1003, - NoStatus = 1005, - Abnormal = 1006, - UnsupportedPayload = 1007, - PolicyViolation = 1008, - TooLarge = 1009, - MandatoryExtension = 1010, - ServerError = 1011, - ServiceRestart = 1012, - TryAgainLater = 1013, - BadGateway = 1014, - TLSHandshakeFailed = 1015, - - // discord - UnknownError = 4000, - UnknownOpcode = 4001, - DecodeError = 4002, - NotAuthenticated = 4003, - AuthenticationFailed = 4004, - AlreadyAuthenticated = 4005, - InvalidSequence = 4007, - RateLimited = 4008, - SessionTimedOut = 4009, - InvalidShard = 4010, - ShardingRequired = 4011, - InvalidAPIVersion = 4012, - InvalidIntents = 4013, - DisallowedIntents = 4014, - - // internal - UserDisconnect = 4091, - Reconnecting = 4092, -}; - -struct GatewayMessage { - GatewayOp Opcode; - nlohmann::json Data; - std::string Type; - int Sequence = -1; - - friend void from_json(const nlohmann::json &j, GatewayMessage &m); -}; - -struct HelloMessageData { - int HeartbeatInterval; - - friend void from_json(const nlohmann::json &j, HelloMessageData &m); -}; - -struct MessageDeleteData { - Snowflake ID; // - Snowflake ChannelID; // - Snowflake GuildID; // opt - - friend void from_json(const nlohmann::json &j, MessageDeleteData &m); -}; - -struct MessageDeleteBulkData { - std::vector IDs; // - Snowflake ChannelID; // - Snowflake GuildID; // opt - - friend void from_json(const nlohmann::json &j, MessageDeleteBulkData &m); -}; - -struct GuildMemberListUpdateMessage { - struct Item { - virtual ~Item() = default; - - std::string Type; - }; - - struct GroupItem : Item { - std::string ID; - int Count; - - friend void from_json(const nlohmann::json &j, GroupItem &m); - }; - - struct MemberItem : Item { - UserData User; - std::vector Roles; - std::optional Presence; - std::string PremiumSince; // opt - std::string Nickname; // opt - bool IsMuted; - std::string JoinedAt; - std::string HoistedRole; // null - bool IsDefeaned; - - GuildMember GetAsMemberData() const; - - friend void from_json(const nlohmann::json &j, MemberItem &m); - - private: - GuildMember m_member_data; - }; - - struct OpObject { - std::string Op; - std::optional Index; - std::optional>> Items; // SYNC - std::optional> Range; // SYNC - std::optional> OpItem; // UPDATE - - friend void from_json(const nlohmann::json &j, OpObject &m); - }; - - int OnlineCount; - int MemberCount; - std::string ListIDHash; - std::string GuildID; - std::vector Groups; - std::vector Ops; - - friend void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m); -}; - -struct LazyLoadRequestMessage { - Snowflake GuildID; - std::optional ShouldGetTyping; - std::optional ShouldGetActivities; - std::optional ShouldGetThreads; - std::optional> Members; // snowflake? - std::optional>>> Channels; // channel ID -> range of sidebar - std::optional> ThreadIDs; - - friend void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m); -}; - -struct UpdateStatusMessage { - int Since = 0; - std::vector Activities; - PresenceStatus Status; - bool IsAFK = false; - - friend void to_json(nlohmann::json &j, const UpdateStatusMessage &m); -}; - -struct ReadyEventData { - int GatewayVersion; - UserData SelfUser; - std::vector Guilds; - std::string SessionID; - std::vector PrivateChannels; - - // undocumented - std::optional> Users; - std::optional AnalyticsToken; - std::optional FriendSuggestionCount; - UserSettings Settings; - std::optional>> MergedMembers; - std::optional> Relationships; - std::optional> GuildJoinRequests; - // std::vector ConnectedAccounts; // opt - // std::map Consents; // opt - // std::vector Experiments; // opt - // std::vector GuildExperiments; // opt - // std::map Notes; // opt - // std::vector Presences; // opt - // std::vector ReadStates; // opt - // Unknown Tutorial; // opt, null - // std::vector UserGuildSettings; // opt - - friend void from_json(const nlohmann::json &j, ReadyEventData &m); -}; - -struct MergedPresence { - Snowflake UserID; - std::optional LastModified; - PresenceData Presence; - - friend void from_json(const nlohmann::json &j, MergedPresence &m); -}; - -struct SupplementalMergedPresencesData { - std::vector> Guilds; - std::vector Friends; - - friend void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m); -}; - -struct ReadySupplementalData { - SupplementalMergedPresencesData MergedPresences; - - friend void from_json(const nlohmann::json &j, ReadySupplementalData &m); -}; - -struct IdentifyProperties { - std::string OS; - std::string Browser; - std::string Device; - std::string SystemLocale; - std::string BrowserUserAgent; - std::string BrowserVersion; - std::string OSVersion; - std::string Referrer; - std::string ReferringDomain; - std::string ReferrerCurrent; - std::string ReferringDomainCurrent; - std::string ReleaseChannel; - int ClientBuildNumber; - std::string ClientEventSource; // empty -> null - - friend void to_json(nlohmann::json &j, const IdentifyProperties &m); -}; - -struct ClientStateProperties { - std::map GuildHashes; - std::string HighestLastMessageID = "0"; - int ReadStateVersion = 0; - int UserGuildSettingsVersion = -1; - - friend void to_json(nlohmann::json &j, const ClientStateProperties &m); -}; - -struct IdentifyMessage : GatewayMessage { - std::string Token; - IdentifyProperties Properties; - PresenceData Presence; - ClientStateProperties ClientState; - bool DoesSupportCompression = false; - int Capabilities; - - friend void to_json(nlohmann::json &j, const IdentifyMessage &m); -}; - -struct HeartbeatMessage : GatewayMessage { - int Sequence; - - friend void to_json(nlohmann::json &j, const HeartbeatMessage &m); -}; - -struct CreateMessageObject { - std::string Content; - std::optional MessageReference; - std::optional Nonce; - - friend void to_json(nlohmann::json &j, const CreateMessageObject &m); -}; - -struct MessageEditObject { - std::string Content; // opt, null - std::vector Embeds; // opt, null - int Flags = -1; // opt, null - - friend void to_json(nlohmann::json &j, const MessageEditObject &m); -}; - -struct GuildMemberUpdateMessage { - Snowflake GuildID; // - std::vector Roles; // - UserData User; // - std::string Nick; // opt, null - std::string JoinedAt; - std::string PremiumSince; // opt, null - - friend void from_json(const nlohmann::json &j, GuildMemberUpdateMessage &m); -}; - -struct ClientStatusData { - std::optional Desktop; - std::optional Mobile; - std::optional Web; - - friend void from_json(const nlohmann::json &j, ClientStatusData &m); -}; - -struct PresenceUpdateMessage { - nlohmann::json User; // the client updates an existing object from this data - std::optional GuildID; - std::string StatusMessage; - std::vector Activities; - ClientStatusData ClientStatus; - - friend void from_json(const nlohmann::json &j, PresenceUpdateMessage &m); -}; - -struct CreateDMObject { - std::vector Recipients; - - friend void to_json(nlohmann::json &j, const CreateDMObject &m); -}; - -struct ResumeMessage : GatewayMessage { - std::string Token; - std::string SessionID; - int Sequence; - - friend void to_json(nlohmann::json &j, const ResumeMessage &m); -}; - -struct GuildRoleUpdateObject { - Snowflake GuildID; - RoleData Role; - - friend void from_json(const nlohmann::json &j, GuildRoleUpdateObject &m); -}; - -struct GuildRoleCreateObject { - Snowflake GuildID; - RoleData Role; - - friend void from_json(const nlohmann::json &j, GuildRoleCreateObject &m); -}; - -struct GuildRoleDeleteObject { - Snowflake GuildID; - Snowflake RoleID; - - friend void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m); -}; - -struct MessageReactionAddObject { - Snowflake UserID; - Snowflake ChannelID; - Snowflake MessageID; - std::optional GuildID; - std::optional Member; - EmojiData Emoji; - - friend void from_json(const nlohmann::json &j, MessageReactionAddObject &m); -}; - -struct MessageReactionRemoveObject { - Snowflake UserID; - Snowflake ChannelID; - Snowflake MessageID; - std::optional GuildID; - EmojiData Emoji; - - friend void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m); -}; - -struct ChannelRecipientAdd { - UserData User; - Snowflake ChannelID; - - friend void from_json(const nlohmann::json &j, ChannelRecipientAdd &m); -}; - -struct ChannelRecipientRemove { - UserData User; - Snowflake ChannelID; - - friend void from_json(const nlohmann::json &j, ChannelRecipientRemove &m); -}; - -struct TypingStartObject { - Snowflake ChannelID; - std::optional GuildID; - Snowflake UserID; - uint64_t Timestamp; - std::optional Member; - - friend void from_json(const nlohmann::json &j, TypingStartObject &m); -}; - -// implement rest as needed -struct ModifyGuildObject { - std::optional Name; - std::optional IconData; - - friend void to_json(nlohmann::json &j, const ModifyGuildObject &m); -}; - -struct GuildBanRemoveObject { - Snowflake GuildID; - UserData User; - - friend void from_json(const nlohmann::json &j, GuildBanRemoveObject &m); -}; - -struct GuildBanAddObject { - Snowflake GuildID; - UserData User; - - friend void from_json(const nlohmann::json &j, GuildBanAddObject &m); -}; - -struct InviteCreateObject { - Snowflake ChannelID; - std::string Code; - std::string CreatedAt; - std::optional GuildID; - std::optional Inviter; - int MaxAge; - int MaxUses; - UserData TargetUser; - std::optional TargetUserType; - bool IsTemporary; - int Uses; - - friend void from_json(const nlohmann::json &j, InviteCreateObject &m); -}; - -struct InviteDeleteObject { - Snowflake ChannelID; - std::optional GuildID; - std::string Code; - - friend void from_json(const nlohmann::json &j, InviteDeleteObject &m); -}; - -struct ConnectionData { - std::string ID; - std::string Type; - std::string Name; - bool IsVerified; - - friend void from_json(const nlohmann::json &j, ConnectionData &m); -}; - -struct MutualGuildData { - Snowflake ID; - std::optional Nick; // null - - friend void from_json(const nlohmann::json &j, MutualGuildData &m); -}; - -struct UserProfileData { - std::vector ConnectedAccounts; - std::vector MutualGuilds; - std::optional PremiumGuildSince; // null - std::optional PremiumSince; // null - UserData User; - - friend void from_json(const nlohmann::json &j, UserProfileData &m); -}; - -struct UserNoteObject { - // idk if these can be null or missing but i play it safe - std::optional Note; - std::optional NoteUserID; - std::optional UserID; - - friend void from_json(const nlohmann::json &j, UserNoteObject &m); -}; - -struct UserSetNoteObject { - std::string Note; - - friend void to_json(nlohmann::json &j, const UserSetNoteObject &m); -}; - -struct UserNoteUpdateMessage { - std::string Note; - Snowflake ID; - - friend void from_json(const nlohmann::json &j, UserNoteUpdateMessage &m); -}; - -struct RelationshipsData { - std::vector Users; - - friend void from_json(const nlohmann::json &j, RelationshipsData &m); -}; - -struct ModifyGuildMemberObject { - // std::optional Nick; - // std::optional IsMuted; - // std::optional IsDeaf; - // std::optional ChannelID; - - std::optional> Roles; - - friend void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m); -}; - -struct ModifyGuildRoleObject { - std::optional Name; - std::optional Permissions; - std::optional Color; - std::optional IsHoisted; - std::optional Mentionable; - - friend void to_json(nlohmann::json &j, const ModifyGuildRoleObject &m); -}; - -struct ModifyGuildRolePositionsObject { - struct PositionParam { - Snowflake ID; - std::optional Position; // no idea why this can be optional - - friend void to_json(nlohmann::json &j, const PositionParam &m); - }; - std::vector Positions; - - friend void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject &m); -}; - -struct GuildEmojisUpdateObject { - Snowflake GuildID; - // std::vector Emojis; - // GuildHashes, undocumented - - friend void from_json(const nlohmann::json &j, GuildEmojisUpdateObject &m); -}; - -struct ModifyGuildEmojiObject { - std::optional Name; - // std::optional> Roles; - - friend void to_json(nlohmann::json &j, const ModifyGuildEmojiObject &m); -}; - -struct GuildJoinRequestCreateData { - GuildApplicationStatus Status; - GuildApplicationData Request; - Snowflake GuildID; - - friend void from_json(const nlohmann::json &j, GuildJoinRequestCreateData &m); -}; - -using GuildJoinRequestUpdateData = GuildJoinRequestCreateData; - -struct GuildJoinRequestDeleteData { - Snowflake UserID; - Snowflake GuildID; - - friend void from_json(const nlohmann::json &j, GuildJoinRequestDeleteData &m); -}; - -struct VerificationFieldObject { - std::string Type; - std::string Label; - bool Required; - std::vector Values; - std::optional Response; // present in client to server - - friend void from_json(const nlohmann::json &j, VerificationFieldObject &m); - friend void to_json(nlohmann::json &j, const VerificationFieldObject &m); -}; - -struct VerificationGateInfoObject { - std::optional Description; - std::optional> VerificationFields; - std::optional Version; - std::optional Enabled; // present only in client to server in modify gate - - friend void from_json(const nlohmann::json &j, VerificationGateInfoObject &m); - friend void to_json(nlohmann::json &j, const VerificationGateInfoObject &m); -}; - -// not sure what the structure for this really is -struct RateLimitedResponse { - int Code; - bool Global; - std::optional Message; - float RetryAfter; - - friend void from_json(const nlohmann::json &j, RateLimitedResponse &m); -}; - -struct RelationshipRemoveData { - Snowflake ID; - RelationshipType Type; - - friend void from_json(const nlohmann::json &j, RelationshipRemoveData &m); -}; - -struct RelationshipAddData { - Snowflake ID; - // Nickname; same deal as the other comment somewhere else - RelationshipType Type; - UserData User; - // std::optional ShouldNotify; // i guess if the client should send a notification. not worth caring about - - friend void from_json(const nlohmann::json &j, RelationshipAddData &m); -}; - -struct FriendRequestObject { - std::string Username; - int Discriminator; - - friend void to_json(nlohmann::json &j, const FriendRequestObject &m); -}; - -struct PutRelationshipObject { - std::optional Type; - - friend void to_json(nlohmann::json &j, const PutRelationshipObject &m); -}; - -struct ThreadCreateData { - ChannelData Channel; - - friend void from_json(const nlohmann::json &j, ThreadCreateData &m); -}; - -struct ThreadDeleteData { - Snowflake ID; - Snowflake GuildID; - Snowflake ParentID; - ChannelType Type; - - friend void from_json(const nlohmann::json &j, ThreadDeleteData &m); -}; - -// pretty different from docs -struct ThreadListSyncData { - std::vector Threads; - Snowflake GuildID; - // std::optional> MostRecentMessages; - - friend void from_json(const nlohmann::json &j, ThreadListSyncData &m); -}; - -struct ThreadMembersUpdateData { - Snowflake ID; - Snowflake GuildID; - int MemberCount; - std::optional> AddedMembers; - std::optional> RemovedMemberIDs; - - friend void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m); -}; - -struct ArchivedThreadsResponseData { - std::vector Threads; - std::vector Members; - bool HasMore; - - friend void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m); -}; - -struct ThreadMemberUpdateData { - ThreadMemberObject Member; - - friend void from_json(const nlohmann::json &j, ThreadMemberUpdateData &m); -}; - -struct ThreadUpdateData { - ChannelData Thread; - - friend void from_json(const nlohmann::json &j, ThreadUpdateData &m); -}; - -struct ThreadMemberListUpdateData { - struct UserEntry { - Snowflake UserID; - // PresenceData Presence; - GuildMember Member; - - friend void from_json(const nlohmann::json &j, UserEntry &m); - }; - - Snowflake ThreadID; - Snowflake GuildID; - std::vector Members; - - friend void from_json(const nlohmann::json &j, ThreadMemberListUpdateData &m); -}; - -struct ModifyChannelObject { - std::optional Archived; - std::optional Locked; - - friend void to_json(nlohmann::json &j, const ModifyChannelObject &m); -}; diff --git a/discord/permissions.cpp b/discord/permissions.cpp deleted file mode 100644 index 63eeb9f..0000000 --- a/discord/permissions.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "permissions.hpp" - -void from_json(const nlohmann::json &j, PermissionOverwrite &m) { - JS_D("id", m.ID); - std::string tmp; - m.Type = j.at("type").get() == 0 ? PermissionOverwrite::ROLE : PermissionOverwrite::MEMBER; - JS_D("allow", tmp); - m.Allow = static_cast(std::stoull(tmp)); - JS_D("deny", tmp); - m.Deny = static_cast(std::stoull(tmp)); -} diff --git a/discord/permissions.hpp b/discord/permissions.hpp deleted file mode 100644 index 56ef742..0000000 --- a/discord/permissions.hpp +++ /dev/null @@ -1,224 +0,0 @@ -#pragma once -#include -#include "snowflake.hpp" -#include "json.hpp" -#include "util.hpp" - -constexpr static uint64_t PERMISSION_MAX_BIT = 36; -enum class Permission : uint64_t { - NONE = 0, - CREATE_INSTANT_INVITE = (1ULL << 0), // Allows creation of instant invites - KICK_MEMBERS = (1ULL << 1), // Allows kicking members - BAN_MEMBERS = (1ULL << 2), // Allows banning members - ADMINISTRATOR = (1ULL << 3), // Allows all permissions and bypasses channel permission overwrites - MANAGE_CHANNELS = (1ULL << 4), // Allows management and editing of channels - MANAGE_GUILD = (1ULL << 5), // Allows management and editing of the guild - ADD_REACTIONS = (1ULL << 6), // Allows for the addition of reactions to messages - VIEW_AUDIT_LOG = (1ULL << 7), // Allows for viewing of audit logs - PRIORITY_SPEAKER = (1ULL << 8), // Allows for using priority speaker in a voice channel - STREAM = (1ULL << 9), // Allows the user to go live - VIEW_CHANNEL = (1ULL << 10), // Allows guild members to view a channel, which includes reading messages in text channels - SEND_MESSAGES = (1ULL << 11), // Allows for sending messages in a channel - SEND_TTS_MESSAGES = (1ULL << 12), // Allows for sending of /tts messages - MANAGE_MESSAGES = (1ULL << 13), // Allows for deletion of other users messages - EMBED_LINKS = (1ULL << 14), // Links sent by users with this permission will be auto-embedded - ATTACH_FILES = (1ULL << 15), // Allows for uploading images and files - READ_MESSAGE_HISTORY = (1ULL << 16), // Allows for reading of message history - MENTION_EVERYONE = (1ULL << 17), // Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel - USE_EXTERNAL_EMOJIS = (1ULL << 18), // Allows the usage of custom emojis from other servers - VIEW_GUILD_INSIGHTS = (1ULL << 19), // Allows for viewing guild insights - CONNECT = (1ULL << 20), // Allows for joining of a voice channel - SPEAK = (1ULL << 21), // Allows for speaking in a voice channel - MUTE_MEMBERS = (1ULL << 22), // Allows for muting members in a voice channel - DEAFEN_MEMBERS = (1ULL << 23), // Allows for deafening of members in a voice channel - MOVE_MEMBERS = (1ULL << 24), // Allows for moving of members between voice channels - USE_VAD = (1ULL << 25), // Allows for using voice-activity-detection in a voice channel - CHANGE_NICKNAME = (1ULL << 26), // Allows for modification of own nickname - MANAGE_NICKNAMES = (1ULL << 27), // Allows for modification of other users nicknames - MANAGE_ROLES = (1ULL << 28), // Allows management and editing of roles - MANAGE_WEBHOOKS = (1ULL << 29), // Allows management and editing of webhooks - MANAGE_EMOJIS = (1ULL << 30), // Allows management and editing of emojis - USE_SLASH_COMMANDS = (1ULL << 31), // Allows members to use slash commands in text channels - REQUEST_TO_SPEAK = (1ULL << 32), // Allows for requesting to speak in stage channels - MANAGE_THREADS = (1ULL << 34), // Allows for deleting and archiving threads, and viewing all private threads - USE_PUBLIC_THREADS = (1ULL << 35), // Allows for creating and participating in threads - USE_PRIVATE_THREADS = (1ULL << 36), // Allows for creating and participating in private threads - - ALL = 0x1FFFFFFFFFULL, -}; -template<> -struct Bitwise { - static const bool enable = true; -}; - -struct PermissionOverwrite { - enum OverwriteType : uint8_t { - ROLE = 0, - MEMBER = 1, - }; - - Snowflake ID; - OverwriteType Type; - Permission Allow; - Permission Deny; - - friend void from_json(const nlohmann::json &j, PermissionOverwrite &m); -}; - -constexpr const char *GetPermissionString(Permission perm) { - switch (perm) { - case Permission::NONE: - return "None"; - case Permission::CREATE_INSTANT_INVITE: - return "Create Invite"; - case Permission::KICK_MEMBERS: - return "Kick Members"; - case Permission::BAN_MEMBERS: - return "Ban Members"; - case Permission::ADMINISTRATOR: - return "Administrator"; - case Permission::MANAGE_CHANNELS: - return "Manage Channels"; - case Permission::MANAGE_GUILD: - return "Manage Server"; - case Permission::ADD_REACTIONS: - return "Add Reactions"; - case Permission::VIEW_AUDIT_LOG: - return "View Audit Log"; - case Permission::PRIORITY_SPEAKER: - return "Use Priority Speaker"; - case Permission::STREAM: - return "Video"; - case Permission::VIEW_CHANNEL: - return "View Channel"; - case Permission::SEND_MESSAGES: - return "Send Messages"; - case Permission::SEND_TTS_MESSAGES: - return "Use TTS"; - case Permission::MANAGE_MESSAGES: - return "Manage Messages"; - case Permission::EMBED_LINKS: - return "Embed Links"; - case Permission::ATTACH_FILES: - return "Attach Files"; - case Permission::READ_MESSAGE_HISTORY: - return "Read Message History"; - case Permission::MENTION_EVERYONE: - return "Mention @everyone"; - case Permission::USE_EXTERNAL_EMOJIS: - return "Use External Emojis"; - case Permission::VIEW_GUILD_INSIGHTS: - return "View Server Insights"; - case Permission::CONNECT: - return "Connect to Voice"; - case Permission::SPEAK: - return "Speak in Voice"; - case Permission::MUTE_MEMBERS: - return "Mute Members"; - case Permission::DEAFEN_MEMBERS: - return "Deafen Members"; - case Permission::MOVE_MEMBERS: - return "Move Members"; - case Permission::USE_VAD: - return "Use Voice Activation"; - case Permission::CHANGE_NICKNAME: - return "Change Nickname"; - case Permission::MANAGE_NICKNAMES: - return "Manage Nicknames"; - case Permission::MANAGE_ROLES: - return "Manage Roles"; - case Permission::MANAGE_WEBHOOKS: - return "Manage Webhooks"; - case Permission::MANAGE_EMOJIS: - return "Manage Emojis"; - case Permission::USE_SLASH_COMMANDS: - return "Use Slash Commands"; - case Permission::MANAGE_THREADS: - return "Manage Threads"; - case Permission::USE_PUBLIC_THREADS: - return "Use Public Threads"; - case Permission::USE_PRIVATE_THREADS: - return "Use Private Threads"; - default: - return "Unknown Permission"; - } -} - -constexpr const char *GetPermissionDescription(Permission perm) { - switch (perm) { - case Permission::NONE: - return ""; - case Permission::CREATE_INSTANT_INVITE: - return "Allows members to invite new people to this server."; - case Permission::KICK_MEMBERS: - return "Allows members to remove other members from this server. Kicked members will be able to rejoin if they have another invite."; - case Permission::BAN_MEMBERS: - return "Allows members to permanently ban other members from this server."; - case Permission::ADMINISTRATOR: - return "Members with this permission will have every permission and will also bypass all channel specific permissions or restrictions (for example, these members would get access to all private channels). This is a dangerous permission to grant."; - case Permission::MANAGE_CHANNELS: - return "Allows members to create, edit, or delete channels."; - case Permission::MANAGE_GUILD: - return "Allows members to change this server's name, switch regions, and add bots to this server."; - case Permission::ADD_REACTIONS: - return "Allows members to add new emoji reactions to a message. If this permission is disabled, members can still react using any existing reactions on a message."; - case Permission::VIEW_AUDIT_LOG: - return "Allows members to view a record of who made which changes in this server."; - case Permission::PRIORITY_SPEAKER: - return "Allows members to be more easily heard in voice channels. When activated, the volume of others without this permission will be automatically lowered. Priority Speaker is activated by using the Push to Talk (Priority) keybind."; - case Permission::STREAM: - return "Allows members to share their video, screen share, or stream a game in this server."; - case Permission::VIEW_CHANNEL: - return "Allows members to view channels by default (excluding private channels)."; - case Permission::SEND_MESSAGES: - return "Allows members to send messages in text channels."; - case Permission::SEND_TTS_MESSAGES: - return "Allows members to send text-to-speech messages by starting a message with /tts. These messages can be heard by anyone focused on thsi channel."; - case Permission::MANAGE_MESSAGES: - return "Allows members to delete messages by other members or pin any message"; - case Permission::EMBED_LINKS: - return "Allows links that members share to show embedded content in text channels."; - case Permission::ATTACH_FILES: - return "Allows members to upload files or media in text channels."; - case Permission::READ_MESSAGE_HISTORY: - return "Allows members to read previous messages sent in channels. If this permission is disabled, members only see messages sent when they are online and focused on that channel."; - case Permission::MENTION_EVERYONE: - return "Allows members to use @everyone (everyone in the server) or @here (only online members in that channel). They can also @mention all roles, even if the role's \"Allow anyone to mention this role\" permission is disabled."; - case Permission::USE_EXTERNAL_EMOJIS: - return "Allows members to use emoji from other servers, if they're a Discord Nitro member"; - case Permission::VIEW_GUILD_INSIGHTS: - return "Allows members to view Server Insights, which shows data on community growth, engagement, and more."; - case Permission::CONNECT: - return "Allows members to join voice channels and hear others."; - case Permission::SPEAK: - return "Allows members to talk in voice channels. If this permission is disabled, members are default muted until somebody with the \"Mute Members\" permission un-mutes them."; - case Permission::MUTE_MEMBERS: - return "Allows members to mute other members in voice channels for everyone."; - case Permission::DEAFEN_MEMBERS: - return "Allows members to deafen other members in voice channels, which means they won't be able to speak or hear others."; - case Permission::MOVE_MEMBERS: - return "Allows members to move other members between voice channels that the member with the permission has access to."; - case Permission::USE_VAD: - return "Allows members to speak in voice channels by simply talking. If this permission is disabled, members are required to use Push-to-talk. Good for controlling background noise or noisy members."; - case Permission::CHANGE_NICKNAME: - return "Allows members to change their own nickname, a custom name for just this server."; - case Permission::MANAGE_NICKNAMES: - return "Allows members to change the nicknames of other members."; - case Permission::MANAGE_ROLES: - return "Allows members to create new roles and edit or delete roles lower than their highest role. Also allows members to change permissions of individual channels that they have access to."; - case Permission::MANAGE_WEBHOOKS: - return "Allows members to create, edit, or delete webhooks, which can post messages from other apps or sites into this server."; - case Permission::MANAGE_EMOJIS: - return "Allows members to add or remove custom emojis in this server."; - case Permission::USE_SLASH_COMMANDS: - return "Allows members to use slash commands in text channels."; - case Permission::MANAGE_THREADS: - return "Allows members to rename, delete, archive/unarchive, and turn on slow mode for threads."; - case Permission::USE_PUBLIC_THREADS: - return "Allows members to talk in threads. The \"Send Messages\" permission must be enabled for members to start new threads; if it's disabled, they can only respond to existing threads."; - case Permission::USE_PRIVATE_THREADS: - return "Allows members to create and chat in private threads. The \"Send Messages\" permission must be enabled for members to start new private threads; if it's disabled, they can only respond to private threads they're added to."; - default: - return ""; - } -} diff --git a/discord/relationship.cpp b/discord/relationship.cpp deleted file mode 100644 index d65d2c1..0000000 --- a/discord/relationship.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "relationship.hpp" - -void from_json(const nlohmann::json &j, RelationshipData &m) { - JS_D("type", m.Type); - JS_D("id", m.ID); -} diff --git a/discord/relationship.hpp b/discord/relationship.hpp deleted file mode 100644 index d492bd3..0000000 --- a/discord/relationship.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "json.hpp" -#include "user.hpp" - -enum class RelationshipType { - None = 0, - Friend = 1, - Blocked = 2, - PendingIncoming = 3, - PendingOutgoing = 4, - Implicit = 5, -}; - -struct RelationshipData { - // Snowflake UserID; this is the same as ID apparently but it looks new so i wont touch it - RelationshipType Type; - Snowflake ID; - // Unknown Nickname; // null - - friend void from_json(const nlohmann::json &j, RelationshipData &m); -}; diff --git a/discord/role.cpp b/discord/role.cpp deleted file mode 100644 index 07a912e..0000000 --- a/discord/role.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "role.hpp" - -void from_json(const nlohmann::json &j, RoleData &m) { - JS_D("id", m.ID); - JS_D("name", m.Name); - JS_D("color", m.Color); - JS_D("hoist", m.IsHoisted); - JS_D("position", m.Position); - std::string tmp; - JS_D("permissions", tmp); - m.Permissions = static_cast(std::stoull(tmp)); - JS_D("managed", m.IsManaged); - JS_D("mentionable", m.IsMentionable); -} diff --git a/discord/role.hpp b/discord/role.hpp deleted file mode 100644 index f638b65..0000000 --- a/discord/role.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "snowflake.hpp" -#include "json.hpp" -#include "permissions.hpp" -#include -#include - -struct RoleData { - Snowflake ID; - std::string Name; - int Color; - bool IsHoisted; - int Position; - int PermissionsLegacy; - Permission Permissions; - bool IsManaged; - bool IsMentionable; - - friend void from_json(const nlohmann::json &j, RoleData &m); -}; diff --git a/discord/snowflake.cpp b/discord/snowflake.cpp deleted file mode 100644 index cea9153..0000000 --- a/discord/snowflake.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "snowflake.hpp" -#include -#include -#include - -constexpr static uint64_t DiscordEpochSeconds = 1420070400; - -const Snowflake Snowflake::Invalid = -1ULL; - -Snowflake::Snowflake() - : m_num(Invalid) {} - -Snowflake::Snowflake(uint64_t n) - : m_num(n) {} - -Snowflake::Snowflake(const std::string &str) { - if (str.size()) - m_num = std::stoull(str); - else - m_num = Invalid; -} -Snowflake::Snowflake(const Glib::ustring &str) { - if (str.size()) - m_num = std::strtoull(str.c_str(), nullptr, 10); - else - m_num = Invalid; -}; - -Snowflake Snowflake::FromNow() { - using namespace std::chrono; - // not guaranteed to work but it probably will anyway - static uint64_t counter = 0; - const auto millis_since_epoch = static_cast(duration_cast(system_clock::now().time_since_epoch()).count()); - const auto epoch = millis_since_epoch - DiscordEpochSeconds * 1000; - uint64_t snowflake = epoch << 22; - // worker id and process id would be OR'd in here but there's no point - snowflake |= counter++ % 4096; - return snowflake; -} - -bool Snowflake::IsValid() const { - return m_num != Invalid; -} - -std::string Snowflake::GetLocalTimestamp() const { - const time_t secs_since_epoch = (m_num / SecondsInterval) + DiscordEpochSeconds; - const std::tm tm = *localtime(&secs_since_epoch); - std::stringstream ss; - const static std::locale locale(""); - ss.imbue(locale); - ss << std::put_time(&tm, "%X %x"); - return ss.str(); -} - -void from_json(const nlohmann::json &j, Snowflake &s) { - if (j.is_string()) { - std::string tmp; - j.get_to(tmp); - s.m_num = std::stoull(tmp); - } else { - j.get_to(s.m_num); - } -} - -void to_json(nlohmann::json &j, const Snowflake &s) { - j = std::to_string(s); -} diff --git a/discord/snowflake.hpp b/discord/snowflake.hpp deleted file mode 100644 index 0b79723..0000000 --- a/discord/snowflake.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include -#include -#include - -struct Snowflake { - Snowflake(); - Snowflake(uint64_t n); - Snowflake(const std::string &str); - Snowflake(const Glib::ustring &str); - - static Snowflake FromNow(); // not thread safe - - bool IsValid() const; - std::string GetLocalTimestamp() const; - - bool operator==(const Snowflake &s) const noexcept { - return m_num == s.m_num; - } - - bool operator<(const Snowflake &s) const noexcept { - return m_num < s.m_num; - } - - operator uint64_t() const noexcept { - return m_num; - } - - const static Snowflake Invalid; // makes sense to me - const static uint64_t SecondsInterval = 4194304000ULL; // the "difference" between two snowflakes one second apart - - friend void from_json(const nlohmann::json &j, Snowflake &s); - friend void to_json(nlohmann::json &j, const Snowflake &s); - -private: - friend struct std::hash; - friend struct std::less; - unsigned long long m_num; -}; - -namespace std { -template<> -struct hash { - std::size_t operator()(const Snowflake &k) const { - return k.m_num; - } -}; - -template<> -struct less { - bool operator()(const Snowflake &l, const Snowflake &r) const { - return l.m_num < r.m_num; - } -}; -} // namespace std diff --git a/discord/sticker.cpp b/discord/sticker.cpp deleted file mode 100644 index b92d031..0000000 --- a/discord/sticker.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "sticker.hpp" - -void to_json(nlohmann::json &j, const StickerData &m) { - j["id"] = m.ID; - j["pack_id"] = m.PackID; - j["name"] = m.Name; - j["description"] = m.Description; - JS_IF("tags", m.Tags); - JS_IF("asset", m.AssetHash); - JS_IF("preview_asset", m.PreviewAssetHash); - j["format_type"] = m.FormatType; -} - -void from_json(const nlohmann::json &j, StickerData &m) { - JS_D("id", m.ID); - JS_D("pack_id", m.PackID); - JS_D("name", m.Name); - JS_D("description", m.Description); - JS_O("tags", m.Tags); - JS_O("asset", m.AssetHash); - JS_ON("preview_asset", m.PreviewAssetHash); - JS_D("format_type", m.FormatType); -} - -std::string StickerData::GetURL() const { - if (!AssetHash.has_value()) return ""; - if (FormatType == StickerFormatType::PNG || FormatType == StickerFormatType::APNG) - return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".png?size=256"; - else if (FormatType == StickerFormatType::LOTTIE) - return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".json"; - return ""; -} - -void to_json(nlohmann::json &j, const StickerItem &m) { - j["id"] = m.ID; - j["name"] = m.Name; - j["format_type"] = m.FormatType; -} - -void from_json(const nlohmann::json &j, StickerItem &m) { - JS_D("id", m.ID); - JS_D("name", m.Name); - JS_D("format_type", m.FormatType); -} - -std::string StickerItem::GetURL() const { - if (FormatType == StickerFormatType::PNG || FormatType == StickerFormatType::APNG) - return "https://media.discordapp.net/stickers/" + std::to_string(ID) + ".png?size=256"; - else if (FormatType == StickerFormatType::LOTTIE) - return "https://media.discordapp.net/stickers/" + std::to_string(ID) + ".json"; - return ""; -} diff --git a/discord/sticker.hpp b/discord/sticker.hpp deleted file mode 100644 index d23fe7b..0000000 --- a/discord/sticker.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include -#include "snowflake.hpp" -#include "json.hpp" - -// unstable - -enum class StickerFormatType { - PNG = 1, - APNG = 2, - LOTTIE = 3, -}; - -struct StickerData { - Snowflake ID; - Snowflake PackID; - std::string Name; - std::string Description; - std::optional Tags; - std::optional AssetHash; - std::optional PreviewAssetHash; - StickerFormatType FormatType; - - friend void to_json(nlohmann::json &j, const StickerData &m); - friend void from_json(const nlohmann::json &j, StickerData &m); - - std::string GetURL() const; -}; - -struct StickerItem { - StickerFormatType FormatType; - Snowflake ID; - std::string Name; - - friend void to_json(nlohmann::json &j, const StickerItem &m); - friend void from_json(const nlohmann::json &j, StickerItem &m); - - std::string GetURL() const; -}; diff --git a/discord/store.cpp b/discord/store.cpp deleted file mode 100644 index 9b615fd..0000000 --- a/discord/store.cpp +++ /dev/null @@ -1,2232 +0,0 @@ -#include "store.hpp" -#include - -using namespace std::literals::string_literals; - -// hopefully the casting between signed and unsigned int64 doesnt cause issues - -Store::Store(bool mem_store) - : m_db_path(mem_store ? ":memory:" : std::filesystem::temp_directory_path() / "abaddon-store.db") - , m_db(m_db_path.string().c_str()) { - if (!m_db.OK()) { - fprintf(stderr, "error opening database: %s\n", m_db.ErrStr()); - return; - } - - m_db.Execute(R"( - PRAGMA writable_schema = 1; - DELETE FROM sqlite_master; - PRAGMA writable_schema = 0; - VACUUM; - PRAGMA integrity_check; - )"); - if (!m_db.OK()) { - fprintf(stderr, "failed to clear database: %s\n", m_db.ErrStr()); - return; - } - - if (m_db.Execute("PRAGMA journal_mode = WAL") != SQLITE_OK) { - fprintf(stderr, "enabling write-ahead-log failed: %s\n", m_db.ErrStr()); - return; - } - - if (m_db.Execute("PRAGMA synchronous = NORMAL") != SQLITE_OK) { - fprintf(stderr, "setting synchronous failed: %s\n", m_db.ErrStr()); - return; - } - - m_ok &= CreateTables(); - m_ok &= CreateStatements(); -} - -Store::~Store() { - m_db.Close(); - if (!m_db.OK()) { - fprintf(stderr, "error closing database: %s\n", m_db.ErrStr()); - return; - } - - if (m_db_path != ":memory:") { - std::error_code ec; - std::filesystem::remove(m_db_path, ec); - } -} - -bool Store::IsValid() const { - return m_db.OK() && m_ok; -} - -void Store::SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban) { - auto &s = m_stmt_set_ban; - - s->Bind(1, guild_id); - s->Bind(2, user_id); - s->Bind(3, ban.Reason); - - if (!s->Insert()) - fprintf(stderr, "ban insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); - - s->Reset(); -} - -void Store::SetChannel(Snowflake id, const ChannelData &chan) { - auto &s = m_stmt_set_chan; - - s->Bind(1, id); - s->Bind(2, chan.Type); - s->Bind(3, chan.GuildID); - s->Bind(4, chan.Position); - s->Bind(5, chan.Name); - s->Bind(6, chan.Topic); - s->Bind(7, chan.IsNSFW); - s->Bind(8, chan.LastMessageID); - s->Bind(9, chan.Bitrate); - s->Bind(10, chan.UserLimit); - s->Bind(11, chan.RateLimitPerUser); - s->Bind(12, chan.Icon); - s->Bind(13, chan.OwnerID); - s->Bind(14, chan.ApplicationID); - s->Bind(15, chan.ParentID); - s->Bind(16, chan.LastPinTimestamp); - if (chan.ThreadMetadata.has_value()) { - s->Bind(17, chan.ThreadMetadata->IsArchived); - s->Bind(18, chan.ThreadMetadata->AutoArchiveDuration); - s->Bind(19, chan.ThreadMetadata->ArchiveTimestamp); - } else { - s->Bind(17); - s->Bind(18); - s->Bind(19); - } - - if (!s->Insert()) - fprintf(stderr, "channel insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - - if (chan.Recipients.has_value()) { - BeginTransaction(); - auto &s = m_stmt_set_recipient; - for (const auto &r : *chan.Recipients) { - s->Bind(1, chan.ID); - s->Bind(2, r.ID); - if (!s->Insert()) - fprintf(stderr, "recipient insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(chan.ID), static_cast(r.ID), m_db.ErrStr()); - s->Reset(); - } - EndTransaction(); - } else if (chan.RecipientIDs.has_value()) { - BeginTransaction(); - auto &s = m_stmt_set_recipient; - for (const auto &id : *chan.RecipientIDs) { - s->Bind(1, chan.ID); - s->Bind(2, id); - if (!s->Insert()) - fprintf(stderr, "recipient insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(chan.ID), static_cast(id), m_db.ErrStr()); - s->Reset(); - } - EndTransaction(); - } - - s->Reset(); -} - -void Store::SetEmoji(Snowflake id, const EmojiData &emoji) { - auto &s = m_stmt_set_emoji; - - s->Bind(1, id); - s->Bind(2, emoji.Name); - if (emoji.Creator.has_value()) - s->Bind(3, emoji.Creator->ID); - else - s->Bind(3); - s->Bind(4, emoji.NeedsColons); - s->Bind(5, emoji.IsManaged); - s->Bind(6, emoji.IsAnimated); - s->Bind(7, emoji.IsAvailable); - - if (emoji.Roles.has_value()) { - BeginTransaction(); - - auto &s = m_stmt_set_emoji_role; - - for (const auto &r : *emoji.Roles) { - s->Bind(1, id); - s->Bind(2, r); - if (!s->Insert()) - fprintf(stderr, "emoji role insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(r), m_db.ErrStr()); - s->Reset(); - } - - EndTransaction(); - } - - if (!s->Insert()) - fprintf(stderr, "emoji insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - - s->Reset(); -} - -void Store::SetGuild(Snowflake id, const GuildData &guild) { - BeginTransaction(); - auto &s = m_stmt_set_guild; - - s->Bind(1, guild.ID); - s->Bind(2, guild.Name); - s->Bind(3, guild.Icon); - s->Bind(4, guild.Splash); - s->Bind(5, guild.IsOwner); - s->Bind(6, guild.OwnerID); - s->Bind(7, guild.PermissionsNew); - s->Bind(8, guild.VoiceRegion); - s->Bind(9, guild.AFKChannelID); - s->Bind(10, guild.AFKTimeout); - s->Bind(11, guild.VerificationLevel); - s->Bind(12, guild.DefaultMessageNotifications); - s->Bind(13, guild.MFALevel); - s->Bind(14, guild.ApplicationID); - s->Bind(15, guild.IsWidgetEnabled); - s->Bind(16, guild.WidgetChannelID); - s->Bind(17, guild.SystemChannelFlags); - s->Bind(18, guild.RulesChannelID); - s->Bind(19, guild.JoinedAt); - s->Bind(20, guild.IsLarge); - s->Bind(21, guild.IsUnavailable); - s->Bind(22, guild.MemberCount); - s->Bind(23, guild.MaxPresences); - s->Bind(24, guild.MaxMembers); - s->Bind(25, guild.VanityURL); - s->Bind(26, guild.Description); - s->Bind(27, guild.BannerHash); - s->Bind(28, guild.PremiumTier); - s->Bind(29, guild.PremiumSubscriptionCount); - s->Bind(30, guild.PreferredLocale); - s->Bind(31, guild.PublicUpdatesChannelID); - s->Bind(32, guild.MaxVideoChannelUsers); - s->Bind(33, guild.ApproximateMemberCount); - s->Bind(34, guild.ApproximatePresenceCount); - s->Bind(35, guild.IsLazy); - - if (!s->Insert()) - fprintf(stderr, "guild insert failed for %" PRIu64 ": %s\n", static_cast(guild.ID), m_db.ErrStr()); - - s->Reset(); - - if (guild.Emojis.has_value()) { - auto &s = m_stmt_set_guild_emoji; - for (const auto &emoji : *guild.Emojis) { - s->Bind(1, guild.ID); - s->Bind(2, emoji.ID); - if (!s->Insert()) - fprintf(stderr, "guild emoji insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild.ID), static_cast(emoji.ID), m_db.ErrStr()); - s->Reset(); - } - } - - if (guild.Features.has_value()) { - auto &s = m_stmt_set_guild_feature; - - for (const auto &feature : *guild.Features) { - s->Bind(1, guild.ID); - s->Bind(2, feature); - if (!s->Insert()) - fprintf(stderr, "guild feature insert failed for %" PRIu64 "/%s: %s\n", static_cast(guild.ID), feature.c_str(), m_db.ErrStr()); - s->Reset(); - } - } - - if (guild.Threads.has_value()) { - auto &s = m_stmt_set_thread; - - for (const auto &thread : *guild.Threads) { - s->Bind(1, guild.ID); - s->Bind(2, thread.ID); - if (!s->Insert()) - fprintf(stderr, "guild thread insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild.ID), static_cast(thread.ID), m_db.ErrStr()); - s->Reset(); - } - } - - EndTransaction(); -} - -void Store::SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data) { - auto &s = m_stmt_set_member; - - s->Bind(1, user_id); - s->Bind(2, guild_id); - s->Bind(3, data.Nickname); - s->Bind(4, data.JoinedAt); - s->Bind(5, data.PremiumSince); - s->Bind(6, data.IsDeafened); - s->Bind(7, data.IsMuted); - s->Bind(8, data.Avatar); - s->Bind(9, data.IsPending); - - if (!s->Insert()) - fprintf(stderr, "member insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); - - s->Reset(); - - { - auto &s = m_stmt_set_member_roles; - - BeginTransaction(); - for (const auto &role : data.Roles) { - s->Bind(1, user_id); - s->Bind(2, role); - if (!s->Insert()) - fprintf(stderr, "member role insert failed for %" PRIu64 "/%" PRIu64 "/%" PRIu64 ": %s\n", - static_cast(user_id), static_cast(guild_id), static_cast(role), m_db.ErrStr()); - s->Reset(); - } - EndTransaction(); - } -} - -void Store::SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction) { - auto &s = m_stmt_set_interaction; - - s->Bind(1, message_id); - s->Bind(2, interaction.ID); - s->Bind(3, interaction.Type); - s->Bind(4, interaction.Name); - s->Bind(5, interaction.User.ID); - - if (!s->Insert()) - fprintf(stderr, "message interaction failed for %" PRIu64 ": %s\n", static_cast(message_id), m_db.ErrStr()); - - s->Reset(); -} - -void Store::SetMessage(Snowflake id, const Message &message) { - auto &s = m_stmt_set_msg; - - BeginTransaction(); - - s->Bind(1, id); - s->Bind(2, message.ChannelID); - s->Bind(3, message.GuildID); - s->Bind(4, message.Author.ID); - s->Bind(5, message.Content); - s->Bind(6, message.Timestamp); - s->Bind(7, message.EditedTimestamp); - s->Bind(8, message.IsTTS); - s->Bind(9, message.DoesMentionEveryone); - s->BindAsJSON(10, message.Embeds); - s->Bind(11, message.IsPinned); - s->Bind(12, message.WebhookID); - s->Bind(13, message.Type); - s->BindAsJSON(14, message.Application); - s->Bind(15, message.Flags); - s->BindAsJSON(16, message.Stickers); - s->Bind(17, message.IsDeleted()); - s->Bind(18, message.IsEdited()); - s->Bind(19, message.IsPending); - s->Bind(20, message.Nonce); - s->BindAsJSON(21, message.StickerItems); - - if (!s->Insert()) - fprintf(stderr, "message insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - - s->Reset(); - - if (message.MessageReference.has_value()) { - auto &s = m_stmt_set_msg_ref; - s->Bind(1, message.ID); - s->Bind(2, message.MessageReference->MessageID); - s->Bind(3, message.MessageReference->ChannelID); - s->Bind(4, message.MessageReference->GuildID); - - if (!s->Insert()) - fprintf(stderr, "message ref insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - - s->Reset(); - } - - for (const auto &u : message.Mentions) { - auto &s = m_stmt_set_mention; - s->Bind(1, id); - s->Bind(2, u.ID); - if (!s->Insert()) - fprintf(stderr, "message mention insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(u.ID), m_db.ErrStr()); - s->Reset(); - } - - for (const auto &a : message.Attachments) { - auto &s = m_stmt_set_attachment; - s->Bind(1, id); - s->Bind(2, a.ID); - s->Bind(3, a.Filename); - s->Bind(4, a.Bytes); - s->Bind(5, a.URL); - s->Bind(6, a.ProxyURL); - s->Bind(7, a.Height); - s->Bind(8, a.Width); - if (!s->Insert()) - fprintf(stderr, "message attachment insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(id), static_cast(a.ID), m_db.ErrStr()); - s->Reset(); - } - - if (message.Reactions.has_value()) { - auto &s = m_stmt_add_reaction; - for (size_t i = 0; i < message.Reactions->size(); i++) { - const auto &reaction = (*message.Reactions)[i]; - s->Bind(1, id); - s->Bind(2, reaction.Emoji.ID); - s->Bind(3, reaction.Emoji.Name); - s->Bind(4, reaction.Count); - s->Bind(5, reaction.HasReactedWith); - s->Bind(6, i); - if (!s->Insert()) - fprintf(stderr, "message reaction insert failed for %" PRIu64 "/%" PRIu64 "/%s: %s\n", static_cast(id), static_cast(reaction.Emoji.ID), reaction.Emoji.Name.c_str(), m_db.ErrStr()); - s->Reset(); - } - } - - if (message.Interaction.has_value()) - SetMessageInteractionPair(id, *message.Interaction); - - EndTransaction(); -} - -void Store::SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm) { - auto &s = m_stmt_set_perm; - - s->Bind(1, perm.ID); - s->Bind(2, channel_id); - s->Bind(3, perm.Type); - s->Bind(4, perm.Allow); - s->Bind(5, perm.Deny); - - if (!s->Insert()) - fprintf(stderr, "permission insert failed for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); - - s->Reset(); -} - -void Store::SetRole(Snowflake guild_id, const RoleData &role) { - auto &s = m_stmt_set_role; - - s->Bind(1, role.ID); - s->Bind(2, guild_id); - s->Bind(3, role.Name); - s->Bind(4, role.Color); - s->Bind(5, role.IsHoisted); - s->Bind(6, role.Position); - s->Bind(7, role.Permissions); - s->Bind(8, role.IsManaged); - s->Bind(9, role.IsMentionable); - - if (!s->Insert()) - fprintf(stderr, "role insert failed for %" PRIu64 ": %s\n", static_cast(role.ID), m_db.ErrStr()); - - s->Reset(); -} - -void Store::SetUser(Snowflake id, const UserData &user) { - auto &s = m_stmt_set_user; - - s->Bind(1, id); - s->Bind(2, user.Username); - s->Bind(3, user.Discriminator); - s->Bind(4, user.Avatar); - s->Bind(5, user.IsBot); - s->Bind(6, user.IsSystem); - s->Bind(7, user.IsMFAEnabled); - s->Bind(8, user.PremiumType); - s->Bind(9, user.PublicFlags); - - if (!s->Insert()) - fprintf(stderr, "user insert failed for %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - - s->Reset(); -} - -std::optional Store::GetBan(Snowflake guild_id, Snowflake user_id) const { - auto &s = m_stmt_get_ban; - - s->Bind(1, guild_id); - s->Bind(2, user_id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching ban for %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(guild_id), static_cast(user_id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - BanData r; - r.User.ID = user_id; - s->Get(2, r.Reason); - - s->Reset(); - - return r; -} - -std::vector Store::GetBans(Snowflake guild_id) const { - auto &s = m_stmt_get_bans; - - std::vector ret; - s->Bind(1, guild_id); - while (s->FetchOne()) { - auto &ban = ret.emplace_back(); - s->Get(1, ban.User.ID); - s->Get(2, ban.Reason); - } - - s->Reset(); - - return ret; -} - -std::vector Store::GetLastMessages(Snowflake id, size_t num) const { - auto &s = m_stmt_get_last_msgs; - std::vector msgs; - s->Bind(1, id); - s->Bind(2, num); - while (s->FetchOne()) { - auto msg = GetMessageBound(s); - msgs.push_back(std::move(msg)); - } - - s->Reset(); - - for (auto &msg : msgs) { - if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { - auto ref = GetMessage(*msg.MessageReference->MessageID); - if (ref.has_value()) - msg.ReferencedMessage = std::make_shared(std::move(*ref)); - } - } - - return msgs; -} - -std::vector Store::GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const { - std::vector msgs; - - auto &s = m_stmt_get_messages_before; - - s->Bind(1, channel_id); - s->Bind(2, message_id); - s->Bind(3, limit); - - while (s->FetchOne()) { - auto msg = GetMessageBound(s); - msgs.push_back(std::move(msg)); - } - - s->Reset(); - - for (auto &msg : msgs) { - if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { - auto ref = GetMessage(*msg.MessageReference->MessageID); - if (ref.has_value()) - msg.ReferencedMessage = std::make_shared(std::move(*ref)); - } - } - - return msgs; -} - -std::vector Store::GetPinnedMessages(Snowflake channel_id) const { - std::vector msgs; - - auto &s = m_stmt_get_pins; - - s->Bind(1, channel_id); - - while (s->FetchOne()) { - auto msg = GetMessageBound(s); - msgs.push_back(std::move(msg)); - } - - s->Reset(); - - for (auto &msg : msgs) { - if (msg.MessageReference.has_value() && msg.MessageReference->MessageID.has_value()) { - auto ref = GetMessage(*msg.MessageReference->MessageID); - if (ref.has_value()) - msg.ReferencedMessage = std::make_shared(std::move(*ref)); - } - } - - return msgs; -} - -std::vector Store::GetActiveThreads(Snowflake channel_id) const { - std::vector ret; - - auto &s = m_stmt_get_active_threads; - - s->Bind(1, channel_id); - while (s->FetchOne()) { - Snowflake x; - s->Get(0, x); - auto chan = GetChannel(x); - if (chan.has_value()) - ret.push_back(*chan); - } - - s->Reset(); - - return ret; -} - -void Store::AddReaction(const MessageReactionAddObject &data, bool byself) { - auto &s = m_stmt_add_reaction; - - s->Bind(1, data.MessageID); - s->Bind(2, data.Emoji.ID); - s->Bind(3, data.Emoji.Name); - s->Bind(4, 1); - if (byself) - s->Bind(5, true); - else - s->Bind(5); - s->Bind(6); - - if (!s->Insert()) - fprintf(stderr, "failed to add reaction for %" PRIu64 ": %s\n", static_cast(data.MessageID), m_db.ErrStr()); - - s->Reset(); -} - -void Store::RemoveReaction(const MessageReactionRemoveObject &data, bool byself) { - auto &s = m_stmt_sub_reaction; - - s->Bind(1, data.MessageID); - s->Bind(2, data.Emoji.ID); - s->Bind(3, data.Emoji.Name); - if (byself) - s->Bind(4, false); - else - s->Bind(4); - - if (!s->Insert()) - fprintf(stderr, "failed to remove reaction for %" PRIu64 ": %s\n", static_cast(data.MessageID), m_db.ErrStr()); - - s->Reset(); -} - -std::optional Store::GetChannel(Snowflake id) const { - auto &s = m_stmt_get_chan; - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching channel %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - ChannelData r; - - // uncomment as necessary - r.ID = id; - s->Get(1, r.Type); - s->Get(2, r.GuildID); - s->Get(3, r.Position); - s->Get(4, r.Name); - s->Get(5, r.Topic); - s->Get(6, r.IsNSFW); - s->Get(7, r.LastMessageID); - s->Get(10, r.RateLimitPerUser); - s->Get(12, r.OwnerID); - s->Get(14, r.ParentID); - if (!s->IsNull(16)) { - r.ThreadMetadata.emplace(); - s->Get(16, r.ThreadMetadata->IsArchived); - s->Get(17, r.ThreadMetadata->AutoArchiveDuration); - s->Get(18, r.ThreadMetadata->ArchiveTimestamp); - } - - s->Reset(); - - { - auto &s = m_stmt_get_recipients; - s->Bind(1, id); - std::vector recipients; - while (s->FetchOne()) { - auto &r = recipients.emplace_back(); - s->Get(0, r); - } - s->Reset(); - if (!recipients.empty()) - r.RecipientIDs = std::move(recipients); - } - - return r; -} - -std::optional Store::GetEmoji(Snowflake id) const { - auto &s = m_stmt_get_emoji; - - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching emoji %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - EmojiData r; - - r.ID = id; - s->Get(1, r.Name); - if (!s->IsNull(2)) { - r.Creator.emplace(); - s->Get(2, r.Creator->ID); - } - s->Get(3, r.NeedsColons); - s->Get(4, r.IsManaged); - s->Get(5, r.IsAnimated); - s->Get(6, r.IsAvailable); - - { - auto &s = m_stmt_get_emoji_roles; - - s->Bind(1, id); - r.Roles.emplace(); - while (s->FetchOne()) { - Snowflake id; - s->Get(0, id); - r.Roles->push_back(id); - } - s->Reset(); - } - - s->Reset(); - - return r; -} - -std::optional Store::GetGuild(Snowflake id) const { - auto &s = m_stmt_get_guild; - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching guild %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - // unfetched fields arent used anywhere - GuildData r; - r.ID = id; - s->Get(1, r.Name); - s->Get(2, r.Icon); - s->Get(5, r.OwnerID); - s->Get(20, r.IsUnavailable); - - s->Reset(); - - { - auto &s = m_stmt_get_guild_emojis; - - s->Bind(1, id); - r.Emojis.emplace(); - while (s->FetchOne()) { - auto &q = r.Emojis->emplace_back(); - s->Get(0, q.ID); - } - s->Reset(); - } - - { - auto &s = m_stmt_get_guild_features; - - s->Bind(1, id); - r.Features.emplace(); - while (s->FetchOne()) { - std::string feature; - s->Get(0, feature); - r.Features->insert(feature); - } - s->Reset(); - } - - { - auto &s = m_stmt_get_guild_chans; - s->Bind(1, id); - r.Channels.emplace(); - while (s->FetchOne()) { - auto &q = r.Channels->emplace_back(); - s->Get(0, q.ID); - } - s->Reset(); - } - - { - auto &s = m_stmt_get_threads; - s->Bind(1, id); - r.Threads.emplace(); - while (s->FetchOne()) { - auto &q = r.Threads->emplace_back(); - s->Get(0, q.ID); - } - s->Reset(); - } - - return r; -} - -std::optional Store::GetGuildMember(Snowflake guild_id, Snowflake user_id) const { - auto &s = m_stmt_get_member; - - s->Bind(1, user_id); - s->Bind(2, guild_id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching member %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(user_id), static_cast(guild_id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - GuildMember r; - r.User.emplace().ID = user_id; - s->Get(2, r.Nickname); - s->Get(3, r.JoinedAt); - s->Get(4, r.PremiumSince); - //s->Get(5, r.IsDeafened); - //s->Get(6, r.IsMuted); - s->Get(7, r.Avatar); - s->Get(8, r.IsPending); - - s->Reset(); - - { - auto &s = m_stmt_get_member_roles; - - s->Bind(1, user_id); - s->Bind(2, guild_id); - - while (s->FetchOne()) { - auto &f = r.Roles.emplace_back(); - s->Get(0, f); - } - - s->Reset(); - } - - return r; -} - -std::optional Store::GetMessage(Snowflake id) const { - auto &s = m_stmt_get_msg; - - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching message %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - auto top = GetMessageBound(s); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching message %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return top; - } - - auto ref = GetMessageBound(s); - top.ReferencedMessage = std::make_shared(std::move(ref)); - - s->Reset(); - - return top; -} - -Message Store::GetMessageBound(std::unique_ptr &s) const { - Message r; - - s->Get(0, r.ID); - s->Get(1, r.ChannelID); - s->Get(2, r.GuildID); - s->Get(3, r.Author.ID); - s->Get(4, r.Content); - s->Get(5, r.Timestamp); - s->Get(6, r.EditedTimestamp); - //s->Get(7, r.IsTTS); - //s->Get(8, r.DoesMentionEveryone); - s->GetJSON(9, r.Embeds); - s->Get(10, r.IsPinned); - s->Get(11, r.WebhookID); - s->Get(12, r.Type); - s->GetJSON(13, r.Application); - s->Get(14, r.Flags); - s->GetJSON(15, r.Stickers); - bool tmpb; - s->Get(16, tmpb); - if (tmpb) r.SetDeleted(); - s->Get(17, tmpb); - if (tmpb) r.SetEdited(); - s->Get(18, r.IsPending); - s->Get(19, r.Nonce); - s->GetJSON(20, r.StickerItems); - - if (!s->IsNull(21)) { - auto &i = r.Interaction.emplace(); - s->Get(21, i.ID); - s->Get(22, i.Name); - s->Get(23, i.Type); - s->Get(24, i.User.ID); - } - - if (!s->IsNull(25)) { - auto &a = r.Attachments.emplace_back(); - s->Get(25, a.ID); - s->Get(26, a.Filename); - s->Get(27, a.Bytes); - s->Get(28, a.URL); - s->Get(29, a.ProxyURL); - s->Get(30, a.Height); - s->Get(31, a.Width); - } - - if (!s->IsNull(32)) { - auto &q = r.MessageReference.emplace(); - s->Get(32, q.MessageID); - s->Get(33, q.ChannelID); - s->Get(34, q.GuildID); - } - - { - auto &s = m_stmt_get_mentions; - s->Bind(1, r.ID); - while (s->FetchOne()) { - Snowflake id; - s->Get(0, id); - auto user = GetUser(id); - if (user.has_value()) - r.Mentions.push_back(std::move(*user)); - } - s->Reset(); - } - - { - auto &s = m_stmt_get_reactions; - s->Bind(1, r.ID); - std::map tmp; - while (s->FetchOne()) { - size_t idx; - ReactionData q; - s->Get(0, q.Emoji.ID); - s->Get(1, q.Emoji.Name); - s->Get(2, q.Count); - s->Get(3, q.HasReactedWith); - s->Get(4, idx); - tmp[idx] = q; - } - s->Reset(); - - r.Reactions.emplace(); - for (const auto &[idx, reaction] : tmp) - r.Reactions->push_back(reaction); - } - - return r; -} - -std::optional Store::GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const { - auto &s = m_stmt_get_perm; - - s->Bind(1, id); - s->Bind(2, channel_id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "failed while fetching permission %" PRIu64 "/%" PRIu64 ": %s\n", static_cast(channel_id), static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - PermissionOverwrite r; - r.ID = id; - s->Get(2, r.Type); - s->Get(3, r.Allow); - s->Get(4, r.Deny); - - s->Reset(); - - return r; -} - -std::optional Store::GetRole(Snowflake id) const { - auto &s = m_stmt_get_role; - - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching role %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - RoleData r; - - r.ID = id; - //s->Get(1, guild id); - s->Get(2, r.Name); - s->Get(3, r.Color); - s->Get(4, r.IsHoisted); - s->Get(5, r.Position); - s->Get(6, r.Permissions); - s->Get(7, r.IsManaged); - s->Get(8, r.IsMentionable); - - s->Reset(); - - return r; -} - -std::optional Store::GetUser(Snowflake id) const { - auto &s = m_stmt_get_user; - s->Bind(1, id); - if (!s->FetchOne()) { - if (m_db.Error() != SQLITE_DONE) - fprintf(stderr, "error while fetching user %" PRIu64 ": %s\n", static_cast(id), m_db.ErrStr()); - s->Reset(); - return {}; - } - - UserData r; - - r.ID = id; - s->Get(1, r.Username); - s->Get(2, r.Discriminator); - s->Get(3, r.Avatar); - s->Get(4, r.IsBot); - s->Get(5, r.IsSystem); - s->Get(6, r.IsMFAEnabled); - s->Get(7, r.PremiumType); - s->Get(8, r.PublicFlags); - - s->Reset(); - - return r; -} - -void Store::ClearGuild(Snowflake id) { - auto &s = m_stmt_clr_guild; - - s->Bind(1, id); - s->Step(); - s->Reset(); -} - -void Store::ClearChannel(Snowflake id) { - auto &s = m_stmt_clr_chan; - - s->Bind(1, id); - s->Step(); - s->Reset(); -} - -void Store::ClearBan(Snowflake guild_id, Snowflake user_id) { - auto &s = m_stmt_clr_ban; - - s->Bind(1, guild_id); - s->Bind(2, user_id); - s->Step(); - s->Reset(); -} - -void Store::ClearRecipient(Snowflake channel_id, Snowflake user_id) { - auto &s = m_stmt_clr_recipient; - - s->Bind(1, channel_id); - s->Bind(2, user_id); - s->Step(); - s->Reset(); -} - -std::unordered_set Store::GetChannels() const { - auto &s = m_stmt_get_chan_ids; - std::unordered_set r; - - while (s->FetchOne()) { - Snowflake id; - s->Get(0, id); - r.insert(id); - } - - s->Reset(); - - return r; -} - -std::unordered_set Store::GetGuilds() const { - auto &s = m_stmt_get_guild_ids; - std::unordered_set r; - - while (s->FetchOne()) { - Snowflake id; - s->Get(0, id); - r.insert(id); - } - - s->Reset(); - - return r; -} - -void Store::ClearAll() { - if (m_db.Execute(R"( - DELETE FROM attachments; - DELETE FROM bans; - DELETE FROM channels; - DELETE FROM emojis; - DELETE FROM emoji_roles; - DELETE FROM guild_emojis; - DELETE FROM guild_features; - DELETE FROM guilds; - DELETE FROM members; - DELETE FROM member_roles; - DELETE FROM mentions; - DELETE FROM message_interactions; - DELETE FROM message_references; - DELETE FROM messages; - DELETE FROM permissions; - DELETE FROM reactions; - DELETE FROM recipients; - DELETE FROM roles; - DELETE FROM threads; - DELETE FROM users; - )") != SQLITE_OK) { - fprintf(stderr, "failed to clear: %s\n", m_db.ErrStr()); - } -} - -void Store::BeginTransaction() { - m_db.StartTransaction(); -} - -void Store::EndTransaction() { - m_db.EndTransaction(); -} - -bool Store::CreateTables() { - const char *create_users = R"( - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY, - username TEXT NOT NULL, - discriminator TEXT NOT NULL, - avatar TEXT, - bot BOOL, - system BOOL, - mfa BOOL, - premium INTEGER, - pubflags INTEGER - ) - )"; - - const char *create_permissions = R"( - CREATE TABLE IF NOT EXISTS permissions ( - id INTEGER NOT NULL, - channel_id INTEGER NOT NULL, - type INTEGER NOT NULL, - allow INTEGER NOT NULL, - deny INTEGER NOT NULL, - PRIMARY KEY(id, channel_id) - ) - )"; - - const char *create_messages = R"( - CREATE TABLE IF NOT EXISTS messages ( - id INTEGER PRIMARY KEY, - channel_id INTEGER NOT NULL, - guild_id INTEGER, - author_id INTEGER NOT NULL, - content TEXT NOT NULL, - timestamp TEXT NOT NULL, - edited_timestamp TEXT, - tts BOOL NOT NULL, - everyone BOOL NOT NULL, - embeds TEXT NOT NULL, /* json */ - pinned BOOL, - webhook_id INTEGER, - type INTEGER, - application TEXT, /* json */ - flags INTEGER, - stickers TEXT, /* json */ - deleted BOOL, /* extra */ - edited BOOL, /* extra */ - pending BOOL, /* extra */ - nonce TEXT, - sticker_items TEXT /* json */ - ) - )"; - - const char *create_roles = R"( - CREATE TABLE IF NOT EXISTS roles ( - id INTEGER PRIMARY KEY, - guild INTEGER NOT NULL, - name TEXT NOT NULL, - color INTEGER NOT NULL, - hoisted BOOL NOT NULL, - position INTEGER NOT NULL, - permissions INTEGER NOT NULL, - managed BOOL NOT NULL, - mentionable BOOL NOT NULL - ) - )"; - - const char *create_emojis = R"( - CREATE TABLE IF NOT EXISTS emojis ( - id INTEGER PRIMARY KEY, /*though nullable, only custom emojis (with non-null ids) are stored*/ - name TEXT NOT NULL, /*same as id*/ - creator_id INTEGER, - colons BOOL, - managed BOOL, - animated BOOL, - available BOOL - ) - )"; - - const char *create_members = R"( - CREATE TABLE IF NOT EXISTS members ( - user_id INTEGER NOT NULL, - guild_id INTEGER NOT NULL, - nickname TEXT, - joined_at TEXT NOT NULL, - premium_since TEXT, - deaf BOOL NOT NULL, - mute BOOL NOT NULL, - avatar TEXT, - pending BOOL, - PRIMARY KEY(user_id, guild_id) - ) - )"; - - const char *create_guilds = R"( - CREATE TABLE IF NOT EXISTS guilds ( - id INTEGER PRIMARY KEY, - name TEXT NOT NULL, - icon TEXT NOT NULL, - splash TEXT, - owner BOOL, - owner_id INTEGER NOT NULL, - permissions INTEGER, /* new */ - voice_region TEXT, - afk_id INTEGER, - afk_timeout INTEGER NOT NULL, - verification INTEGER NOT NULL, - notifications INTEGER NOT NULL, - mfa INTEGER NOT NULL, - application INTEGER, - widget BOOL, - widget_channel INTEGER, - system_flags INTEGER NOT NULL, - rules_channel INTEGER, - joined_at TEXT, - large BOOL, - unavailable BOOL, - member_count INTEGER, - max_presences INTEGER, - max_members INTEGER, - vanity TEXT, - description TEXT, - banner_hash TEXT, - premium_tier INTEGER NOT NULL, - premium_count INTEGER, - locale TEXT NOT NULL, - public_updates_id INTEGER, - max_video_users INTEGER, - approx_members INTEGER, - approx_presences INTEGER, - lazy BOOL - ) - )"; - - const char *create_channels = R"( - CREATE TABLE IF NOT EXISTS channels ( - id INTEGER PRIMARY KEY, - type INTEGER NOT NULL, - guild_id INTEGER, - position INTEGER, - name TEXT, - topic TEXT, - is_nsfw BOOL, - last_message_id INTEGER, - bitrate INTEGER, - user_limit INTEGER, - rate_limit INTEGER, - icon TEXT, - owner_id INTEGER, - application_id INTEGER, - parent_id INTEGER, - last_pin_timestamp TEXT, - archived BOOL, /* threads */ - auto_archive INTEGER, /* threads */ - archived_ts TEXT /* threads */ - ) - )"; - - const char *create_bans = R"( - CREATE TABLE IF NOT EXISTS bans ( - guild_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, - reason TEXT, - PRIMARY KEY(user_id, guild_id) - ) - )"; - - const char *create_interactions = R"( - CREATE TABLE IF NOT EXISTS message_interactions ( - message_id INTEGER NOT NULL, - interaction_id INTEGER NOT NULL, - type INTEGER NOT NULL, - name STRING NOT NULL, - user_id INTEGER NOT NULL, - PRIMARY KEY(message_id) - ) - )"; - - const char *create_references = R"( - CREATE TABLE IF NOT EXISTS message_references ( - id INTEGER NOT NULL, - message INTEGER, - channel INTEGER, - guild INTEGER, - PRIMARY KEY(id) - ) - )"; - - const char *create_member_roles = R"( - CREATE TABLE IF NOT EXISTS member_roles ( - user INTEGER NOT NULL, - role INTEGER NOT NULL, - PRIMARY KEY(user, role) - ) - )"; - - const char *create_guild_emojis = R"( - CREATE TABLE IF NOT EXISTS guild_emojis ( - guild INTEGER NOT NULL, - emoji INTEGER NOT NULL, - PRIMARY KEY(guild, emoji) - ) - )"; - - const char *create_guild_features = R"( - CREATE TABLE IF NOT EXISTS guild_features ( - guild INTEGER NOT NULL, - feature CHAR(63) NOT NULL, - PRIMARY KEY(guild, feature) - ) - )"; - - const char *create_threads = R"( - CREATE TABLE IF NOT EXISTS threads ( - guild INTEGER NOT NULL, - id INTEGER NOT NULL, - PRIMARY KEY(guild, id) - ) - )"; - - const char *create_emoji_roles = R"( - CREATE TABLE IF NOT EXISTS emoji_roles ( - emoji INTEGER NOT NULL, - role INTEGER NOT NULL, - PRIMARY KEY(emoji, role) - ) - )"; - - const char *create_mentions = R"( - CREATE TABLE IF NOT EXISTS mentions ( - message INTEGER NOT NULL, - user INTEGER NOT NULL, - PRIMARY KEY(message, user) - ) - )"; - - const char *create_attachments = R"( - CREATE TABLE IF NOT EXISTS attachments ( - message INTEGER NOT NULL, - id INTEGER NOT NULL, - filename TEXT NOT NULL, - size INTEGER NOT NULL, - url TEXT NOT NULL, - proxy TEXT NOT NULL, - height INTEGER, - width INTEGER, - PRIMARY KEY(message, id) - ) - )"; - - const char *create_recipients = R"( - CREATE TABLE IF NOT EXISTS recipients ( - channel INTEGER NOT NULL, - user INTEGER NOT NULL, - PRIMARY KEY(channel, user) - ) - )"; - - const char *create_reactions = R"( - CREATE TABLE IF NOT EXISTS reactions ( - message INTEGER NOT NULL, - emoji_id INTEGER, - name TEXT NOT NULL, - count INTEGER NOT NULL, - me BOOL NOT NULL, - idx INTEGER NOT NULL, - PRIMARY KEY(message, emoji_id, name) - ) - )"; - - if (m_db.Execute(create_users) != SQLITE_OK) { - fprintf(stderr, "failed to create user table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_permissions) != SQLITE_OK) { - fprintf(stderr, "failed to create permissions table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_messages) != SQLITE_OK) { - fprintf(stderr, "failed to create messages table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_roles) != SQLITE_OK) { - fprintf(stderr, "failed to create roles table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_emojis) != SQLITE_OK) { - fprintf(stderr, "failed to create emojis table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_members) != SQLITE_OK) { - fprintf(stderr, "failed to create members table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_guilds) != SQLITE_OK) { - fprintf(stderr, "failed to create guilds table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_channels) != SQLITE_OK) { - fprintf(stderr, "failed to create channels table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_bans) != SQLITE_OK) { - fprintf(stderr, "failed to create bans table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_interactions) != SQLITE_OK) { - fprintf(stderr, "failed to create interactions table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_references) != SQLITE_OK) { - fprintf(stderr, "failed to create references table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_member_roles) != SQLITE_OK) { - fprintf(stderr, "failed to create member roles table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_guild_emojis) != SQLITE_OK) { - fprintf(stderr, "failed to create guild emojis table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_guild_features) != SQLITE_OK) { - fprintf(stderr, "failed to create guild features table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_threads) != SQLITE_OK) { - fprintf(stderr, "failed to create threads table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_emoji_roles) != SQLITE_OK) { - fprintf(stderr, "failed to create emoji roles table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_mentions) != SQLITE_OK) { - fprintf(stderr, "failed to create mentions table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_attachments) != SQLITE_OK) { - fprintf(stderr, "failed to create attachments table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_recipients) != SQLITE_OK) { - fprintf(stderr, "failed to create recipients table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(create_reactions) != SQLITE_OK) { - fprintf(stderr, "failed to create reactions table: %s\n", m_db.ErrStr()); - return false; - } - - if (m_db.Execute(R"( - CREATE TRIGGER remove_zero_reactions AFTER UPDATE ON reactions WHEN new.count = 0 - BEGIN - DELETE FROM reactions WHERE message = new.message AND emoji_id = new.emoji_id AND name = new.name; - END - )") != SQLITE_OK) { - fprintf(stderr, "failed to create reactions trigger: %s\n", m_db.ErrStr()); - return false; - } - - return true; -} - -bool Store::CreateStatements() { - m_stmt_set_guild = std::make_unique(m_db, R"( - REPLACE INTO guilds VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_guild->OK()) { - fprintf(stderr, "failed to prepare set guild statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_guild = std::make_unique(m_db, R"( - SELECT * FROM guilds WHERE id = ? - )"); - if (!m_stmt_get_guild->OK()) { - fprintf(stderr, "failed to prepare get guild statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_guild_ids = std::make_unique(m_db, R"( - SELECT id FROM guilds - )"); - if (!m_stmt_get_guild_ids->OK()) { - fprintf(stderr, "failed to prepare get guild ids statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_clr_guild = std::make_unique(m_db, R"( - DELETE FROM guilds WHERE id = ? - )"); - if (!m_stmt_clr_guild->OK()) { - fprintf(stderr, "failed to prepare clear guild statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_chan = std::make_unique(m_db, R"( - REPLACE INTO channels VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_chan->OK()) { - fprintf(stderr, "failed to prepare set channel statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_chan = std::make_unique(m_db, R"( - SELECT * FROM channels WHERE id = ? - )"); - if (!m_stmt_get_chan->OK()) { - fprintf(stderr, "failed to prepare get channel statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_chan_ids = std::make_unique(m_db, R"( - SELECT id FROM channels - )"); - if (!m_stmt_get_chan_ids->OK()) { - fprintf(stderr, "failed to prepare get channel ids statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_clr_chan = std::make_unique(m_db, R"( - DELETE FROM channels WHERE id = ? - )"); - if (!m_stmt_clr_chan->OK()) { - fprintf(stderr, "failed to prepare clear channel statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_msg = std::make_unique(m_db, R"( - REPLACE INTO messages VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_msg->OK()) { - fprintf(stderr, "failed to prepare set message statement: %s\n", m_db.ErrStr()); - return false; - } - - // wew - m_stmt_get_msg = std::make_unique(m_db, R"( - SELECT messages.*, - message_interactions.interaction_id, - message_interactions.name, - message_interactions.type, - message_interactions.user_id, - attachments.id, - attachments.filename, - attachments.size, - attachments.url, - attachments.proxy, - attachments.height, - attachments.width, - message_references.message, - message_references.channel, - message_references.guild - FROM messages - LEFT OUTER JOIN - message_interactions - ON messages.id = message_interactions.message_id - LEFT OUTER JOIN - attachments - ON messages.id = attachments.message - LEFT OUTER JOIN - message_references - ON messages.id = message_references.id - WHERE messages.id = ?1 - UNION ALL - SELECT messages.*, - message_interactions.interaction_id, - message_interactions.name, - message_interactions.type, - message_interactions.user_id, - attachments.id, - attachments.filename, - attachments.size, - attachments.url, - attachments.proxy, - attachments.height, - attachments.width, - message_references.message, - message_references.channel, - message_references.guild - FROM messages - LEFT OUTER JOIN - message_interactions - ON messages.id = message_interactions.message_id - LEFT OUTER JOIN - attachments - ON messages.id = attachments.message - LEFT OUTER JOIN - message_references - ON messages.id = message_references.id - WHERE messages.id = (SELECT message FROM message_references WHERE id = ?1) - ORDER BY messages.id DESC - )"); - if (!m_stmt_get_msg->OK()) { - fprintf(stderr, "failed to prepare get message statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_msg_ref = std::make_unique(m_db, R"( - REPLACE INTO message_references VALUES ( - ?, ?, ?, ? - ); - )"); - if (!m_stmt_set_msg_ref->OK()) { - fprintf(stderr, "failed to prepare set message reference statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_last_msgs = std::make_unique(m_db, R"( - SELECT * FROM ( - SELECT messages.*, - message_interactions.interaction_id, - message_interactions.name, - message_interactions.type, - message_interactions.user_id, - attachments.id, - attachments.filename, - attachments.size, - attachments.url, - attachments.proxy, - attachments.height, - attachments.width, - message_references.message, - message_references.channel, - message_references.guild - FROM messages - LEFT OUTER JOIN - message_interactions - ON messages.id = message_interactions.message_id - LEFT OUTER JOIN - attachments - ON messages.id = attachments.message - LEFT OUTER JOIN - message_references - ON messages.id = message_references.id - WHERE channel_id = ? AND pending = 0 ORDER BY id DESC LIMIT ? - ) ORDER BY id ASC - )"); - if (!m_stmt_get_last_msgs->OK()) { - fprintf(stderr, "failed to prepare get last messages statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_user = std::make_unique(m_db, R"( - REPLACE INTO users VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_user->OK()) { - fprintf(stderr, "failed to prepare set user statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_user = std::make_unique(m_db, R"( - SELECT * FROM users WHERE id = ? - )"); - if (!m_stmt_get_user->OK()) { - fprintf(stderr, "failed to prepare get user statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_member = std::make_unique(m_db, R"( - REPLACE INTO members VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_member->OK()) { - fprintf(stderr, "failed to prepare set member statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_member = std::make_unique(m_db, R"( - SELECT * FROM members WHERE user_id = ? AND guild_id = ? - )"); - if (!m_stmt_get_member->OK()) { - fprintf(stderr, "failed to prepare get member statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_role = std::make_unique(m_db, R"( - REPLACE INTO roles VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_role->OK()) { - fprintf(stderr, "failed to prepare set role statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_role = std::make_unique(m_db, R"( - SELECT * FROM roles WHERE id = ? - )"); - if (!m_stmt_get_role->OK()) { - fprintf(stderr, "failed to prepare get role statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_emoji = std::make_unique(m_db, R"( - REPLACE INTO emojis VALUES ( - ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_emoji->OK()) { - fprintf(stderr, "failed to prepare set emoji statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_emoji = std::make_unique(m_db, R"( - SELECT * FROM emojis WHERE id = ? - )"); - if (!m_stmt_get_emoji->OK()) { - fprintf(stderr, "failed to prepare get emoji statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_perm = std::make_unique(m_db, R"( - REPLACE INTO permissions VALUES ( - ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_perm->OK()) { - fprintf(stderr, "failed to prepare set permission statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_perm = std::make_unique(m_db, R"( - SELECT * FROM permissions WHERE id = ? AND channel_id = ? - )"); - if (!m_stmt_get_perm->OK()) { - fprintf(stderr, "failed to prepare get permission statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_ban = std::make_unique(m_db, R"( - REPLACE INTO bans VALUES ( - ?, ?, ? - ) - )"); - if (!m_stmt_set_ban->OK()) { - fprintf(stderr, "failed to prepare set ban statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_ban = std::make_unique(m_db, R"( - SELECT * FROM bans WHERE guild_id = ? AND user_id = ? - )"); - if (!m_stmt_get_ban->OK()) { - fprintf(stderr, "failed to prepare get ban statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_bans = std::make_unique(m_db, R"( - SELECT * FROM bans WHERE guild_id = ? - )"); - if (!m_stmt_get_bans->OK()) { - fprintf(stderr, "failed to prepare get bans statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_clr_ban = std::make_unique(m_db, R"( - DELETE FROM bans WHERE guild_id = ? AND user_id = ? - )"); - if (!m_stmt_clr_ban->OK()) { - fprintf(stderr, "failed to prepare clear ban statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_interaction = std::make_unique(m_db, R"( - REPLACE INTO message_interactions VALUES ( - ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_interaction->OK()) { - fprintf(stderr, "failed to prepare set interaction statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_member_roles = std::make_unique(m_db, R"( - REPLACE INTO member_roles VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_member_roles->OK()) { - fprintf(stderr, "faile to prepare set member roles statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_member_roles = std::make_unique(m_db, R"( - SELECT id FROM roles, member_roles - WHERE roles.id = member_roles.role - AND member_roles.user = ? - AND roles.guild = ? - )"); - if (!m_stmt_get_member_roles->OK()) { - fprintf(stderr, "failed to prepare get member role statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_guild_emoji = std::make_unique(m_db, R"( - REPLACE INTO guild_emojis VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_guild_emoji->OK()) { - fprintf(stderr, "failed to prepare set guild emoji statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_guild_emojis = std::make_unique(m_db, R"( - SELECT emoji FROM guild_emojis WHERE guild = ? - )"); - if (!m_stmt_get_guild_emojis->OK()) { - fprintf(stderr, "failed to prepare get guild emojis statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_clr_guild_emoji = std::make_unique(m_db, R"( - DELETE FROM guild_emojis WHERE guild = ? AND emoji = ? - )"); - if (!m_stmt_clr_guild_emoji->OK()) { - fprintf(stderr, "failed to prepare clear guild emoji statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_guild_feature = std::make_unique(m_db, R"( - REPLACE INTO guild_features VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_guild_feature->OK()) { - fprintf(stderr, "failed to prepare set guild feature statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_guild_features = std::make_unique(m_db, R"( - SELECT feature FROM guild_features WHERE guild = ? - )"); - if (!m_stmt_get_guild_features->OK()) { - fprintf(stderr, "failed to prepare get guild features statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_guild_chans = std::make_unique(m_db, R"( - SELECT id FROM channels WHERE guild_id = ? - )"); - if (!m_stmt_get_guild_chans->OK()) { - fprintf(stderr, "failed to prepare get guild channels statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_thread = std::make_unique(m_db, R"( - REPLACE INTO threads VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_thread->OK()) { - fprintf(stderr, "failed to prepare set thread statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_threads = std::make_unique(m_db, R"( - SELECT id FROM threads WHERE guild = ? - )"); - if (!m_stmt_get_threads->OK()) { - fprintf(stderr, "failed to prepare get threads statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_active_threads = std::make_unique(m_db, R"( - SELECT id FROM channels WHERE parent_id = ? AND (type = 10 OR type = 11 OR type = 12) AND archived = FALSE - )"); - if (!m_stmt_get_active_threads->OK()) { - fprintf(stderr, "faile to prepare get active threads statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_messages_before = std::make_unique(m_db, R"( - SELECT * FROM ( - SELECT messages.*, - message_interactions.interaction_id, - message_interactions.name, - message_interactions.type, - message_interactions.user_id, - attachments.id, - attachments.filename, - attachments.size, - attachments.url, - attachments.proxy, - attachments.height, - attachments.width, - message_references.message - FROM messages - LEFT OUTER JOIN - message_interactions - ON messages.id = message_interactions.message_id - LEFT OUTER JOIN - attachments - ON messages.id = attachments.message - LEFT OUTER JOIN - message_references - ON messages.id = message_references.id - WHERE channel_id = ? AND pending = 0 AND messages.id < ? ORDER BY id DESC LIMIT ? - ) ORDER BY id ASC - )"); - if (!m_stmt_get_messages_before->OK()) { - fprintf(stderr, "failed to prepare get messages before statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_pins = std::make_unique(m_db, R"( - SELECT messages.*, - message_interactions.interaction_id, - message_interactions.name, - message_interactions.type, - message_interactions.user_id, - attachments.id, - attachments.filename, - attachments.size, - attachments.url, - attachments.proxy, - attachments.height, - attachments.width, - message_references.message, - message_references.channel, - message_references.guild - FROM messages - LEFT OUTER JOIN - message_interactions - ON messages.id = message_interactions.message_id - LEFT OUTER JOIN - attachments - ON messages.id = attachments.message - LEFT OUTER JOIN - message_references - ON messages.id = message_references.id - WHERE channel_id = ? AND pinned = 1 ORDER BY id ASC - )"); - if (!m_stmt_get_pins->OK()) { - fprintf(stderr, "failed to prepare get pins statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_emoji_role = std::make_unique(m_db, R"( - REPLACE INTO emoji_roles VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_emoji_role->OK()) { - fprintf(stderr, "failed to prepare set emoji role statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_emoji_roles = std::make_unique(m_db, R"( - SELECT role FROM emoji_roles WHERE emoji = ? - )"); - if (!m_stmt_get_emoji_roles->OK()) { - fprintf(stderr, "failed to prepare get emoji role statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_mention = std::make_unique(m_db, R"( - REPLACE INTO mentions VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_mention->OK()) { - fprintf(stderr, "failed to prepare set mention statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_mentions = std::make_unique(m_db, R"( - SELECT user FROM mentions WHERE message = ? - )"); - if (!m_stmt_get_mentions->OK()) { - fprintf(stderr, "failed to prepare get mentions statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_attachment = std::make_unique(m_db, R"( - REPLACE INTO attachments VALUES ( - ?, ?, ?, ?, ?, ?, ?, ? - ) - )"); - if (!m_stmt_set_attachment->OK()) { - fprintf(stderr, "failed to prepare set attachment statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_attachments = std::make_unique(m_db, R"( - SELECT * FROM attachments WHERE message = ? - )"); - if (!m_stmt_get_attachments->OK()) { - fprintf(stderr, "failed to prepare get attachments statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_set_recipient = std::make_unique(m_db, R"( - REPLACE INTO recipients VALUES ( - ?, ? - ) - )"); - if (!m_stmt_set_recipient->OK()) { - fprintf(stderr, "failed to prepare set recipient statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_recipients = std::make_unique(m_db, R"( - SELECT user FROM recipients WHERE channel = ? - )"); - if (!m_stmt_get_recipients->OK()) { - fprintf(stderr, "failed to prepare get recipients statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_clr_recipient = std::make_unique(m_db, R"( - DELETE FROM recipients WHERE channel = ? AND user = ? - )"); - if (!m_stmt_clr_recipient->OK()) { - fprintf(stderr, "failed to prepare clear recipient statement: %s\n", m_db.ErrStr()); - return false; - } - - // probably not the best way to do this lol but i just want one statement i guess - m_stmt_add_reaction = std::make_unique(m_db, R"( - INSERT OR REPLACE INTO reactions VALUES ( - ?1, ?2, ?3, - COALESCE( - (SELECT count FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), - 0 - ) + ?4, - COALESCE( - ?5, - (SELECT me FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), - false - ), - COALESCE( - ?6, - (SELECT idx FROM reactions WHERE message = ?1 AND emoji_id = ?2 AND name = ?3), - (SELECT MAX(idx) + 1 FROM reactions WHERE message = ?1), - 0 - ) - ) - )"); - if (!m_stmt_add_reaction->OK()) { - fprintf(stderr, "failed to prepare add reaction statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_sub_reaction = std::make_unique(m_db, R"( - UPDATE reactions - SET count = count - 1, - me = COALESCE(?4, me) - WHERE message = ?1 AND emoji_id = ?2 AND name = ?3 - )"); - if (!m_stmt_sub_reaction->OK()) { - fprintf(stderr, "failed to prepare sub reaction statement: %s\n", m_db.ErrStr()); - return false; - } - - m_stmt_get_reactions = std::make_unique(m_db, R"( - SELECT emoji_id, name, count, me, idx FROM reactions WHERE message = ? - )"); - if (!m_stmt_get_reactions->OK()) { - fprintf(stderr, "failed to prepare get reactions statement: %s\n", m_db.ErrStr()); - return false; - } - - return true; -} - -Store::Database::Database(const char *path) { - m_err = sqlite3_open(path, &m_db); -} - -Store::Database::~Database() { - Close(); -} - -int Store::Database::Close() { - if (m_db == nullptr) return m_err; - m_signal_close.emit(); - m_err = sqlite3_close(m_db); - m_db = nullptr; - return m_err; -} - -int Store::Database::StartTransaction() { - return m_err = Execute("BEGIN TRANSACTION"); -} - -int Store::Database::EndTransaction() { - return m_err = Execute("COMMIT"); -} - -int Store::Database::Execute(const char *command) { - return m_err = sqlite3_exec(m_db, command, nullptr, nullptr, nullptr); -} - -int Store::Database::Error() const { - return m_err; -} - -bool Store::Database::OK() const { - return Error() == SQLITE_OK; -} - -const char *Store::Database::ErrStr() const { - const char *errstr = sqlite3_errstr(m_err); - const char *errmsg = sqlite3_errmsg(m_db); - std::string tmp = errstr + std::string("\n\t") + errmsg; - tmp.copy(m_err_scratch, sizeof(m_err_scratch) - 1); - m_err_scratch[std::min(tmp.size(), sizeof(m_err_scratch) - 1)] = '\0'; - return m_err_scratch; -} - -int Store::Database::SetError(int err) { - return m_err = err; -} - -sqlite3 *Store::Database::obj() { - return m_db; -} - -Store::Database::type_signal_close Store::Database::signal_close() { - return m_signal_close; -} - -Store::Statement::Statement(Database &db, const char *command) - : m_db(&db) { - if (m_db->SetError(sqlite3_prepare_v2(m_db->obj(), command, -1, &m_stmt, nullptr)) != SQLITE_OK) return; - std::string tmp = command; - m_db->signal_close().connect([tmp, this] { - sqlite3_finalize(m_stmt); - m_stmt = nullptr; - }); -} - -Store::Statement::~Statement() { - sqlite3_finalize(m_stmt); -} - -bool Store::Statement::OK() const { - return m_stmt != nullptr; -} - -int Store::Statement::Bind(int index, Snowflake id) { - return Bind(index, static_cast(id)); -} - -int Store::Statement::Bind(int index, const char *str, size_t len) { - if (len == -1) len = strlen(str); - return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str, len, SQLITE_TRANSIENT)); -} - -int Store::Statement::Bind(int index, const std::string &str) { - return m_db->SetError(sqlite3_bind_blob(m_stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT)); -} - -int Store::Statement::Bind(int index) { - return m_db->SetError(sqlite3_bind_null(m_stmt, index)); -} - -void Store::Statement::Get(int index, Snowflake &out) const { - out = static_cast(sqlite3_column_int64(m_stmt, index)); -} - -void Store::Statement::Get(int index, std::string &out) const { - const unsigned char *ptr = sqlite3_column_text(m_stmt, index); - if (ptr == nullptr) - out = ""; - else - out = reinterpret_cast(ptr); -} - -bool Store::Statement::IsNull(int index) const { - return sqlite3_column_type(m_stmt, index) == SQLITE_NULL; -} - -int Store::Statement::Step() { - return m_db->SetError(sqlite3_step(m_stmt)); -} - -bool Store::Statement::Insert() { - return m_db->SetError(sqlite3_step(m_stmt)) == SQLITE_DONE; -} - -bool Store::Statement::FetchOne() { - return m_db->SetError(sqlite3_step(m_stmt)) == SQLITE_ROW; -} - -int Store::Statement::Reset() { - if (m_db->SetError(sqlite3_reset(m_stmt)) != SQLITE_OK) - return m_db->Error(); - if (m_db->SetError(sqlite3_clear_bindings(m_stmt)) != SQLITE_OK) - return m_db->Error(); - return m_db->Error(); -} - -sqlite3_stmt *Store::Statement::obj() { - return m_stmt; -} diff --git a/discord/store.hpp b/discord/store.hpp deleted file mode 100644 index 80e2407..0000000 --- a/discord/store.hpp +++ /dev/null @@ -1,302 +0,0 @@ -#pragma once -#include "util.hpp" -#include "objects.hpp" -#include -#include -#include -#include -#include - -#ifdef GetMessage // fuck you windows.h - #undef GetMessage -#endif - -class Store { -public: - Store(bool mem_store = false); - ~Store(); - - bool IsValid() const; - - void SetUser(Snowflake id, const UserData &user); - void SetChannel(Snowflake id, const ChannelData &chan); - void SetGuild(Snowflake id, const GuildData &guild); - void SetRole(Snowflake guild_id, const RoleData &role); - void SetMessage(Snowflake id, const Message &message); - void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data); - void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm); - void SetEmoji(Snowflake id, const EmojiData &emoji); - void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban); - - std::optional GetChannel(Snowflake id) const; - std::optional GetEmoji(Snowflake id) const; - std::optional GetGuild(Snowflake id) const; - std::optional GetGuildMember(Snowflake guild_id, Snowflake user_id) const; - std::optional GetMessage(Snowflake id) const; - std::optional GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const; - std::optional GetRole(Snowflake id) const; - std::optional GetUser(Snowflake id) const; - std::optional GetBan(Snowflake guild_id, Snowflake user_id) const; - std::vector GetBans(Snowflake guild_id) const; - - std::vector GetLastMessages(Snowflake id, size_t num) const; - std::vector GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const; - std::vector GetPinnedMessages(Snowflake channel_id) const; - std::vector GetActiveThreads(Snowflake channel_id) const; // public - - void AddReaction(const MessageReactionAddObject &data, bool byself); - void RemoveReaction(const MessageReactionRemoveObject &data, bool byself); - - void ClearGuild(Snowflake id); - void ClearChannel(Snowflake id); - void ClearBan(Snowflake guild_id, Snowflake user_id); - void ClearRecipient(Snowflake channel_id, Snowflake user_id); - - std::unordered_set GetChannels() const; - std::unordered_set GetGuilds() const; - - void ClearAll(); - - void BeginTransaction(); - void EndTransaction(); - -private: - class Statement; - class Database { - public: - Database(const char *path); - ~Database(); - - int Close(); - int StartTransaction(); - int EndTransaction(); - int Execute(const char *command); - int Error() const; - bool OK() const; - const char *ErrStr() const; - int SetError(int err); - sqlite3 *obj(); - - private: - sqlite3 *m_db; - int m_err = SQLITE_OK; - mutable char m_err_scratch[256] { 0 }; - - // stupid shit i dont like to allow closing properly - using type_signal_close = sigc::signal; - type_signal_close m_signal_close; - - public: - type_signal_close signal_close(); - }; - - class Statement { - public: - Statement() = delete; - Statement(const Statement &other) = delete; - Statement(Database &db, const char *command); - ~Statement(); - Statement &operator=(Statement &other) = delete; - - bool OK() const; - - int Bind(int index, Snowflake id); - int Bind(int index, const char *str, size_t len = -1); - int Bind(int index, const std::string &str); - int Bind(int index); - - template - int Bind(int index, std::optional opt) { - if (opt.has_value()) - return Bind(index, opt.value()); - else - return Bind(index); - } - - template - int BindIDsAsJSON(int index, Iter start, Iter end) { - std::vector x; - for (Iter it = start; it != end; it++) { - x.push_back((*it).ID); - } - return Bind(index, nlohmann::json(x).dump()); - } - - template - int BindAsJSONArray(int index, const std::optional &obj) { - if (obj.has_value()) - return Bind(index, nlohmann::json(obj.value()).dump()); - else - return Bind(index, std::string("[]")); - } - - template - int BindAsJSON(int index, const T &obj) { - return Bind(index, nlohmann::json(obj).dump()); - } - - template - inline typename std::enable_if::value, int>::type - Bind(int index, T val) { - return Bind(index, static_cast::type>(val)); - } - - template - typename std::enable_if::value, int>::type - Bind(int index, T val) { - return m_db->SetError(sqlite3_bind_int64(m_stmt, index, val)); - } - - template - int BindAsJSON(int index, const std::optional &obj) { - if (obj.has_value()) - return Bind(index, nlohmann::json(obj.value()).dump()); - else - return Bind(index); - } - - template - typename std::enable_if::value>::type - Get(int index, T &out) const { - out = static_cast(sqlite3_column_int64(m_stmt, index)); - } - - void Get(int index, Snowflake &out) const; - void Get(int index, std::string &out) const; - - template - void GetJSON(int index, std::optional &out) const { - if (IsNull(index)) - out = std::nullopt; - else { - std::string stuff; - Get(index, stuff); - if (stuff == "") - out = std::nullopt; - else - out = nlohmann::json::parse(stuff).get(); - } - } - - template - void GetJSON(int index, T &out) const { - std::string stuff; - Get(index, stuff); - nlohmann::json::parse(stuff).get_to(out); - } - - template - void Get(int index, std::optional &out) const { - if (IsNull(index)) - out = std::nullopt; - else { - T tmp; - Get(index, tmp); - out = std::optional(std::move(tmp)); - } - } - - template - inline typename std::enable_if::value, void>::type - Get(int index, T &val) const { - typename std::underlying_type::type tmp; - Get(index, tmp); - val = static_cast(tmp); - } - - template - void GetIDOnlyStructs(int index, std::optional> &out) const { - out.emplace(); - std::string str; - Get(index, str); - for (const auto &id : nlohmann::json::parse(str)) - out->emplace_back().ID = id.get(); - } - - template - void GetArray(int index, OutputIt first) const { - std::string str; - Get(index, str); - for (const auto &id : nlohmann::json::parse(str)) - *first++ = id.get(); - } - - bool IsNull(int index) const; - int Step(); - bool Insert(); - bool FetchOne(); - int Reset(); - - sqlite3_stmt *obj(); - - private: - Database *m_db; - sqlite3_stmt *m_stmt; - }; - - Message GetMessageBound(std::unique_ptr &stmt) const; - - void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction); - - bool CreateTables(); - bool CreateStatements(); - - bool m_ok = true; - - std::filesystem::path m_db_path; - Database m_db; -#define STMT(x) mutable std::unique_ptr m_stmt_##x - STMT(set_guild); - STMT(get_guild); - STMT(get_guild_ids); - STMT(clr_guild); - STMT(set_chan); - STMT(get_chan); - STMT(get_chan_ids); - STMT(clr_chan); - STMT(set_msg); - STMT(get_msg); - STMT(set_msg_ref); - STMT(get_last_msgs); - STMT(set_user); - STMT(get_user); - STMT(set_member); - STMT(get_member); - STMT(set_role); - STMT(get_role); - STMT(set_emoji); - STMT(get_emoji); - STMT(set_perm); - STMT(get_perm); - STMT(set_ban); - STMT(get_ban); - STMT(get_bans); - STMT(clr_ban); - STMT(set_interaction); - STMT(set_member_roles); - STMT(get_member_roles); - STMT(set_guild_emoji); - STMT(get_guild_emojis); - STMT(clr_guild_emoji); - STMT(set_guild_feature); - STMT(get_guild_features); - STMT(get_guild_chans); - STMT(set_thread); - STMT(get_threads); - STMT(get_active_threads); - STMT(get_messages_before); - STMT(get_pins); - STMT(set_emoji_role); - STMT(get_emoji_roles); - STMT(set_mention); - STMT(get_mentions); - STMT(set_attachment); - STMT(get_attachments); - STMT(set_recipient); - STMT(get_recipients); - STMT(clr_recipient); - STMT(add_reaction); - STMT(sub_reaction); - STMT(get_reactions); -#undef STMT -}; diff --git a/discord/user.cpp b/discord/user.cpp deleted file mode 100644 index fae212d..0000000 --- a/discord/user.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "user.hpp" -#include "abaddon.hpp" - -bool UserData::IsDeleted() const { - return Discriminator == "0000"; -} - -bool UserData::HasAvatar() const { - return Avatar.size() > 0; -} - -bool UserData::HasAnimatedAvatar() const { - return Avatar.size() > 0 && Avatar[0] == 'a' && Avatar[1] == '_'; -} - -std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::string size) const { - const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id); - if (member.has_value() && member->Avatar.has_value()) - return "https://cdn.discordapp.com/guilds/" + - std::to_string(guild_id) + "/users/" + std::to_string(ID) + - "/avatars/" + *member->Avatar + "." + - ext + "?" + "size=" + size; - else - return GetAvatarURL(ext, size); -} - -std::string UserData::GetAvatarURL(const std::optional &guild_id, std::string ext, std::string size) const { - if (guild_id.has_value()) - return GetAvatarURL(*guild_id, ext, size); - else - return GetAvatarURL(ext, size); -} - -std::string UserData::GetAvatarURL(std::string ext, std::string size) const { - if (HasAvatar()) - return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size; - else - return GetDefaultAvatarURL(); -} - -std::string UserData::GetDefaultAvatarURL() const { - return "https://cdn.discordapp.com/embed/avatars/" + std::to_string(std::stoul(Discriminator) % 5) + ".png"; // size isn't respected by the cdn -} - -Snowflake UserData::GetHoistedRole(Snowflake guild_id, bool with_color) const { - return Abaddon::Get().GetDiscordClient().GetMemberHoistedRole(guild_id, ID, with_color); -} - -std::string UserData::GetMention() const { - return "<@" + std::to_string(ID) + ">"; -} - -std::string UserData::GetEscapedName() const { - return Glib::Markup::escape_text(Username); -} - -std::string UserData::GetEscapedBoldName() const { - return "" + Glib::Markup::escape_text(Username) + ""; -} - -std::string UserData::GetEscapedString() const { - return Glib::Markup::escape_text(Username) + "#" + Discriminator; -} - -void from_json(const nlohmann::json &j, UserData &m) { - JS_D("id", m.ID); - JS_D("username", m.Username); - JS_D("discriminator", m.Discriminator); - JS_N("avatar", m.Avatar); - JS_O("bot", m.IsBot); - JS_O("system", m.IsSystem); - JS_O("mfa_enabled", m.IsMFAEnabled); - JS_O("locale", m.Locale); - JS_O("verified", m.IsVerified); - JS_O("email", m.Email); - JS_O("flags", m.Flags); - JS_ON("premium_type", m.PremiumType); - JS_O("public_flags", m.PublicFlags); - JS_O("desktop", m.IsDesktop); - JS_O("mobile", m.IsMobile); - JS_ON("nsfw_allowed", m.IsNSFWAllowed); - JS_ON("phone", m.Phone); - JS_ON("bio", m.Bio); - JS_ON("banner", m.BannerHash); -} - -void to_json(nlohmann::json &j, const UserData &m) { - j["id"] = m.ID; - j["username"] = m.Username; - j["discriminator"] = m.Discriminator; - if (m.Avatar == "") - j["avatar"] = nullptr; - else - j["avatar"] = m.Avatar; - JS_IF("bot", m.IsBot); - JS_IF("system", m.IsSystem); - JS_IF("mfa_enabled", m.IsMFAEnabled); - JS_IF("locale", m.Locale); - JS_IF("verified", m.IsVerified); - JS_IF("email", m.Email); - JS_IF("flags", m.Flags); - JS_IF("premium_type", m.PremiumType); - JS_IF("public_flags", m.PublicFlags); - JS_IF("desktop", m.IsDesktop); - JS_IF("mobile", m.IsMobile); - JS_IF("nsfw_allowed", m.IsNSFWAllowed); - JS_IF("phone", m.Phone); -} - -void UserData::update_from_json(const nlohmann::json &j) { - JS_RD("username", Username); - JS_RD("discriminator", Discriminator); - JS_RD("avatar", Avatar); - JS_RD("bot", IsBot); - JS_RD("system", IsSystem); - JS_RD("mfa_enabled", IsMFAEnabled); - JS_RD("locale", Locale); - JS_RD("verified", IsVerified); - JS_RD("email", Email); - JS_RD("flags", Flags); - JS_RD("premium_type", PremiumType); - JS_RD("public_flags", PublicFlags); - JS_RD("desktop", IsDesktop); - JS_RD("mobile", IsMobile); - JS_RD("nsfw_allowed", IsNSFWAllowed); - JS_RD("phone", Phone); -} - -const char *UserData::GetFlagName(uint64_t flag) { - switch (flag) { - case DiscordEmployee: - return "discordstaff"; - case PartneredServerOwner: - return "partneredowner"; - case HypeSquadEvents: - return "hypesquadevents"; - case BugHunterLevel1: - return "discordbughunter"; - case HouseBravery: - return "hypesquadbravery"; - case HouseBrilliance: - return "hypesquadbrilliance"; - case HouseBalance: - return "hypesquadbalance"; - case EarlySupporter: - return "earlysupporter"; - case TeamUser: - return "teamuser"; - case System: - return "system"; - case BugHunterLevel2: - return "discordbughunter2"; - case VerifiedBot: - return "verifiedbot"; - case EarlyVerifiedBotDeveloper: - return "earlyverifiedbotdeveloper"; - case CertifiedModerator: - return "certifiedmoderator"; - default: - return "unknown"; - } -} - -const char *UserData::GetFlagReadableName(uint64_t flag) { - switch (flag) { - case DiscordEmployee: - return "Discord Staff"; - case PartneredServerOwner: - return "Partnered Server Owner"; - case HypeSquadEvents: - return "HypeSquad Events"; - case BugHunterLevel1: - return "Discord Bug Hunter"; - case HouseBravery: - return "HypeSquad Bravery"; - case HouseBrilliance: - return "HypeSquad Brilliance"; - case HouseBalance: - return "HypeSquad Balance"; - case EarlySupporter: - return "Early Supporter"; - case TeamUser: - return "Team User"; // ??? - case System: - return "System"; - case BugHunterLevel2: - return "Discord Bug Hunter Level 2"; - case VerifiedBot: - return "Verified Bot"; - case EarlyVerifiedBotDeveloper: - return "Early Verified Bot Developer"; - case CertifiedModerator: - return "Discord Certified Moderator"; - default: - return ""; - } -} diff --git a/discord/user.hpp b/discord/user.hpp deleted file mode 100644 index d4711fa..0000000 --- a/discord/user.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once -#include "snowflake.hpp" -#include "json.hpp" -#include - -enum class EPremiumType { - None = 0, - NitroClassic = 1, - Nitro = 2, -}; - -struct UserData { - enum { - DiscordEmployee = 1 << 0, - PartneredServerOwner = 1 << 1, - HypeSquadEvents = 1 << 2, - BugHunterLevel1 = 1 << 3, - HouseBravery = 1 << 6, - HouseBrilliance = 1 << 7, - HouseBalance = 1 << 8, - EarlySupporter = 1 << 9, - TeamUser = 1 << 10, // no idea what this is - System = 1 << 12, - BugHunterLevel2 = 1 << 14, - VerifiedBot = 1 << 16, - EarlyVerifiedBotDeveloper = 1 << 17, - CertifiedModerator = 1 << 18, - - MaxFlag_PlusOne, - MaxFlag = MaxFlag_PlusOne - 1, - }; - - static const char *GetFlagName(uint64_t flag); - static const char *GetFlagReadableName(uint64_t flag); - - Snowflake ID; - std::string Username; - std::string Discriminator; - std::string Avatar; // null - std::optional IsBot; - std::optional IsSystem; - std::optional IsMFAEnabled; - std::optional Locale; - std::optional IsVerified; - std::optional Email; // null - std::optional Flags; - std::optional PremiumType; // null - std::optional PublicFlags; - - // undocumented (opt) - std::optional IsDesktop; - std::optional IsMobile; - std::optional IsNSFWAllowed; // null - std::optional Phone; // null? - // for now (unserialized) - std::optional BannerHash; // null - std::optional Bio; // null - - friend void from_json(const nlohmann::json &j, UserData &m); - friend void to_json(nlohmann::json &j, const UserData &m); - void update_from_json(const nlohmann::json &j); - - bool IsDeleted() const; - bool HasAvatar() const; - bool HasAnimatedAvatar() const; - std::string GetAvatarURL(Snowflake guild_id, std::string ext = "png", std::string size = "32") const; - std::string GetAvatarURL(const std::optional &guild_id, std::string ext = "png", std::string size = "32") const; - std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const; - std::string GetDefaultAvatarURL() const; - Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const; - std::string GetMention() const; - std::string GetEscapedName() const; - std::string GetEscapedBoldName() const; - std::string GetEscapedString() const; - template - inline std::string GetEscapedBoldString() const { - if constexpr (with_at) - return "@" + Glib::Markup::escape_text(Username) + "#" + Discriminator; - else - return "" + Glib::Markup::escape_text(Username) + "#" + Discriminator; - } -}; diff --git a/discord/usersettings.cpp b/discord/usersettings.cpp deleted file mode 100644 index e4ab41a..0000000 --- a/discord/usersettings.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "usersettings.hpp" - -void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m) { - JS_N("color", m.Color); - JS_D("guild_ids", m.GuildIDs); - JS_N("id", m.ID); - JS_N("name", m.Name); -} - -void from_json(const nlohmann::json &j, UserSettings &m) { - JS_D("timezone_offset", m.TimezoneOffset); - JS_D("theme", m.Theme); - JS_D("stream_notifications_enabled", m.AreStreamNotificationsEnabled); - JS_D("status", m.Status); - JS_D("show_current_game", m.ShouldShowCurrentGame); - // JS_D("restricted_guilds", m.RestrictedGuilds); - JS_D("render_reactions", m.ShouldRenderReactions); - JS_D("render_embeds", m.ShouldRenderEmbeds); - JS_D("native_phone_integration_enabled", m.IsNativePhoneIntegrationEnabled); - JS_D("message_display_compact", m.ShouldMessageDisplayCompact); - JS_D("locale", m.Locale); - JS_D("inline_embed_media", m.ShouldInlineEmbedMedia); - JS_D("inline_attachment_media", m.ShouldInlineAttachmentMedia); - JS_D("guild_positions", m.GuildPositions); - JS_D("guild_folders", m.GuildFolders); - JS_D("gif_auto_play", m.ShouldGIFAutoplay); - // JS_D("friend_source_flags", m.FriendSourceFlags); - JS_D("explicit_content_filter", m.ExplicitContentFilter); - JS_D("enable_tts_command", m.IsTTSCommandEnabled); - JS_D("disable_games_tab", m.ShouldDisableGamesTab); - JS_D("developer_mode", m.DeveloperMode); - JS_D("detect_platform_accounts", m.ShouldDetectPlatformAccounts); - JS_D("default_guilds_restricted", m.AreDefaultGuildsRestricted); - // JS_N("custom_status", m.CustomStatus); - JS_D("convert_emoticons", m.ShouldConvertEmoticons); - JS_D("contact_sync_enabled", m.IsContactSyncEnabled); - JS_D("animate_emoji", m.ShouldAnimateEmojis); - JS_D("allow_accessibility_detection", m.IsAccessibilityDetectionAllowed); - JS_D("afk_timeout", m.AFKTimeout); -} diff --git a/discord/usersettings.hpp b/discord/usersettings.hpp deleted file mode 100644 index 6d37b3c..0000000 --- a/discord/usersettings.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "json.hpp" -#include "snowflake.hpp" -#include - -struct UserSettingsGuildFoldersEntry { - int Color = -1; // null - std::vector GuildIDs; - Snowflake ID; // null (this can be a snowflake as a string or an int that isnt a snowflake lol) - std::string Name; // null - - friend void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m); -}; - -struct UserSettings { - int TimezoneOffset; // - std::string Theme; // - bool AreStreamNotificationsEnabled; // - std::string Status; // - bool ShouldShowCurrentGame; // - // std::vector RestrictedGuilds; // - bool ShouldRenderReactions; // - bool ShouldRenderEmbeds; // - bool IsNativePhoneIntegrationEnabled; // - bool ShouldMessageDisplayCompact; // - std::string Locale; // - bool ShouldInlineEmbedMedia; // - bool ShouldInlineAttachmentMedia; // - std::vector GuildPositions; // deprecated? - std::vector GuildFolders; // - bool ShouldGIFAutoplay; // - // Unknown FriendSourceFlags; // - int ExplicitContentFilter; // - bool IsTTSCommandEnabled; // - bool ShouldDisableGamesTab; // - bool DeveloperMode; // - bool ShouldDetectPlatformAccounts; // - bool AreDefaultGuildsRestricted; // - // Unknown CustomStatus; // null - bool ShouldConvertEmoticons; // - bool IsContactSyncEnabled; // - bool ShouldAnimateEmojis; // - bool IsAccessibilityDetectionAllowed; // - int AFKTimeout; - - friend void from_json(const nlohmann::json &j, UserSettings &m); -}; diff --git a/discord/webhook.cpp b/discord/webhook.cpp deleted file mode 100644 index 4e8b422..0000000 --- a/discord/webhook.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "webhook.hpp" - -void from_json(const nlohmann::json &j, WebhookData &m) { - JS_D("id", m.ID); - JS_D("type", m.Type); - JS_O("guild_id", m.GuildID); - JS_D("channel_id", m.ChannelID); - JS_O("user", m.User); - JS_N("name", m.Name); - JS_N("avatar", m.Avatar); - JS_O("token", m.Token); - JS_N("application_id", m.ApplicationID); -} diff --git a/discord/webhook.hpp b/discord/webhook.hpp deleted file mode 100644 index f0214df..0000000 --- a/discord/webhook.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include "json.hpp" -#include "snowflake.hpp" -#include "user.hpp" - -enum class WebhookType { - Incoming = 1, - ChannelFollower = 2, -}; - -struct WebhookData { - Snowflake ID; - WebhookType Type; - std::optional GuildID; - Snowflake ChannelID; - std::optional User; - std::string Name; // null - std::string Avatar; // null - std::optional Token; - Snowflake ApplicationID; // null - - friend void from_json(const nlohmann::json &j, WebhookData &m); -}; diff --git a/discord/websocket.cpp b/discord/websocket.cpp deleted file mode 100644 index ff50cd3..0000000 --- a/discord/websocket.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "websocket.hpp" -#include - -Websocket::Websocket() {} - -void Websocket::StartConnection(std::string url) { - m_websocket.disableAutomaticReconnection(); - m_websocket.setUrl(url); - m_websocket.setOnMessageCallback(std::bind(&Websocket::OnMessage, this, std::placeholders::_1)); - m_websocket.setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works - m_websocket.start(); -} - -void Websocket::SetUserAgent(std::string agent) { - m_agent = agent; -} - -void Websocket::Stop() { - Stop(ix::WebSocketCloseConstants::kNormalClosureCode); -} - -void Websocket::Stop(uint16_t code) { - m_websocket.stop(code); -} - -bool Websocket::IsOpen() const { - auto state = m_websocket.getReadyState(); - return state == ix::ReadyState::Open; -} - -void Websocket::Send(const std::string &str) { - printf("sending %s\n", str.c_str()); - m_websocket.sendText(str); -} - -void Websocket::Send(const nlohmann::json &j) { - Send(j.dump()); -} - -void Websocket::OnMessage(const ix::WebSocketMessagePtr &msg) { - switch (msg->type) { - case ix::WebSocketMessageType::Open: { - m_signal_open.emit(); - } break; - case ix::WebSocketMessageType::Close: { - m_signal_close.emit(msg->closeInfo.code); - } break; - case ix::WebSocketMessageType::Message: { - m_signal_message.emit(msg->str); - } break; - default: - break; - } -} - -Websocket::type_signal_open Websocket::signal_open() { - return m_signal_open; -} - -Websocket::type_signal_close Websocket::signal_close() { - return m_signal_close; -} - -Websocket::type_signal_message Websocket::signal_message() { - return m_signal_message; -} diff --git a/discord/websocket.hpp b/discord/websocket.hpp deleted file mode 100644 index e6a6489..0000000 --- a/discord/websocket.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include - -class Websocket { -public: - Websocket(); - void StartConnection(std::string url); - - void SetUserAgent(std::string agent); - - void Send(const std::string &str); - void Send(const nlohmann::json &j); - void Stop(); - void Stop(uint16_t code); - bool IsOpen() const; - -private: - void OnMessage(const ix::WebSocketMessagePtr &msg); - - ix::WebSocket m_websocket; - std::string m_agent; - -public: - typedef sigc::signal type_signal_open; - typedef sigc::signal type_signal_close; - typedef sigc::signal type_signal_message; - - type_signal_open signal_open(); - type_signal_close signal_close(); - type_signal_message signal_message(); - -private: - type_signal_open m_signal_open; - type_signal_close m_signal_close; - type_signal_message m_signal_message; -}; -- cgit v1.2.3