diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ebdc02..4af6846 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,23 @@ add_executable(abaddon discord/store.cpp discord/websocket.hpp discord/websocket.cpp + discord/user.hpp + discord/snowflake.hpp + discord/snowflake.cpp + discord/user.cpp + discord/json.hpp + discord/role.hpp + discord/role.cpp + discord/member.hpp + discord/member.cpp + discord/channel.hpp + discord/channel.cpp + discord/guild.hpp + discord/guild.cpp + discord/usersettings.hpp + discord/usersettings.cpp + discord/message.hpp + discord/message.cpp windows/mainwindow.hpp windows/mainwindow.cpp ) diff --git a/components/channels.cpp b/components/channels.cpp index 2da5395..013b950 100644 --- a/components/channels.cpp +++ b/components/channels.cpp @@ -163,8 +163,8 @@ void ChannelList::SetListingFromGuildsInternal() { // map each category to its channels - std::unordered_map> cat_to_channels; - std::unordered_map> orphan_channels; + std::unordered_map> cat_to_channels; + std::unordered_map> orphan_channels; for (const auto &[gid, guild] : guilds) { for (const auto &chan : guild.Channels) { switch (chan.Type) { @@ -172,12 +172,12 @@ void ChannelList::SetListingFromGuildsInternal() { if (chan.ParentID.IsValid()) { auto it = cat_to_channels.find(chan.ParentID); if (it == cat_to_channels.end()) - cat_to_channels[chan.ParentID] = std::vector(); + cat_to_channels[chan.ParentID] = std::vector(); cat_to_channels[chan.ParentID].push_back(&chan); } else { auto it = orphan_channels.find(gid); if (it == orphan_channels.end()) - orphan_channels[gid] = std::vector(); + orphan_channels[gid] = std::vector(); orphan_channels[gid].push_back(&chan); } } break; @@ -187,7 +187,7 @@ void ChannelList::SetListingFromGuildsInternal() { } } - auto add_channel = [&](const Snowflake &id, const ChannelData &channel) -> Gtk::ListBoxRow * { + auto add_channel = [&](const Snowflake &id, const Channel &channel) -> Gtk::ListBoxRow * { auto *channel_row = Gtk::manage(new Gtk::ListBoxRow); auto *channel_ev = Gtk::manage(new Gtk::EventBox); auto *channel_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); @@ -215,7 +215,7 @@ void ChannelList::SetListingFromGuildsInternal() { return channel_row; }; - auto add_category = [&](const Snowflake &id, const ChannelData &channel) -> Gtk::ListBoxRow * { + auto add_category = [&](const Snowflake &id, const Channel &channel) -> Gtk::ListBoxRow * { auto *category_row = Gtk::manage(new Gtk::ListBoxRow); auto *category_ev = Gtk::manage(new Gtk::EventBox); auto *category_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); @@ -243,7 +243,7 @@ void ChannelList::SetListingFromGuildsInternal() { info.Type = ListItemInfo::ListItemType::Category; if (cat_to_channels.find(id) != cat_to_channels.end()) { - std::map sorted_channels; + std::map sorted_channels; for (const auto channel : cat_to_channels[id]) { assert(channel->Position != -1); @@ -260,7 +260,7 @@ void ChannelList::SetListingFromGuildsInternal() { return category_row; }; - auto add_guild = [&](const Snowflake &id, const GuildData &guild) { + auto add_guild = [&](const Snowflake &id, const Guild &guild) { auto *guild_row = Gtk::manage(new Gtk::ListBoxRow); auto *guild_ev = Gtk::manage(new Gtk::EventBox); auto *guild_box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL)); @@ -287,7 +287,7 @@ void ChannelList::SetListingFromGuildsInternal() { info.Type = ListItemInfo::ListItemType::Guild; if (orphan_channels.find(id) != orphan_channels.end()) { - std::map sorted_orphans; + std::map sorted_orphans; for (const auto channel : orphan_channels[id]) { assert(channel->Position != -1); // can this trigger? @@ -301,7 +301,7 @@ void ChannelList::SetListingFromGuildsInternal() { } } - std::map sorted_categories; + std::map sorted_categories; for (const auto &channel : guild.Channels) { if (channel.Type == ChannelType::GUILD_CATEGORY) { diff --git a/components/memberlist.cpp b/components/memberlist.cpp index d7b79b6..129f27f 100644 --- a/components/memberlist.cpp +++ b/components/memberlist.cpp @@ -74,8 +74,8 @@ void MemberList::UpdateMemberListInternal() { } // process all the shit first so its in proper order - std::map pos_to_role; - std::map> pos_to_users; + std::map pos_to_role; + std::map> pos_to_users; std::unordered_map user_to_color; std::vector roleless_users; @@ -106,7 +106,7 @@ void MemberList::UpdateMemberListInternal() { } } - auto add_user = [this, &user_to_color](const UserData *data) { + auto add_user = [this, &user_to_color](const User *data) { auto *user_row = Gtk::manage(new MemberListUserRow); user_row->ID = data->ID; auto *user_ev = Gtk::manage(new Gtk::EventBox); diff --git a/discord/channel.cpp b/discord/channel.cpp new file mode 100644 index 0000000..fe1a6a6 --- /dev/null +++ b/discord/channel.cpp @@ -0,0 +1,22 @@ +#include "channel.hpp" + +void from_json(const nlohmann::json &j, Channel &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_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); +} diff --git a/discord/channel.hpp b/discord/channel.hpp new file mode 100644 index 0000000..78246f6 --- /dev/null +++ b/discord/channel.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "snowflake.hpp" +#include "json.hpp" +#include "user.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, +}; + +struct Channel { + Snowflake ID; // + ChannelType Type; // + Snowflake GuildID; // opt + int Position = -1; // opt + // std::vector PermissionOverwrites; // opt + std::string Name; // opt, null (null for dm's) + std::string Topic; // opt, null + bool IsNSFW = false; // opt + Snowflake LastMessageID; // opt, null + int Bitrate = 0; // opt + int UserLimit = 0; // opt + int RateLimitPerUser = 0; // opt + std::vector Recipients; // opt + std::string Icon; // opt, null + Snowflake OwnerID; // opt + Snowflake ApplicationID; // opt + Snowflake ParentID; // opt, null + std::string LastPinTimestamp; // opt, can be null even tho docs say otherwise + + friend void from_json(const nlohmann::json &j, Channel &m); +}; diff --git a/discord/discord.cpp b/discord/discord.cpp index 88933b5..63722fe 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -41,16 +41,16 @@ const Store::guilds_type &DiscordClient::GetGuilds() const { return m_store.GetGuilds(); } -const UserSettingsData &DiscordClient::GetUserSettings() const { +const UserSettings &DiscordClient::GetUserSettings() const { return m_user_settings; } -const UserData &DiscordClient::GetUserData() const { +const User &DiscordClient::GetUserData() const { return m_user_data; } std::vector DiscordClient::GetUserSortedGuilds() const { - std::vector> sorted_guilds; + std::vector> sorted_guilds; if (m_user_settings.GuildPositions.size()) { std::unordered_set positioned_guilds(m_user_settings.GuildPositions.begin(), m_user_settings.GuildPositions.end()); @@ -156,19 +156,19 @@ const MessageData *DiscordClient::GetMessage(Snowflake id) const { return m_store.GetMessage(id); } -const ChannelData *DiscordClient::GetChannel(Snowflake id) const { +const Channel *DiscordClient::GetChannel(Snowflake id) const { return m_store.GetChannel(id); } -const UserData *DiscordClient::GetUser(Snowflake id) const { +const User *DiscordClient::GetUser(Snowflake id) const { return m_store.GetUser(id); } -const RoleData *DiscordClient::GetRole(Snowflake id) const { +const Role *DiscordClient::GetRole(Snowflake id) const { return m_store.GetRole(id); } -const GuildData *DiscordClient::GetGuild(Snowflake id) const { +const Guild *DiscordClient::GetGuild(Snowflake id) const { return m_store.GetGuild(id); } @@ -176,7 +176,7 @@ Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user auto *data = m_store.GetGuildMemberData(guild_id, user_id); if (data == nullptr) return Snowflake::Invalid; - std::vector roles; + std::vector roles; for (const auto &id : data->Roles) { auto *role = GetRole(id); if (role != nullptr) { @@ -187,7 +187,7 @@ Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user if (roles.size() == 0) return Snowflake::Invalid; - std::sort(roles.begin(), roles.end(), [this](const RoleData *a, const RoleData *b) -> bool { + std::sort(roles.begin(), roles.end(), [this](const Role *a, const Role *b) -> bool { return a->Position > b->Position; }); diff --git a/discord/discord.hpp b/discord/discord.hpp index 953be69..f6e7241 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -61,8 +61,8 @@ public: using members_type = Store::members_type; const guilds_type &GetGuilds() const; - const UserData &GetUserData() const; - const UserSettingsData &GetUserSettings() const; + const User &GetUserData() const; + const UserSettings &GetUserSettings() const; std::vector GetUserSortedGuilds() const; std::set GetMessagesForChannel(Snowflake id) const; std::set GetPrivateChannels() const; @@ -71,10 +71,10 @@ public: void FetchMessagesInChannel(Snowflake id, std::function &)> cb); void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function &)> cb); const MessageData *GetMessage(Snowflake id) const; - const ChannelData *GetChannel(Snowflake id) const; - const UserData *GetUser(Snowflake id) const; - const RoleData *GetRole(Snowflake id) const; - const GuildData *GetGuild(Snowflake id) const; + const Channel *GetChannel(Snowflake id) const; + const User *GetUser(Snowflake id) const; + const Role *GetRole(Snowflake id) const; + const Guild *GetGuild(Snowflake id) const; Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const; std::unordered_set GetUsersInGuild(Snowflake id) const; @@ -110,8 +110,8 @@ private: void AddUserToGuild(Snowflake user_id, Snowflake guild_id); std::unordered_map> m_guild_to_users; - UserData m_user_data; - UserSettingsData m_user_settings; + User m_user_data; + UserSettings m_user_settings; Store m_store; HTTPClient m_http; diff --git a/discord/guild.cpp b/discord/guild.cpp new file mode 100644 index 0000000..fae30f1 --- /dev/null +++ b/discord/guild.cpp @@ -0,0 +1,56 @@ +#include "guild.hpp" + +void from_json(const nlohmann::json &j, Guild &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_D("owner_id", m.OwnerID); + JS_O("permissions", m.Permissions); + JS_O("permissions_new", m.PermissionsNew); + JS_D("region", m.VoiceRegion); + JS_N("afk_channel_id", m.AFKChannelID); + JS_D("afk_timeout", m.AFKTimeout); + JS_O("embed_enabled", m.IsEmbedEnabled); + JS_ON("embed_channel_id", m.EmbedChannelID); + JS_D("verification_level", m.VerificationLevel); + JS_D("default_message_notifications", m.DefaultMessageNotifications); + JS_D("explicit_content_filter", m.ExplicitContentFilter); + JS_D("roles", m.Roles); + // JS_D("emojis", m.Emojis); + JS_D("features", m.Features); + JS_D("mfa_level", m.MFALevel); + JS_N("application_id", m.ApplicationID); + JS_O("widget_enabled", m.IsWidgetEnabled); + JS_ON("widget_channel_id", m.WidgetChannelID); + JS_N("system_channel_id", m.SystemChannelID); + JS_D("system_channel_flags", m.SystemChannelFlags); + JS_N("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("presences", m.Presences); + JS_ON("max_presences", m.MaxPresences); + JS_O("max_members", m.MaxMembers); + JS_N("vanity_url_code", m.VanityURL); + JS_N("description", m.Description); + JS_N("banner", m.BannerHash); + JS_D("premium_tier", m.PremiumTier); + JS_O("premium_subscription_count", m.PremiumSubscriptionCount); + JS_D("preferred_locale", m.PreferredLocale); + JS_N("public_updates_channel_id", m.PublicUpdatesChannelID); + JS_O("max_video_channel_users", m.MaxVideoChannelUsers); + JS_O("approximate_member_count", m.ApproximateMemberCount); + JS_O("approximate_presence_count", m.ApproximatePresenceCount); +} diff --git a/discord/guild.hpp b/discord/guild.hpp new file mode 100644 index 0000000..d90c3e8 --- /dev/null +++ b/discord/guild.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "json.hpp" +#include "snowflake.hpp" +#include "role.hpp" +#include "channel.hpp" +#include +#include + +// 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) +struct Guild { + Snowflake ID; // + std::string Name; // + std::string Icon; // null + std::string Splash; // null + std::string DiscoverySplash; // opt, null (docs wrong) + bool IsOwner = false; // opt + Snowflake OwnerID; // + int Permissions = 0; // opt + std::string PermissionsNew; // opt + std::string VoiceRegion; // opt + Snowflake AFKChannelID; // null + int AFKTimeout; // + bool IsEmbedEnabled = false; // opt, deprecated + Snowflake EmbedChannelID; // opt, null, deprecated + int VerificationLevel; // + int DefaultMessageNotifications; // + int ExplicitContentFilter; // + std::vector Roles; // + // std::vector Emojis; // + std::vector Features; // + int MFALevel; // + Snowflake ApplicationID; // null + bool IsWidgetEnabled = false; // opt + Snowflake WidgetChannelID; // opt, null + Snowflake SystemChannelID; // null + int SystemChannelFlags; // + Snowflake RulesChannelID; // null + std::string JoinedAt; // opt* + bool IsLarge = false; // opt* + bool IsUnavailable = false; // opt* + int MemberCount = 0; // opt* + // std::vector VoiceStates; // opt* + // std::vector Members; // opt* - incomplete anyways + std::vector Channels; // opt* + // std::vector Presences; // opt* + int MaxPresences = 0; // opt, null + int MaxMembers = 0; // opt + std::string VanityURL; // null + std::string Description; // null + std::string BannerHash; // null + int PremiumTier; // + int PremiumSubscriptionCount = 0; // opt + std::string PreferredLocale; // + Snowflake PublicUpdatesChannelID; // null + int MaxVideoChannelUsers = 0; // opt + int ApproximateMemberCount = 0; // opt + int ApproximatePresenceCount = 0; // opt + + // 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, Guild &m); +}; diff --git a/discord/json.hpp b/discord/json.hpp new file mode 100644 index 0000000..1cd4a14 --- /dev/null +++ b/discord/json.hpp @@ -0,0 +1,23 @@ +#pragma once +#include + +#define JS_D(k, t) \ + do { \ + j.at(k).get_to(t); \ + } while (0) + +#define JS_O(k, t) \ + do { \ + if (j.contains(k)) j.at(k).get_to(t); \ + } while (0) + +#define JS_N(k, t) \ + do { \ + if (!j.at(k).is_null()) j.at(k).get_to(t); \ + } while (0) + +#define JS_ON(k, t) \ + do { \ + if (j.contains(k) && !j.at(k).is_null()) \ + j.at(k).get_to(t); \ + } while (0) diff --git a/discord/member.cpp b/discord/member.cpp new file mode 100644 index 0000000..9452695 --- /dev/null +++ b/discord/member.cpp @@ -0,0 +1,11 @@ +#include "member.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); +} diff --git a/discord/member.hpp b/discord/member.hpp new file mode 100644 index 0000000..4ff84fc --- /dev/null +++ b/discord/member.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "snowflake.hpp" +#include "json.hpp" +#include "user.hpp" +#include +#include + +struct GuildMember { + User User; // opt + std::string Nickname; // null + std::vector Roles; // + std::string JoinedAt; // + std::string PremiumSince; // opt, null + bool IsDeafened; // + bool IsMuted; // + + friend void from_json(const nlohmann::json &j, GuildMember &m); +}; diff --git a/discord/message.cpp b/discord/message.cpp new file mode 100644 index 0000000..600ffcb --- /dev/null +++ b/discord/message.cpp @@ -0,0 +1,107 @@ +#include "message.hpp" + +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 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 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 from_json(const nlohmann::json &j, EmbedVideoData &m) { + JS_O("url", m.URL); + JS_O("height", m.Height); + JS_O("width", m.Width); +} + +void from_json(const nlohmann::json &j, EmbedProviderData &m) { + JS_O("name", m.Name); + JS_ON("url", m.URL); +} + +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 from_json(const nlohmann::json &j, EmbedFieldData &m) { + JS_D("name", m.Name); + JS_D("value", m.Value); + JS_O("inline", m.Inline); +} + +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 from_json(const nlohmann::json &j, MessageData &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); + 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); +} + +// probably gonna need to return present keys +void MessageData::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("content", Content); + JS_O("timestamp", Timestamp); + JS_ON("edited_timestamp", EditedTimestamp); + JS_O("tts", IsTTS); + JS_O("mention_everyone", DoesMentionEveryone); + JS_O("mentions", Mentions); + JS_O("nonce", Nonce); + JS_O("pinned", IsPinned); + JS_O("webhook_id", WebhookID); + JS_O("type", Type); + JS_O("flags", Flags); +} diff --git a/discord/message.hpp b/discord/message.hpp new file mode 100644 index 0000000..7eb0cdc --- /dev/null +++ b/discord/message.hpp @@ -0,0 +1,138 @@ +#pragma once +#include "snowflake.hpp" +#include "json.hpp" +#include "user.hpp" +#include +#include + +enum class MessageType { + DEFAULT = 0, + RECIPIENT_ADD = 1, + RECIPIENT_REMOVE = 2, + CALL = 3, + CHANNEL_NaME_CHANGE = 4, + CHANNEL_ICON_CHANGE = 5, + CHANNEL_PINNED_MESSAGE = 6, + GUILD_MEMBER_JOIN = 6, + USER_PREMIUM_GUILD_SUBSCRIPTION = 7, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 8, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 9, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 10, + CHANNEL_FOLLOW_ADD = 12, + GUILD_DISCOVERY_DISQUALIFIED = 13, + GUILD_DISCOVERY_REQUALIFIED = 14, +}; + +enum class MessageFlags { + NONE = 0, + CROSSPOSTED = 1 << 0, + IS_CROSSPOST = 1 << 1, + SUPPRESS_EMBEDS = 1 << 2, + SOURCE_MESSAGE_DELETE = 1 << 3, + URGENT = 1 << 4, +}; + +struct EmbedFooterData { + std::string Text; // + std::string IconURL; // opt + std::string ProxyIconURL; // opt + + friend void from_json(const nlohmann::json &j, EmbedFooterData &m); +}; + +struct EmbedImageData { + std::string URL; // opt + std::string ProxyURL; // opt + int Height = 0; // opt + int Width = 0; // opt + + friend void from_json(const nlohmann::json &j, EmbedImageData &m); +}; + +struct EmbedThumbnailData { + std::string URL; // opt + std::string ProxyURL; // opt + int Height = 0; // opt + int Width = 0; // opt + + friend void from_json(const nlohmann::json &j, EmbedThumbnailData &m); +}; + +struct EmbedVideoData { + std::string URL; // opt + int Height = 0; // opt + int Width = 0; // opt + friend void from_json(const nlohmann::json &j, EmbedVideoData &m); +}; + +struct EmbedProviderData { + std::string Name; // opt + std::string URL; // opt, null (docs wrong) + + friend void from_json(const nlohmann::json &j, EmbedProviderData &m); +}; + +struct EmbedAuthorData { + std::string Name; // opt + std::string URL; // opt + std::string IconURL; // opt + std::string ProxyIconURL; // opt + + friend void from_json(const nlohmann::json &j, EmbedAuthorData &m); +}; + +struct EmbedFieldData { + std::string Name; // + std::string Value; // + bool Inline = false; // opt + + friend void from_json(const nlohmann::json &j, EmbedFieldData &m); +}; + +struct EmbedData { + std::string Title; // opt + std::string Type; // opt + std::string Description; // opt + std::string URL; // opt + std::string Timestamp; // opt + int Color = -1; // opt + EmbedFooterData Footer; // opt + EmbedImageData Image; // opt + EmbedThumbnailData Thumbnail; // opt + EmbedVideoData Video; // opt + EmbedProviderData Provider; // opt + EmbedAuthorData Author; // opt + std::vector Fields; // opt + + friend void from_json(const nlohmann::json &j, EmbedData &m); +}; + +struct MessageData { + Snowflake ID; // + Snowflake ChannelID; // + Snowflake GuildID; // opt + User Author; // + // GuildMember Member; // opt + std::string Content; // + std::string Timestamp; // + std::string EditedTimestamp; // null + bool IsTTS; // + bool DoesMentionEveryone; // + std::vector Mentions; // + // std::vector MentionRoles; // + // std::vector MentionChannels; // opt + // std::vector Attachments; // + std::vector Embeds; // + // std::vector Reactions; // opt + std::string Nonce; // opt + bool IsPinned; // + Snowflake WebhookID; // opt + MessageType Type; // + // MessageActivityData Activity; // opt + // MessageApplicationData Application; // opt + // MessageReferenceData MessageReference; // opt + MessageFlags Flags = MessageFlags::NONE; // opt + + friend void from_json(const nlohmann::json &j, MessageData &m); + void from_json_edited(const nlohmann::json &j); // for MESSAGE_UPDATE +}; diff --git a/discord/objects.cpp b/discord/objects.cpp index 4ef3499..2b2e170 100644 --- a/discord/objects.cpp +++ b/discord/objects.cpp @@ -1,26 +1,5 @@ #include "objects.hpp" -#define JS_D(k, t) \ - do { \ - j.at(k).get_to(t); \ - } while (0) - -#define JS_O(k, t) \ - do { \ - if (j.contains(k)) j.at(k).get_to(t); \ - } while (0) - -#define JS_N(k, t) \ - do { \ - if (!j.at(k).is_null()) j.at(k).get_to(t); \ - } while (0) - -#define JS_ON(k, t) \ - do { \ - if (j.contains(k) && !j.at(k).is_null()) \ - j.at(k).get_to(t); \ - } while (0) - void from_json(const nlohmann::json &j, GatewayMessage &m) { JS_D("op", m.Opcode); m.Data = j.at("d"); @@ -32,172 +11,6 @@ void from_json(const nlohmann::json &j, HelloMessageData &m) { JS_D("heartbeat_interval", m.HeartbeatInterval); } -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); - JS_D("permissions", m.PermissionsLegacy); - std::string tmp; - JS_D("permissions_new", tmp); - m.Permissions = std::stoull(tmp); - JS_D("managed", m.IsManaged); - JS_D("mentionable", m.IsMentionable); -} - -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); -} - -void from_json(const nlohmann::json &j, GuildMemberData &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); -} - -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_D("owner_id", m.OwnerID); - JS_O("permissions", m.Permissions); - JS_O("permissions_new", m.PermissionsNew); - JS_D("region", m.VoiceRegion); - JS_N("afk_channel_id", m.AFKChannelID); - JS_D("afk_timeout", m.AFKTimeout); - JS_O("embed_enabled", m.IsEmbedEnabled); - JS_ON("embed_channel_id", m.EmbedChannelID); - JS_D("verification_level", m.VerificationLevel); - JS_D("default_message_notifications", m.DefaultMessageNotifications); - JS_D("explicit_content_filter", m.ExplicitContentFilter); - JS_D("roles", m.Roles); - // JS_D("emojis", m.Emojis); - JS_D("features", m.Features); - JS_D("mfa_level", m.MFALevel); - JS_N("application_id", m.ApplicationID); - JS_O("widget_enabled", m.IsWidgetEnabled); - JS_ON("widget_channel_id", m.WidgetChannelID); - JS_N("system_channel_id", m.SystemChannelID); - JS_D("system_channel_flags", m.SystemChannelFlags); - JS_N("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("presences", m.Presences); - JS_ON("max_presences", m.MaxPresences); - JS_O("max_members", m.MaxMembers); - JS_N("vanity_url_code", m.VanityURL); - JS_N("description", m.Description); - JS_N("banner", m.BannerHash); - JS_D("premium_tier", m.PremiumTier); - JS_O("premium_subscription_count", m.PremiumSubscriptionCount); - JS_D("preferred_locale", m.PreferredLocale); - JS_N("public_updates_channel_id", m.PublicUpdatesChannelID); - JS_O("max_video_channel_users", m.MaxVideoChannelUsers); - JS_O("approximate_member_count", m.ApproximateMemberCount); - JS_O("approximate_presence_count", m.ApproximatePresenceCount); -} - -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_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); -} - -void from_json(const nlohmann::json &j, MessageData &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); - 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); -} - -// probably gonna need to return present keys -void MessageData::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("content", Content); - JS_O("timestamp", Timestamp); - JS_ON("edited_timestamp", EditedTimestamp); - JS_O("tts", IsTTS); - JS_O("mention_everyone", DoesMentionEveryone); - JS_O("mentions", Mentions); - JS_O("nonce", Nonce); - JS_O("pinned", IsPinned); - JS_O("webhook_id", WebhookID); - JS_O("type", Type); - JS_O("flags", Flags); -} - void from_json(const nlohmann::json &j, MessageDeleteData &m) { JS_D("id", m.ID); JS_D("channel_id", m.ChannelID); @@ -210,7 +23,7 @@ void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::GroupItem JS_D("count", m.Count); } -GuildMemberData GuildMemberListUpdateMessage::MemberItem::GetAsMemberData() const { +GuildMember GuildMemberListUpdateMessage::MemberItem::GetAsMemberData() const { return m_member_data; } @@ -274,98 +87,6 @@ void from_json(const nlohmann::json &j, ReadyEventData &m) { JS_D("private_channels", m.PrivateChannels); } -void from_json(const nlohmann::json &j, UserSettingsData &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); -} - -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 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 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 from_json(const nlohmann::json &j, EmbedVideoData &m) { - JS_O("url", m.URL); - JS_O("height", m.Height); - JS_O("width", m.Width); -} - -void from_json(const nlohmann::json &j, EmbedProviderData &m) { - JS_O("name", m.Name); - JS_ON("url", m.URL); -} - -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 from_json(const nlohmann::json &j, EmbedFieldData &m) { - JS_D("name", m.Name); - JS_D("value", m.Value); - JS_O("inline", m.Inline); -} - -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 IdentifyProperties &m) { j["$os"] = m.OS; j["$browser"] = m.Browser; @@ -405,38 +126,3 @@ void to_json(nlohmann::json &j, const MessageEditObject &m) { if (m.Flags != -1) j["flags"] = m.Flags; } - -Snowflake::Snowflake() - : m_num(Invalid) {} - -Snowflake::Snowflake(const Snowflake &s) - : m_num(s.m_num) {} - -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; -}; - -bool Snowflake::IsValid() const { - return m_num != Invalid; -} - -void from_json(const nlohmann::json &j, Snowflake &s) { - std::string tmp; - j.get_to(tmp); - s.m_num = std::stoull(tmp); -} - -void to_json(nlohmann::json &j, const Snowflake &s) { - j = std::to_string(s); -} - -#undef JS_O -#undef JS_D -#undef JS_N -#undef JS_ON diff --git a/discord/objects.hpp b/discord/objects.hpp index 065e040..4714c85 100644 --- a/discord/objects.hpp +++ b/discord/objects.hpp @@ -3,53 +3,16 @@ #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" -struct Snowflake { - Snowflake(); - Snowflake(const Snowflake &s); - Snowflake(uint64_t n); - Snowflake(const std::string &str); - - bool IsValid() 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 int Invalid = -1; - - 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 +// most stuff below should just be objects that get processed and thrown away immediately enum class GatewayOp : int { Event = 0, @@ -82,315 +45,6 @@ struct HelloMessageData { friend void from_json(const nlohmann::json &j, HelloMessageData &m); }; -enum class ChannelType : int { - GUILD_TEXT = 0, - DM = 1, - GUILD_VOICE = 2, - GROUP_DM = 3, - GUILD_CATEGORY = 4, - GUILD_NEWS = 5, - GUILD_STORE = 6, -}; - -struct RoleData { - Snowflake ID; - std::string Name; - int Color; - bool IsHoisted; - int Position; - int PermissionsLegacy; - uint64_t Permissions; - bool IsManaged; - bool IsMentionable; - - friend void from_json(const nlohmann::json &j, RoleData &m); -}; - -struct UserData { - Snowflake ID; // - std::string Username; // - std::string Discriminator; // - std::string Avatar; // null - bool IsBot = false; // opt - bool IsSystem = false; // opt - bool IsMFAEnabled = false; // opt - std::string Locale; // opt - bool IsVerified = false; // opt - std::string Email; // opt, null - int Flags = 0; // opt - int PremiumType = 0; // opt, null (docs wrong) - int PublicFlags = 0; // opt - - // undocumented (opt) - bool IsDesktop = false; // - bool IsMobile = false; // - bool IsNSFWAllowed = false; // null - std::string Phone; // null? - - friend void from_json(const nlohmann::json &j, UserData &m); -}; - -struct GuildMemberData { - UserData User; // opt - std::string Nickname; // null - std::vector Roles; // - std::string JoinedAt; // - std::string PremiumSince; // opt, null - bool IsDeafened; // - bool IsMuted; // - - friend void from_json(const nlohmann::json &j, GuildMemberData &m); -}; - -struct ChannelData { - Snowflake ID; // - ChannelType Type; // - Snowflake GuildID; // opt - int Position = -1; // opt - // std::vector PermissionOverwrites; // opt - std::string Name; // opt, null (null for dm's) - std::string Topic; // opt, null - bool IsNSFW = false; // opt - Snowflake LastMessageID; // opt, null - int Bitrate = 0; // opt - int UserLimit = 0; // opt - int RateLimitPerUser = 0; // opt - std::vector Recipients; // opt - std::string Icon; // opt, null - Snowflake OwnerID; // opt - Snowflake ApplicationID; // opt - Snowflake ParentID; // opt, null - std::string LastPinTimestamp; // opt, can be null even tho docs say otherwise - - friend void from_json(const nlohmann::json &j, ChannelData &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) -struct GuildData { - Snowflake ID; // - std::string Name; // - std::string Icon; // null - std::string Splash; // null - std::string DiscoverySplash; // opt, null (docs wrong) - bool IsOwner = false; // opt - Snowflake OwnerID; // - int Permissions = 0; // opt - std::string PermissionsNew; // opt - std::string VoiceRegion; // opt - Snowflake AFKChannelID; // null - int AFKTimeout; // - bool IsEmbedEnabled = false; // opt, deprecated - Snowflake EmbedChannelID; // opt, null, deprecated - int VerificationLevel; // - int DefaultMessageNotifications; // - int ExplicitContentFilter; // - std::vector Roles; // - // std::vector Emojis; // - std::vector Features; // - int MFALevel; // - Snowflake ApplicationID; // null - bool IsWidgetEnabled = false; // opt - Snowflake WidgetChannelID; // opt, null - Snowflake SystemChannelID; // null - int SystemChannelFlags; // - Snowflake RulesChannelID; // null - std::string JoinedAt; // opt* - bool IsLarge = false; // opt* - bool IsUnavailable = false; // opt* - int MemberCount = 0; // opt* - // std::vector VoiceStates; // opt* - // std::vector Members; // opt* - incomplete anyways - std::vector Channels; // opt* - // std::vector Presences; // opt* - int MaxPresences = 0; // opt, null - int MaxMembers = 0; // opt - std::string VanityURL; // null - std::string Description; // null - std::string BannerHash; // null - int PremiumTier; // - int PremiumSubscriptionCount = 0; // opt - std::string PreferredLocale; // - Snowflake PublicUpdatesChannelID; // null - int MaxVideoChannelUsers = 0; // opt - int ApproximateMemberCount = 0; // opt - int ApproximatePresenceCount = 0; // opt - - // 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); -}; - -struct UserSettingsData { - 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; // - // 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, UserSettingsData &m); -}; - -enum class MessageType { - DEFAULT = 0, - RECIPIENT_ADD = 1, - RECIPIENT_REMOVE = 2, - CALL = 3, - CHANNEL_NaME_CHANGE = 4, - CHANNEL_ICON_CHANGE = 5, - CHANNEL_PINNED_MESSAGE = 6, - GUILD_MEMBER_JOIN = 6, - USER_PREMIUM_GUILD_SUBSCRIPTION = 7, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 8, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 9, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 10, - CHANNEL_FOLLOW_ADD = 12, - GUILD_DISCOVERY_DISQUALIFIED = 13, - GUILD_DISCOVERY_REQUALIFIED = 14, -}; - -enum class MessageFlags { - NONE = 0, - CROSSPOSTED = 1 << 0, - IS_CROSSPOST = 1 << 1, - SUPPRESS_EMBEDS = 1 << 2, - SOURCE_MESSAGE_DELETE = 1 << 3, - URGENT = 1 << 4, -}; - -struct EmbedFooterData { - std::string Text; // - std::string IconURL; // opt - std::string ProxyIconURL; // opt - - friend void from_json(const nlohmann::json &j, EmbedFooterData &m); -}; - -struct EmbedImageData { - std::string URL; // opt - std::string ProxyURL; // opt - int Height = 0; // opt - int Width = 0; // opt - - friend void from_json(const nlohmann::json &j, EmbedImageData &m); -}; - -struct EmbedThumbnailData { - std::string URL; // opt - std::string ProxyURL; // opt - int Height = 0; // opt - int Width = 0; // opt - - friend void from_json(const nlohmann::json &j, EmbedThumbnailData &m); -}; - -struct EmbedVideoData { - std::string URL; // opt - int Height = 0; // opt - int Width = 0; // opt - friend void from_json(const nlohmann::json &j, EmbedVideoData &m); -}; - -struct EmbedProviderData { - std::string Name; // opt - std::string URL; // opt, null (docs wrong) - - friend void from_json(const nlohmann::json &j, EmbedProviderData &m); -}; - -struct EmbedAuthorData { - std::string Name; // opt - std::string URL; // opt - std::string IconURL; // opt - std::string ProxyIconURL; // opt - - friend void from_json(const nlohmann::json &j, EmbedAuthorData &m); -}; - -struct EmbedFieldData { - std::string Name; // - std::string Value; // - bool Inline = false; // opt - - friend void from_json(const nlohmann::json &j, EmbedFieldData &m); -}; - -struct EmbedData { - std::string Title; // opt - std::string Type; // opt - std::string Description; // opt - std::string URL; // opt - std::string Timestamp; // opt - int Color = -1; // opt - EmbedFooterData Footer; // opt - EmbedImageData Image; // opt - EmbedThumbnailData Thumbnail; // opt - EmbedVideoData Video; // opt - EmbedProviderData Provider; // opt - EmbedAuthorData Author; // opt - std::vector Fields; // opt - - friend void from_json(const nlohmann::json &j, EmbedData &m); -}; - -struct MessageData { - Snowflake ID; // - Snowflake ChannelID; // - Snowflake GuildID; // opt - UserData Author; // - // GuildMemberData Member; // opt - std::string Content; // - std::string Timestamp; // - std::string EditedTimestamp; // null - bool IsTTS; // - bool DoesMentionEveryone; // - std::vector Mentions; // - // std::vector MentionRoles; // - // std::vector MentionChannels; // opt - // std::vector Attachments; // - std::vector Embeds; // - // std::vector Reactions; // opt - std::string Nonce; // opt - bool IsPinned; // - Snowflake WebhookID; // opt - MessageType Type; // - // MessageActivityData Activity; // opt - // MessageApplicationData Application; // opt - // MessageReferenceData MessageReference; // opt - MessageFlags Flags = MessageFlags::NONE; // opt - - friend void from_json(const nlohmann::json &j, MessageData &m); - void from_json_edited(const nlohmann::json &j); // for MESSAGE_UPDATE -}; - struct MessageDeleteData { Snowflake ID; // Snowflake ChannelID; // @@ -412,7 +66,7 @@ struct GuildMemberListUpdateMessage { }; struct MemberItem : Item { - UserData User; // + User User; // std::vector Roles; // // PresenceData Presence; // std::string PremiumSince; // opt @@ -422,12 +76,12 @@ struct GuildMemberListUpdateMessage { std::string HoistedRole; // null bool IsDefeaned; // - GuildMemberData GetAsMemberData() const; + GuildMember GetAsMemberData() const; friend void from_json(const nlohmann::json &j, MemberItem &m); private: - GuildMemberData m_member_data; + GuildMember m_member_data; }; struct OpObject { @@ -460,16 +114,16 @@ struct LazyLoadRequestMessage { }; struct ReadyEventData { - int GatewayVersion; // - UserData User; // - std::vector Guilds; // - std::string SessionID; // - std::vector PrivateChannels; // + int GatewayVersion; // + User User; // + std::vector Guilds; // + std::string SessionID; // + std::vector PrivateChannels; // // undocumented - std::string AnalyticsToken; // opt - int FriendSuggestionCount; // opt - UserSettingsData UserSettings; // opt + std::string AnalyticsToken; // opt + int FriendSuggestionCount; // opt + UserSettings UserSettings; // opt // std::vector ConnectedAccounts; // opt // std::map Consents; // opt // std::vector Experiments; // opt diff --git a/discord/role.cpp b/discord/role.cpp new file mode 100644 index 0000000..9e77f2b --- /dev/null +++ b/discord/role.cpp @@ -0,0 +1,15 @@ +#include "role.hpp" + +void from_json(const nlohmann::json &j, Role &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); + JS_D("permissions", m.PermissionsLegacy); + std::string tmp; + JS_D("permissions_new", tmp); + m.Permissions = std::stoull(tmp); + JS_D("managed", m.IsManaged); + JS_D("mentionable", m.IsMentionable); +} diff --git a/discord/role.hpp b/discord/role.hpp new file mode 100644 index 0000000..79f1038 --- /dev/null +++ b/discord/role.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "snowflake.hpp" +#include "json.hpp" +#include +#include + +struct Role { + Snowflake ID; + std::string Name; + int Color; + bool IsHoisted; + int Position; + int PermissionsLegacy; + uint64_t Permissions; + bool IsManaged; + bool IsMentionable; + + friend void from_json(const nlohmann::json &j, Role &m); +}; diff --git a/discord/snowflake.cpp b/discord/snowflake.cpp new file mode 100644 index 0000000..1158172 --- /dev/null +++ b/discord/snowflake.cpp @@ -0,0 +1,31 @@ +#include "snowflake.hpp" + +Snowflake::Snowflake() + : m_num(Invalid) {} + +Snowflake::Snowflake(const Snowflake &s) + : m_num(s.m_num) {} + +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; +}; + +bool Snowflake::IsValid() const { + return m_num != Invalid; +} + +void from_json(const nlohmann::json &j, Snowflake &s) { + std::string tmp; + j.get_to(tmp); + s.m_num = std::stoull(tmp); +} + +void to_json(nlohmann::json &j, const Snowflake &s) { + j = std::to_string(s); +} diff --git a/discord/snowflake.hpp b/discord/snowflake.hpp new file mode 100644 index 0000000..d16e236 --- /dev/null +++ b/discord/snowflake.hpp @@ -0,0 +1,50 @@ +#pragma once +#include +#include + +struct Snowflake { + Snowflake(); + Snowflake(const Snowflake &s); + Snowflake(uint64_t n); + Snowflake(const std::string &str); + + bool IsValid() 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 int Invalid = -1; + + 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/store.cpp b/discord/store.cpp index 454e1b9..badb46a 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -1,18 +1,18 @@ #include "store.hpp" -void Store::SetUser(Snowflake id, const UserData &user) { +void Store::SetUser(Snowflake id, const User &user) { m_users[id] = user; } -void Store::SetChannel(Snowflake id, const ChannelData &channel) { +void Store::SetChannel(Snowflake id, const Channel &channel) { m_channels[id] = channel; } -void Store::SetGuild(Snowflake id, const GuildData &guild) { +void Store::SetGuild(Snowflake id, const Guild &guild) { m_guilds[id] = guild; } -void Store::SetRole(Snowflake id, const RoleData &role) { +void Store::SetRole(Snowflake id, const Role &role) { m_roles[id] = role; } @@ -20,32 +20,32 @@ void Store::SetMessage(Snowflake id, const MessageData &message) { m_messages[id] = message; } -void Store::SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const GuildMemberData &data) { +void Store::SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const GuildMember &data) { m_members[guild_id][user_id] = data; } -const UserData *Store::GetUser(Snowflake id) const { +const User *Store::GetUser(Snowflake id) const { auto it = m_users.find(id); if (it == m_users.end()) return nullptr; return &it->second; } -const ChannelData *Store::GetChannel(Snowflake id) const { +const Channel *Store::GetChannel(Snowflake id) const { auto it = m_channels.find(id); if (it == m_channels.end()) return nullptr; return &it->second; } -const GuildData *Store::GetGuild(Snowflake id) const { +const Guild *Store::GetGuild(Snowflake id) const { auto it = m_guilds.find(id); if (it == m_guilds.end()) return nullptr; return &it->second; } -const RoleData *Store::GetRole(Snowflake id) const { +const Role *Store::GetRole(Snowflake id) const { auto it = m_roles.find(id); if (it == m_roles.end()) return nullptr; @@ -59,7 +59,7 @@ const MessageData *Store::GetMessage(Snowflake id) const { return &it->second; } -const GuildMemberData *Store::GetGuildMemberData(Snowflake guild_id, Snowflake user_id) const { +const GuildMember *Store::GetGuildMemberData(Snowflake guild_id, Snowflake user_id) const { auto git = m_members.find(guild_id); if (git == m_members.end()) return nullptr; diff --git a/discord/store.hpp b/discord/store.hpp index c0cbf40..124d126 100644 --- a/discord/store.hpp +++ b/discord/store.hpp @@ -9,26 +9,26 @@ class Store { public: - void SetUser(Snowflake id, const UserData &user); - void SetChannel(Snowflake id, const ChannelData &channel); - void SetGuild(Snowflake id, const GuildData &guild); - void SetRole(Snowflake id, const RoleData &role); + void SetUser(Snowflake id, const User &user); + void SetChannel(Snowflake id, const Channel &channel); + void SetGuild(Snowflake id, const Guild &guild); + void SetRole(Snowflake id, const Role &role); void SetMessage(Snowflake id, const MessageData &message); - void SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const GuildMemberData &data); + void SetGuildMemberData(Snowflake guild_id, Snowflake user_id, const GuildMember &data); - const UserData *GetUser(Snowflake id) const; - const ChannelData *GetChannel(Snowflake id) const; - const GuildData *GetGuild(Snowflake id) const; - const RoleData *GetRole(Snowflake id) const; + const User *GetUser(Snowflake id) const; + const Channel *GetChannel(Snowflake id) const; + const Guild *GetGuild(Snowflake id) const; + const Role *GetRole(Snowflake id) const; const MessageData *GetMessage(Snowflake id) const; - const GuildMemberData *GetGuildMemberData(Snowflake guild_id, Snowflake user_id) const; + const GuildMember *GetGuildMemberData(Snowflake guild_id, Snowflake user_id) const; - using users_type = std::unordered_map; - using channels_type = std::unordered_map; - using guilds_type = std::unordered_map; - using roles_type = std::unordered_map; + using users_type = std::unordered_map; + using channels_type = std::unordered_map; + using guilds_type = std::unordered_map; + using roles_type = std::unordered_map; using messages_type = std::unordered_map; - using members_type = std::unordered_map>; + using members_type = std::unordered_map>; const channels_type &GetChannels() const; const guilds_type &GetGuilds() const; diff --git a/discord/user.cpp b/discord/user.cpp new file mode 100644 index 0000000..a4c4424 --- /dev/null +++ b/discord/user.cpp @@ -0,0 +1,21 @@ +#include "user.hpp" + +void from_json(const nlohmann::json &j, User &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); +} diff --git a/discord/user.hpp b/discord/user.hpp new file mode 100644 index 0000000..1a6beaa --- /dev/null +++ b/discord/user.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "snowflake.hpp" +#include "json.hpp" +#include + +struct User { + Snowflake ID; // + std::string Username; // + std::string Discriminator; // + std::string Avatar; // null + bool IsBot = false; // opt + bool IsSystem = false; // opt + bool IsMFAEnabled = false; // opt + std::string Locale; // opt + bool IsVerified = false; // opt + std::string Email; // opt, null + int Flags = 0; // opt + int PremiumType = 0; // opt, null (docs wrong) + int PublicFlags = 0; // opt + + // undocumented (opt) + bool IsDesktop = false; // + bool IsMobile = false; // + bool IsNSFWAllowed = false; // null + std::string Phone; // null? + + friend void from_json(const nlohmann::json &j, User &m); +}; diff --git a/discord/usersettings.cpp b/discord/usersettings.cpp new file mode 100644 index 0000000..4cbf29c --- /dev/null +++ b/discord/usersettings.cpp @@ -0,0 +1,33 @@ +#include "usersettings.hpp" + +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 new file mode 100644 index 0000000..ed55761 --- /dev/null +++ b/discord/usersettings.hpp @@ -0,0 +1,38 @@ +#pragma once +#include "json.hpp" +#include "snowflake.hpp" +#include + +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; // + // 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); +};