From ac31bc6b94e422b929e63437696994b17002334d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 22 Mar 2021 01:30:51 -0400 Subject: [PATCH] basic member verification for guilds that set rules --- abaddon.cpp | 37 ++++++++++++++++--- abaddon.hpp | 5 ++- dialogs/verificationgate.cpp | 51 ++++++++++++++++++++++++++ dialogs/verificationgate.hpp | 22 +++++++++++ discord/channel.hpp | 3 ++ discord/discord.cpp | 71 +++++++++++++++++++++++++++++++++++- discord/discord.hpp | 23 ++++++++++-- discord/guild.cpp | 17 +++++++++ discord/guild.hpp | 22 ++++++++++- discord/objects.cpp | 49 +++++++++++++++++++++++++ discord/objects.hpp | 42 +++++++++++++++++++++ discord/store.cpp | 2 +- 12 files changed, 332 insertions(+), 12 deletions(-) create mode 100644 dialogs/verificationgate.cpp create mode 100644 dialogs/verificationgate.hpp diff --git a/abaddon.cpp b/abaddon.cpp index 4a32b5d..233b22e 100644 --- a/abaddon.cpp +++ b/abaddon.cpp @@ -9,6 +9,7 @@ #include "dialogs/confirm.hpp" #include "dialogs/setstatus.hpp" #include "dialogs/friendpicker.hpp" +#include "dialogs/verificationgate.hpp" #include "abaddon.hpp" #include "windows/guildsettingswindow.hpp" #include "windows/profilewindow.hpp" @@ -40,6 +41,7 @@ Abaddon::Abaddon() m_discord.signal_guild_update().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildUpdate)); m_discord.signal_reaction_add().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionAdd)); m_discord.signal_reaction_remove().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnReactionRemove)); + m_discord.signal_guild_join_request_create().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnGuildJoinRequestCreate)); m_discord.signal_disconnected().connect(sigc::mem_fun(*this, &Abaddon::DiscordOnDisconnect)); if (m_settings.GetPrefetch()) m_discord.signal_message_create().connect([this](Snowflake id) { @@ -185,8 +187,8 @@ void Abaddon::DiscordOnGuildMemberListUpdate(Snowflake guild_id) { m_main_window->UpdateMembers(); } -void Abaddon::DiscordOnGuildCreate(Snowflake guild_id) { - m_main_window->UpdateChannelsNewGuild(guild_id); +void Abaddon::DiscordOnGuildCreate(const GuildData &guild) { + m_main_window->UpdateChannelsNewGuild(guild.ID); } void Abaddon::DiscordOnGuildDelete(Snowflake guild_id) { @@ -217,6 +219,13 @@ void Abaddon::DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring m_main_window->UpdateChatReactionAdd(message_id, param); } +// this will probably cause issues when actual applications are rolled out but that doesn't seem like it will happen for a while +void Abaddon::DiscordOnGuildJoinRequestCreate(const GuildJoinRequestCreateData &data) { + if (data.Status == GuildApplicationStatus::STARTED) { + ShowGuildVerificationGateDialog(data.GuildID); + } +} + void Abaddon::DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code) { m_main_window->UpdateComponents(); if (close_code == GatewayCloseCode::AuthenticationFailed) { @@ -287,6 +296,19 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_ m_user_menu->popup_at_pointer(event); } +void Abaddon::ShowGuildVerificationGateDialog(Snowflake guild_id) { + VerificationGateDialog dlg(*m_main_window, guild_id); + if (dlg.run() == Gtk::RESPONSE_OK) { + const auto cb = [this](bool success) { + if (!success) { + Gtk::MessageDialog dlg(*m_main_window, "Failed to accept the verification gate.", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + dlg.run(); + } + }; + m_discord.AcceptVerificationGate(guild_id, dlg.GetVerificationGate(), cb); + } +} + void Abaddon::SetupUserMenu() { m_user_menu = Gtk::manage(new Gtk::Menu); m_user_menu_insert_mention = Gtk::manage(new Gtk::MenuItem("Insert Mention")); @@ -417,9 +439,6 @@ void Abaddon::ActionChannelOpened(Snowflake id) { if (id == m_main_window->GetChatActiveChannel()) return; const auto channel = m_discord.GetChannel(id); - if (channel->Type != ChannelType::DM && channel->Type != ChannelType::GROUP_DM) - m_discord.SendLazyLoad(id); - if (channel->Type == ChannelType::GUILD_TEXT || channel->Type == ChannelType::GUILD_NEWS) m_main_window->set_title(std::string(APP_TITLE) + " - #" + *channel->Name); else { @@ -445,6 +464,14 @@ void Abaddon::ActionChannelOpened(Snowflake id) { } else { m_main_window->UpdateChatWindowContents(); } + + if (channel->Type != ChannelType::DM && channel->Type != ChannelType::GROUP_DM) { + m_discord.SendLazyLoad(id); + + const auto request = m_discord.GetGuildApplication(*channel->GuildID); + if (request.has_value() && request->ApplicationStatus == GuildApplicationStatus::STARTED) + ShowGuildVerificationGateDialog(*channel->GuildID); + } } void Abaddon::ActionChatLoadHistory(Snowflake id) { diff --git a/abaddon.hpp b/abaddon.hpp index 0799ed3..8415bb7 100644 --- a/abaddon.hpp +++ b/abaddon.hpp @@ -66,7 +66,7 @@ public: void DiscordOnMessageDelete(Snowflake id, Snowflake channel_id); void DiscordOnMessageUpdate(Snowflake id, Snowflake channel_id); void DiscordOnGuildMemberListUpdate(Snowflake guild_id); - void DiscordOnGuildCreate(Snowflake guild_id); + void DiscordOnGuildCreate(const GuildData &guild); void DiscordOnGuildDelete(Snowflake guild_id); void DiscordOnChannelDelete(Snowflake channel_id); void DiscordOnChannelUpdate(Snowflake channel_id); @@ -74,6 +74,7 @@ public: void DiscordOnGuildUpdate(Snowflake guild_id); void DiscordOnReactionAdd(Snowflake message_id, const Glib::ustring ¶m); void DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring ¶m); + void DiscordOnGuildJoinRequestCreate(const GuildJoinRequestCreateData &data); void DiscordOnDisconnect(bool is_reconnecting, GatewayCloseCode close_code); const SettingsManager &GetSettings() const; @@ -83,6 +84,8 @@ public: void ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_id); protected: + void ShowGuildVerificationGateDialog(Snowflake guild_id); + void SetupUserMenu(); void ManageHeapWindow(Gtk::Window *window); diff --git a/dialogs/verificationgate.cpp b/dialogs/verificationgate.cpp new file mode 100644 index 0000000..bd034f2 --- /dev/null +++ b/dialogs/verificationgate.cpp @@ -0,0 +1,51 @@ +#include "verificationgate.hpp" +#include "../../abaddon.hpp" + +VerificationGateDialog::VerificationGateDialog(Gtk::Window &parent, Snowflake guild_id) + : Gtk::Dialog("Verification Required", parent, true) + , m_bbox(Gtk::ORIENTATION_HORIZONTAL) { + set_default_size(300, 300); + get_style_context()->add_class("app-window"); + get_style_context()->add_class("app-popup"); + + m_ok_button = add_button("Accept", Gtk::RESPONSE_OK); + + m_scroll_rules.set_vexpand(true); + m_scroll_rules.set_hexpand(true); + + m_description.set_line_wrap(true); + m_description.set_line_wrap_mode(Pango::WRAP_WORD_CHAR); + m_description.set_halign(Gtk::ALIGN_CENTER); + m_description.set_margin_bottom(5); + + m_scroll_rules.add(m_rules); + get_content_area()->add(m_description); + get_content_area()->add(m_scroll_rules); + show_all_children(); + + Abaddon::Get().GetDiscordClient().GetVerificationGateInfo(guild_id, sigc::mem_fun(*this, &VerificationGateDialog::OnVerificationGateFetch)); +} + +const VerificationGateInfoObject &VerificationGateDialog::GetVerificationGate() const { + return m_gate_info; +} + +void VerificationGateDialog::OnVerificationGateFetch(const std::optional &info) { + m_gate_info = *info; + if (m_gate_info.Description.has_value()) + m_description.set_markup("" + Glib::Markup::escape_text(*m_gate_info.Description) + ""); + else + m_description.hide(); + for (const auto &field : *info->VerificationFields) { + if (field.Type == "TERMS") { + for (const auto &rule : field.Values) { + auto *lbl = Gtk::manage(new Gtk::Label(rule)); + lbl->set_halign(Gtk::ALIGN_START); + lbl->set_ellipsize(Pango::ELLIPSIZE_END); + lbl->show(); + m_rules.add(*lbl); + } + break; + } + } +} diff --git a/dialogs/verificationgate.hpp b/dialogs/verificationgate.hpp new file mode 100644 index 0000000..31152b9 --- /dev/null +++ b/dialogs/verificationgate.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include "../../discord/objects.hpp" + +class VerificationGateDialog : public Gtk::Dialog { +public: + VerificationGateDialog(Gtk::Window &parent, Snowflake guild_id); + const VerificationGateInfoObject &GetVerificationGate() const; + +protected: + void OnVerificationGateFetch(const std::optional &info); + + VerificationGateInfoObject m_gate_info; + + Gtk::Label m_description; + Gtk::ScrolledWindow m_scroll_rules; + Gtk::ListBox m_rules; + Gtk::ButtonBox m_bbox; + + Gtk::Button *m_ok_button; +}; diff --git a/discord/channel.hpp b/discord/channel.hpp index 7b0cf50..4f93569 100644 --- a/discord/channel.hpp +++ b/discord/channel.hpp @@ -14,6 +14,9 @@ enum class ChannelType : int { GUILD_CATEGORY = 4, GUILD_NEWS = 5, GUILD_STORE = 6, + PUBLIC_THREAD = 11, + PRIVATE_THREAD = 12, + GUILD_STAGE_VOICE = 13, }; struct ChannelData { diff --git a/discord/discord.cpp b/discord/discord.cpp index a16a119..6d481d2 100644 --- a/discord/discord.cpp +++ b/discord/discord.cpp @@ -631,6 +631,12 @@ void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::sl }); } +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; +} + 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; @@ -753,6 +759,23 @@ void DiscordClient::FetchUserRelationships(Snowflake user_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) { + callback(CheckCode(response)); + }); +} + void DiscordClient::UpdateToken(std::string token) { if (!IsStarted()) { m_token = token; @@ -949,6 +972,15 @@ void DiscordClient::HandleGatewayMessage(std::string str) { 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; } } break; default: @@ -1033,6 +1065,10 @@ void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) { 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; @@ -1361,6 +1397,24 @@ void DiscordClient::HandleGatewayGuildEmojisUpdate(const GatewayMessage &msg) { 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::HandleGatewayReadySupplemental(const GatewayMessage &msg) { ReadySupplementalData data = msg.Data; for (const auto &p : data.MergedPresences.Friends) { @@ -1478,7 +1532,7 @@ void DiscordClient::HandleGatewayGuildCreate(const GatewayMessage &msg) { GuildData data = msg.Data; ProcessNewGuild(data); - m_signal_guild_create.emit(data.ID); + m_signal_guild_create.emit(data); } void DiscordClient::HandleGatewayGuildDelete(const GatewayMessage &msg) { @@ -1682,6 +1736,9 @@ void DiscordClient::LoadEventMap() { 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; } DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() { @@ -1791,3 +1848,15 @@ DiscordClient::type_signal_note_update DiscordClient::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; +} diff --git a/discord/discord.hpp b/discord/discord.hpp index 476adc8..fbcdb5f 100644 --- a/discord/discord.hpp +++ b/discord/discord.hpp @@ -131,6 +131,7 @@ public: 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; bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const; bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const; @@ -163,6 +164,9 @@ public: void SetUserNote(Snowflake user_id, std::string note, sigc::slot callback); void FetchUserRelationships(Snowflake user_id, sigc::slot)> callback); + 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); @@ -209,6 +213,9 @@ private: 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 HandleGatewayReadySupplemental(const GatewayMessage &msg); void HandleGatewayReconnect(const GatewayMessage &msg); void HandleGatewayInvalidSession(const GatewayMessage &msg); @@ -233,6 +240,7 @@ private: std::unordered_map> m_guild_to_users; std::unordered_map> m_guild_to_channels; + std::unordered_map m_guild_join_requests; std::unordered_map m_user_to_status; @@ -275,7 +283,7 @@ public: 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_create; typedef sigc::signal type_signal_guild_delete; typedef sigc::signal type_signal_channel_delete; typedef sigc::signal type_signal_channel_update; @@ -295,7 +303,10 @@ public: 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_disconnected; // bool true if reconnecting + 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_disconnected; // bool true if reconnecting typedef sigc::signal type_signal_connected; type_signal_gateway_ready signal_gateway_ready(); @@ -303,7 +314,7 @@ public: 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(); + 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(); @@ -323,6 +334,9 @@ public: 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_disconnected signal_disconnected(); type_signal_connected signal_connected(); @@ -352,6 +366,9 @@ protected: 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_disconnected m_signal_disconnected; type_signal_connected m_signal_connected; }; diff --git a/discord/guild.cpp b/discord/guild.cpp index 5fb524e..d387729 100644 --- a/discord/guild.cpp +++ b/discord/guild.cpp @@ -197,3 +197,20 @@ std::vector GuildData::FetchRoles() const { }); 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 index c2d7333..a57bc15 100644 --- a/discord/guild.hpp +++ b/discord/guild.hpp @@ -6,6 +6,26 @@ #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) @@ -32,7 +52,7 @@ struct GuildData { std::optional ExplicitContentFilter; std::optional> Roles; // only access id std::optional> Emojis; // only access id - std::optional> Features; + std::optional> Features; std::optional MFALevel; std::optional ApplicationID; // null std::optional IsWidgetEnabled; diff --git a/discord/objects.cpp b/discord/objects.cpp index 3aef9ee..7d44ece 100644 --- a/discord/objects.cpp +++ b/discord/objects.cpp @@ -125,6 +125,7 @@ void from_json(const nlohmann::json &j, ReadyEventData &m) { 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) { @@ -394,3 +395,51 @@ void from_json(const nlohmann::json &j, GuildEmojisUpdateObject &m) { 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); +} diff --git a/discord/objects.hpp b/discord/objects.hpp index 88cac21..bad9fd4 100644 --- a/discord/objects.hpp +++ b/discord/objects.hpp @@ -65,6 +65,9 @@ enum class GatewayEvent : int { USER_NOTE_UPDATE, READY_SUPPLEMENTAL, GUILD_EMOJIS_UPDATE, + GUILD_JOIN_REQUEST_CREATE, + GUILD_JOIN_REQUEST_UPDATE, + GUILD_JOIN_REQUEST_DELETE, }; enum class GatewayCloseCode : uint16_t { @@ -206,6 +209,7 @@ struct ReadyEventData { UserSettings Settings; std::optional>> MergedMembers; std::optional> Relationships; + std::optional> GuildJoinRequests; // std::vector ConnectedAccounts; // opt // std::map Consents; // opt // std::vector Experiments; // opt @@ -555,3 +559,41 @@ struct ModifyGuildEmojiObject { 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); +}; diff --git a/discord/store.cpp b/discord/store.cpp index 44af4cd..6dcaabb 100644 --- a/discord/store.cpp +++ b/discord/store.cpp @@ -449,7 +449,7 @@ std::optional Store::GetGuild(Snowflake id) const { for (const auto &id : nlohmann::json::parse(tmp).get>()) ret.Emojis->emplace_back().ID = id; Get(m_get_guild_stmt, 14, tmp); - ret.Features = nlohmann::json::parse(tmp).get>(); + ret.Features = nlohmann::json::parse(tmp).get>(); Get(m_get_guild_stmt, 15, ret.MFALevel); Get(m_get_guild_stmt, 16, ret.ApplicationID); Get(m_get_guild_stmt, 17, ret.IsWidgetEnabled);