basic member verification for guilds that set rules

This commit is contained in:
ouwou 2021-03-22 01:30:51 -04:00
parent 60404783bd
commit ac31bc6b94
12 changed files with 332 additions and 12 deletions

View File

@ -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) {

View File

@ -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 &param);
void DiscordOnReactionRemove(Snowflake message_id, const Glib::ustring &param);
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);

View File

@ -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<VerificationGateInfoObject> &info) {
m_gate_info = *info;
if (m_gate_info.Description.has_value())
m_description.set_markup("<b>" + Glib::Markup::escape_text(*m_gate_info.Description) + "</b>");
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;
}
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <gtkmm.h>
#include <optional>
#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<VerificationGateInfoObject> &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;
};

View File

@ -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 {

View File

@ -631,6 +631,12 @@ void DiscordClient::DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::sl
});
}
std::optional<GuildApplicationData> 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<void(st
});
}
void DiscordClient::GetVerificationGateInfo(Snowflake guild_id, sigc::slot<void(std::optional<VerificationGateInfoObject>)> 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<VerificationGateInfoObject>());
});
}
void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot<void(bool success)> 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;
}

View File

@ -131,6 +131,7 @@ public:
void ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot<void(bool success)> callback);
void ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot<void(bool success)> callback);
void DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot<void(bool success)> callback);
std::optional<GuildApplicationData> 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<void(bool success)> callback);
void FetchUserRelationships(Snowflake user_id, sigc::slot<void(std::vector<UserData>)> callback);
void GetVerificationGateInfo(Snowflake guild_id, sigc::slot<void(std::optional<VerificationGateInfoObject>)> callback);
void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot<void(bool success)> 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<Snowflake, std::unordered_set<Snowflake>> m_guild_to_users;
std::unordered_map<Snowflake, std::unordered_set<Snowflake>> m_guild_to_channels;
std::unordered_map<Snowflake, GuildApplicationData> m_guild_join_requests;
std::unordered_map<Snowflake, PresenceStatus> m_user_to_status;
@ -275,7 +283,7 @@ public:
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_message_delete;
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_message_update;
typedef sigc::signal<void, Snowflake> type_signal_guild_member_list_update;
typedef sigc::signal<void, Snowflake> type_signal_guild_create;
typedef sigc::signal<void, GuildData> type_signal_guild_create;
typedef sigc::signal<void, Snowflake> type_signal_guild_delete;
typedef sigc::signal<void, Snowflake> type_signal_channel_delete;
typedef sigc::signal<void, Snowflake> type_signal_channel_update;
@ -295,7 +303,10 @@ public:
typedef sigc::signal<void, Snowflake, PresenceStatus> type_signal_presence_update;
typedef sigc::signal<void, Snowflake, std::string> type_signal_note_update;
typedef sigc::signal<void, Snowflake, std::vector<EmojiData>> type_signal_guild_emojis_update; // guild id
typedef sigc::signal<void, bool, GatewayCloseCode> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void, GuildJoinRequestCreateData> type_signal_guild_join_request_create;
typedef sigc::signal<void, GuildJoinRequestUpdateData> type_signal_guild_join_request_update;
typedef sigc::signal<void, GuildJoinRequestDeleteData> type_signal_guild_join_request_delete;
typedef sigc::signal<void, bool, GatewayCloseCode> type_signal_disconnected; // bool true if reconnecting
typedef sigc::signal<void> 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;
};

View File

@ -197,3 +197,20 @@ std::vector<RoleData> 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<std::string_view>();
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);
}

View File

@ -6,6 +6,26 @@
#include "emoji.hpp"
#include <vector>
#include <string>
#include <unordered_set>
enum class GuildApplicationStatus {
STARTED,
PENDING,
REJECTED,
APPROVED,
UNKNOWN,
};
struct GuildApplicationData {
Snowflake UserID;
Snowflake GuildID;
GuildApplicationStatus ApplicationStatus;
std::optional<std::string> RejectionReason;
std::optional<std::string> LastSeen;
std::optional<std::string> 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<int> ExplicitContentFilter;
std::optional<std::vector<RoleData>> Roles; // only access id
std::optional<std::vector<EmojiData>> Emojis; // only access id
std::optional<std::vector<std::string>> Features;
std::optional<std::unordered_set<std::string>> Features;
std::optional<int> MFALevel;
std::optional<Snowflake> ApplicationID; // null
std::optional<bool> IsWidgetEnabled;

View File

@ -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<std::string_view>();
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);
}

View File

@ -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<std::vector<std::vector<GuildMember>>> MergedMembers;
std::optional<std::vector<RelationshipData>> Relationships;
std::optional<std::vector<GuildApplicationData>> GuildJoinRequests;
// std::vector<Unknown> ConnectedAccounts; // opt
// std::map<std::string, Unknown> Consents; // opt
// std::vector<Unknown> 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<std::string> Values;
std::optional<bool> 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<std::string> Description;
std::optional<std::vector<VerificationFieldObject>> VerificationFields;
std::optional<std::string> Version;
std::optional<bool> 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);
};

View File

@ -449,7 +449,7 @@ std::optional<GuildData> Store::GetGuild(Snowflake id) const {
for (const auto &id : nlohmann::json::parse(tmp).get<std::vector<Snowflake>>())
ret.Emojis->emplace_back().ID = id;
Get(m_get_guild_stmt, 14, tmp);
ret.Features = nlohmann::json::parse(tmp).get<std::vector<std::string>>();
ret.Features = nlohmann::json::parse(tmp).get<std::unordered_set<std::string>>();
Get(m_get_guild_stmt, 15, ret.MFALevel);
Get(m_get_guild_stmt, 16, ret.ApplicationID);
Get(m_get_guild_stmt, 17, ret.IsWidgetEnabled);