forked from OpenGamers/abaddon
view and delete invites, more tweaking
This commit is contained in:
@@ -178,15 +178,17 @@ void Completer::CompleteEmojis(const Glib::ustring &term) {
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
for (const auto tmp : guild->Emojis) {
|
||||
const auto emoji = discord.GetEmoji(tmp.ID);
|
||||
if (!emoji.has_value()) continue;
|
||||
if (emoji->IsAnimated.has_value() && *emoji->IsAnimated) continue;
|
||||
if (term.size() > 0)
|
||||
if (!StringContainsCaseless(emoji->Name, term)) continue;
|
||||
if (i++ > MaxCompleterEntries) break;
|
||||
if (guild->Emojis.has_value()) {
|
||||
for (const auto tmp : *guild->Emojis) {
|
||||
const auto emoji = discord.GetEmoji(tmp.ID);
|
||||
if (!emoji.has_value()) continue;
|
||||
if (emoji->IsAnimated.has_value() && *emoji->IsAnimated) continue;
|
||||
if (term.size() > 0)
|
||||
if (!StringContainsCaseless(emoji->Name, term)) continue;
|
||||
if (i++ > MaxCompleterEntries) break;
|
||||
|
||||
const auto entry = make_entry(emoji->Name, "<:" + emoji->Name + ":" + std::to_string(emoji->ID) + ">", emoji->GetURL());
|
||||
const auto entry = make_entry(emoji->Name, "<:" + emoji->Name + ":" + std::to_string(emoji->ID) + ">", emoji->GetURL());
|
||||
}
|
||||
}
|
||||
|
||||
// if <15 guild emojis match then load up stock
|
||||
|
||||
@@ -56,25 +56,21 @@ void JoinGuildDialog::on_entry_changed() {
|
||||
}
|
||||
|
||||
void JoinGuildDialog::CheckCode() {
|
||||
// clang-format off
|
||||
Abaddon::Get().GetDiscordClient().FetchInviteData(
|
||||
m_code,
|
||||
[this](Invite invite) {
|
||||
m_ok.set_sensitive(true);
|
||||
if (invite.Members != -1)
|
||||
m_info.set_text(invite.Guild.Name + " (" + std::to_string(invite.Members) + " members)");
|
||||
auto cb = [this](const std::optional<InviteData> &invite) {
|
||||
if (invite.has_value()) {
|
||||
m_ok.set_sensitive(true);
|
||||
if (invite->Guild.has_value()) {
|
||||
if (invite->MemberCount.has_value())
|
||||
m_info.set_text(invite->Guild->Name + " (" + std::to_string(*invite->MemberCount) + " members)");
|
||||
else
|
||||
m_info.set_text(invite.Guild.Name);
|
||||
},
|
||||
[this](bool not_found) {
|
||||
m_info.set_text(invite->Guild->Name);
|
||||
}
|
||||
} else {
|
||||
m_ok.set_sensitive(false);
|
||||
if (not_found)
|
||||
m_info.set_text("Invalid invite");
|
||||
else
|
||||
m_info.set_text("HTTP error (try again)");
|
||||
m_info.set_text("Invalid invite");
|
||||
}
|
||||
);
|
||||
// clang-format on
|
||||
};
|
||||
Abaddon::Get().GetDiscordClient().FetchInvite(m_code, sigc::track_obj(cb, *this));
|
||||
}
|
||||
|
||||
bool JoinGuildDialog::IsCode(std::string str) {
|
||||
|
||||
@@ -98,14 +98,17 @@ std::set<Snowflake> DiscordClient::GetMessagesForChannel(Snowflake id) const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DiscordClient::FetchInviteData(std::string code, std::function<void(Invite)> cb, std::function<void(bool)> err) {
|
||||
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, cb, err](cpr::Response r) {
|
||||
void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback) {
|
||||
sigc::signal<void, std::optional<InviteData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](cpr::Response r) {
|
||||
if (!CheckCode(r)) {
|
||||
err(r.status_code == 404);
|
||||
if (r.status_code == 404)
|
||||
callback(std::nullopt);
|
||||
return;
|
||||
};
|
||||
|
||||
cb(nlohmann::json::parse(r.text));
|
||||
callback(nlohmann::json::parse(r.text).get<InviteData>());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -483,6 +486,18 @@ void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<
|
||||
});
|
||||
}
|
||||
|
||||
void DiscordClient::DeleteInvite(const std::string &code) {
|
||||
DeleteInvite(code, [](const auto) {});
|
||||
}
|
||||
|
||||
void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(bool success)> callback) {
|
||||
sigc::signal<void, bool> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeDELETE("/invites/" + code, [this, callback](const cpr::Response &response) {
|
||||
callback(CheckCode(response));
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<BanData> DiscordClient::GetBansInGuild(Snowflake guild_id) {
|
||||
return m_store.GetBans(guild_id);
|
||||
}
|
||||
@@ -491,7 +506,7 @@ void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::s
|
||||
sigc::signal<void, BanData> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const cpr::Response &response) {
|
||||
if (response.status_code != 200) return;
|
||||
if (!CheckCode(response)) return;
|
||||
auto ban = nlohmann::json::parse(response.text).get<BanData>();
|
||||
m_store.SetBan(guild_id, ban.User.ID, ban);
|
||||
m_store.SetUser(ban.User.ID, ban.User);
|
||||
@@ -503,6 +518,7 @@ void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vect
|
||||
sigc::signal<void, std::vector<BanData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const cpr::Response &response) {
|
||||
if (!CheckCode(response)) return;
|
||||
auto bans = nlohmann::json::parse(response.text).get<std::vector<BanData>>();
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &ban : bans) {
|
||||
@@ -514,6 +530,24 @@ void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vect
|
||||
});
|
||||
}
|
||||
|
||||
void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback) {
|
||||
sigc::signal<void, std::vector<InviteData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const cpr::Response &response) {
|
||||
// store?
|
||||
if (!CheckCode(response)) return;
|
||||
auto invites = nlohmann::json::parse(response.text).get<std::vector<InviteData>>();
|
||||
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &invite : invites)
|
||||
if (invite.Inviter.has_value())
|
||||
m_store.SetUser(invite.Inviter->ID, *invite.Inviter);
|
||||
m_store.EndTransaction();
|
||||
|
||||
callback(invites);
|
||||
});
|
||||
}
|
||||
|
||||
void DiscordClient::UpdateToken(std::string token) {
|
||||
if (!IsStarted()) {
|
||||
m_token = token;
|
||||
@@ -676,6 +710,12 @@ void DiscordClient::HandleGatewayMessage(std::string str) {
|
||||
case GatewayEvent::GUILD_BAN_ADD: {
|
||||
HandleGatewayGuildBanAdd(m);
|
||||
} break;
|
||||
case GatewayEvent::INVITE_CREATE: {
|
||||
HandleGatewayInviteCreate(m);
|
||||
} break;
|
||||
case GatewayEvent::INVITE_DELETE: {
|
||||
HandleGatewayInviteDelete(m);
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
@@ -719,10 +759,10 @@ void DiscordClient::ProcessNewGuild(GuildData &guild) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &r : guild.Roles)
|
||||
for (auto &r : *guild.Roles)
|
||||
m_store.SetRole(r.ID, r);
|
||||
|
||||
for (auto &e : guild.Emojis)
|
||||
for (auto &e : *guild.Emojis)
|
||||
m_store.SetEmoji(e.ID, e);
|
||||
|
||||
m_store.EndTransaction();
|
||||
@@ -1000,6 +1040,32 @@ void DiscordClient::HandleGatewayGuildBanAdd(const GatewayMessage &msg) {
|
||||
m_signal_guild_ban_add.emit(data.GuildID, data.User.ID);
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayInviteCreate(const GatewayMessage &msg) {
|
||||
InviteCreateObject data = msg.Data;
|
||||
InviteData invite;
|
||||
invite.Code = std::move(data.Code);
|
||||
invite.CreatedAt = std::move(data.CreatedAt);
|
||||
invite.Channel = *m_store.GetChannel(data.ChannelID);
|
||||
invite.Guild = *m_store.GetGuild(*invite.Channel->GuildID);
|
||||
invite.Inviter = std::move(data.Inviter);
|
||||
invite.IsTemporary = std::move(data.IsTemporary);
|
||||
invite.MaxAge = std::move(data.MaxAge);
|
||||
invite.MaxUses = std::move(data.MaxUses);
|
||||
invite.TargetUser = std::move(data.TargetUser);
|
||||
invite.TargetUserType = std::move(data.TargetUserType);
|
||||
invite.Uses = std::move(data.Uses);
|
||||
m_signal_invite_create.emit(invite);
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayInviteDelete(const GatewayMessage &msg) {
|
||||
InviteDeleteObject data = msg.Data;
|
||||
if (!data.GuildID.has_value()) {
|
||||
const auto chan = GetChannel(data.ChannelID);
|
||||
data.GuildID = chan->ID;
|
||||
}
|
||||
m_signal_invite_delete.emit(data);
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayReconnect(const GatewayMessage &msg) {
|
||||
m_signal_disconnected.emit(true);
|
||||
inflateEnd(&m_zstream);
|
||||
@@ -1223,6 +1289,8 @@ void DiscordClient::LoadEventMap() {
|
||||
m_event_map["TYPING_START"] = GatewayEvent::TYPING_START;
|
||||
m_event_map["GUILD_BAN_REMOVE"] = GatewayEvent::GUILD_BAN_REMOVE;
|
||||
m_event_map["GUILD_BAN_ADD"] = GatewayEvent::GUILD_BAN_ADD;
|
||||
m_event_map["INVITE_CREATE"] = GatewayEvent::INVITE_CREATE;
|
||||
m_event_map["INVITE_DELETE"] = GatewayEvent::INVITE_DELETE;
|
||||
}
|
||||
|
||||
DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() {
|
||||
@@ -1312,3 +1380,11 @@ DiscordClient::type_signal_guild_ban_remove DiscordClient::signal_guild_ban_remo
|
||||
DiscordClient::type_signal_guild_ban_add DiscordClient::signal_guild_ban_add() {
|
||||
return m_signal_guild_ban_add;
|
||||
}
|
||||
|
||||
DiscordClient::type_signal_invite_create DiscordClient::signal_invite_create() {
|
||||
return m_signal_invite_create;
|
||||
}
|
||||
|
||||
DiscordClient::type_signal_invite_delete DiscordClient::signal_invite_delete() {
|
||||
return m_signal_invite_delete;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ public:
|
||||
std::set<Snowflake> GetMessagesForChannel(Snowflake id) const;
|
||||
std::set<Snowflake> GetPrivateChannels() const;
|
||||
|
||||
void FetchInviteData(std::string code, std::function<void(Invite)> cb, std::function<void(bool)> err);
|
||||
void FetchMessagesInChannel(Snowflake id, std::function<void(const std::vector<Snowflake> &)> cb);
|
||||
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function<void(const std::vector<Snowflake> &)> cb);
|
||||
std::optional<Message> GetMessage(Snowflake id) const;
|
||||
@@ -117,12 +116,17 @@ public:
|
||||
void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(bool success)> callback);
|
||||
void UnbanUser(Snowflake guild_id, Snowflake user_id);
|
||||
void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback);
|
||||
void DeleteInvite(const std::string &code);
|
||||
void DeleteInvite(const std::string &code, sigc::slot<void(bool success)> callback);
|
||||
|
||||
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
|
||||
std::vector<BanData> GetBansInGuild(Snowflake guild_id);
|
||||
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback);
|
||||
void FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback);
|
||||
|
||||
void FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback);
|
||||
void FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback);
|
||||
|
||||
void UpdateToken(std::string token);
|
||||
void SetUserAgent(std::string agent);
|
||||
|
||||
@@ -161,6 +165,8 @@ private:
|
||||
void HandleGatewayTypingStart(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildBanRemove(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildBanAdd(const GatewayMessage &msg);
|
||||
void HandleGatewayInviteCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayInviteDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayReconnect(const GatewayMessage &msg);
|
||||
void HeartbeatThread();
|
||||
void SendIdentify();
|
||||
@@ -231,7 +237,9 @@ public:
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_member_update; // guild id, user id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_remove; // guild id, user id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_add; // guild id, user id
|
||||
typedef sigc::signal<void, bool> type_signal_disconnected; // bool true if reconnecting
|
||||
typedef sigc::signal<void, InviteData> type_signal_invite_create;
|
||||
typedef sigc::signal<void, InviteDeleteObject> type_signal_invite_delete;
|
||||
typedef sigc::signal<void, bool> type_signal_disconnected; // bool true if reconnecting
|
||||
typedef sigc::signal<void> type_signal_connected;
|
||||
|
||||
type_signal_gateway_ready signal_gateway_ready();
|
||||
@@ -254,6 +262,8 @@ public:
|
||||
type_signal_guild_member_update signal_guild_member_update();
|
||||
type_signal_guild_ban_remove signal_guild_ban_remove();
|
||||
type_signal_guild_ban_add signal_guild_ban_add();
|
||||
type_signal_invite_create signal_invite_create();
|
||||
type_signal_invite_delete signal_invite_delete(); // safe to assume guild id is set
|
||||
type_signal_disconnected signal_disconnected();
|
||||
type_signal_connected signal_connected();
|
||||
|
||||
@@ -278,6 +288,8 @@ protected:
|
||||
type_signal_guild_member_update m_signal_guild_member_update;
|
||||
type_signal_guild_ban_remove m_signal_guild_ban_remove;
|
||||
type_signal_guild_ban_add m_signal_guild_ban_add;
|
||||
type_signal_invite_create m_signal_invite_create;
|
||||
type_signal_invite_delete m_signal_invite_delete;
|
||||
type_signal_disconnected m_signal_disconnected;
|
||||
type_signal_connected m_signal_connected;
|
||||
};
|
||||
|
||||
@@ -13,29 +13,29 @@ void from_json(const nlohmann::json &j, GuildData &m) {
|
||||
JS_N("splash", m.Splash);
|
||||
JS_ON("discovery_splash", m.DiscoverySplash);
|
||||
JS_O("owner", m.IsOwner);
|
||||
JS_D("owner_id", m.OwnerID);
|
||||
JS_O("owner_id", m.OwnerID);
|
||||
std::optional<std::string> tmp;
|
||||
JS_O("permissions", tmp);
|
||||
if (tmp.has_value())
|
||||
m.Permissions = std::stoull(*tmp);
|
||||
JS_D("region", m.VoiceRegion);
|
||||
JS_N("afk_channel_id", m.AFKChannelID);
|
||||
JS_D("afk_timeout", m.AFKTimeout);
|
||||
JS_O("region", m.VoiceRegion);
|
||||
JS_ON("afk_channel_id", m.AFKChannelID);
|
||||
JS_O("afk_timeout", m.AFKTimeout);
|
||||
JS_O("embed_enabled", m.IsEmbedEnabled);
|
||||
JS_ON("embed_channel_id", m.EmbedChannelID);
|
||||
JS_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("verification_level", m.VerificationLevel);
|
||||
JS_O("default_message_notifications", m.DefaultMessageNotifications);
|
||||
JS_O("explicit_content_filter", m.ExplicitContentFilter);
|
||||
JS_O("roles", m.Roles);
|
||||
JS_O("emojis", m.Emojis);
|
||||
JS_O("features", m.Features);
|
||||
JS_O("mfa_level", m.MFALevel);
|
||||
JS_ON("application_id", m.ApplicationID);
|
||||
JS_O("widget_enabled", m.IsWidgetEnabled);
|
||||
JS_ON("widget_channel_id", m.WidgetChannelID);
|
||||
JS_N("system_channel_id", m.SystemChannelID);
|
||||
JS_D("system_channel_flags", m.SystemChannelFlags);
|
||||
JS_N("rules_channel_id", m.RulesChannelID);
|
||||
JS_ON("system_channel_id", m.SystemChannelID);
|
||||
JS_O("system_channel_flags", m.SystemChannelFlags);
|
||||
JS_ON("rules_channel_id", m.RulesChannelID);
|
||||
JS_O("joined_at", m.JoinedAt);
|
||||
JS_O("large", m.IsLarge);
|
||||
JS_O("unavailable", m.IsUnavailable);
|
||||
@@ -46,13 +46,13 @@ void from_json(const nlohmann::json &j, GuildData &m) {
|
||||
// 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_ON("vanity_url_code", m.VanityURL);
|
||||
JS_ON("description", m.Description);
|
||||
JS_ON("banner", m.BannerHash);
|
||||
JS_O("premium_tier", m.PremiumTier);
|
||||
JS_O("premium_subscription_count", m.PremiumSubscriptionCount);
|
||||
JS_D("preferred_locale", m.PreferredLocale);
|
||||
JS_N("public_updates_channel_id", m.PublicUpdatesChannelID);
|
||||
JS_O("preferred_locale", m.PreferredLocale);
|
||||
JS_ON("public_updates_channel_id", m.PublicUpdatesChannelID);
|
||||
JS_O("max_video_channel_users", m.MaxVideoChannelUsers);
|
||||
JS_O("approximate_member_count", tmp);
|
||||
if (tmp.has_value())
|
||||
@@ -119,7 +119,8 @@ void GuildData::update_from_json(const nlohmann::json &j) {
|
||||
}
|
||||
|
||||
bool GuildData::HasFeature(const std::string &search_feature) {
|
||||
for (const auto &feature : Features)
|
||||
if (!Features.has_value()) return false;
|
||||
for (const auto &feature : *Features)
|
||||
if (search_feature == feature)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
// a bot is apparently only supposed to receive the `id` and `unavailable` as false
|
||||
// but user tokens seem to get the full objects (minus users)
|
||||
|
||||
// everythings optional cuz of muh partial guild object
|
||||
// anything not marked optional in https://discord.com/developers/docs/resources/guild#guild-object is guaranteed to be set when returned from Store::GetGuild
|
||||
struct GuildData {
|
||||
Snowflake ID;
|
||||
std::string Name;
|
||||
@@ -16,44 +19,44 @@ struct GuildData {
|
||||
std::string Splash; // null
|
||||
std::optional<std::string> DiscoverySplash; // null
|
||||
std::optional<bool> IsOwner;
|
||||
Snowflake OwnerID;
|
||||
std::optional<Snowflake> OwnerID;
|
||||
std::optional<uint64_t> Permissions;
|
||||
std::optional<std::string> PermissionsNew;
|
||||
std::optional<std::string> VoiceRegion;
|
||||
Snowflake AFKChannelID; // null
|
||||
int AFKTimeout;
|
||||
std::optional<Snowflake> AFKChannelID; // null
|
||||
std::optional<int> AFKTimeout;
|
||||
std::optional<bool> IsEmbedEnabled; // deprecated
|
||||
std::optional<Snowflake> EmbedChannelID; // null, deprecated
|
||||
int VerificationLevel;
|
||||
int DefaultMessageNotifications;
|
||||
int ExplicitContentFilter;
|
||||
std::vector<RoleData> Roles; // only access id
|
||||
std::vector<EmojiData> Emojis; // only access id
|
||||
std::vector<std::string> Features;
|
||||
int MFALevel;
|
||||
Snowflake ApplicationID; // null
|
||||
std::optional<int> VerificationLevel;
|
||||
std::optional<int> DefaultMessageNotifications;
|
||||
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<int> MFALevel;
|
||||
std::optional<Snowflake> ApplicationID; // null
|
||||
std::optional<bool> IsWidgetEnabled;
|
||||
std::optional<Snowflake> WidgetChannelID; // null
|
||||
Snowflake SystemChannelID; // null
|
||||
int SystemChannelFlags;
|
||||
Snowflake RulesChannelID; // null
|
||||
std::optional<std::string> JoinedAt; // *
|
||||
std::optional<bool> IsLarge; // *
|
||||
std::optional<bool> IsUnavailable; // *
|
||||
std::optional<int> MemberCount; // *
|
||||
std::optional<Snowflake> SystemChannelID; // null
|
||||
std::optional<int> SystemChannelFlags;
|
||||
std::optional<Snowflake> RulesChannelID; // null
|
||||
std::optional<std::string> JoinedAt; // *
|
||||
std::optional<bool> IsLarge; // *
|
||||
std::optional<bool> IsUnavailable; // *
|
||||
std::optional<int> MemberCount; // *
|
||||
// std::vector<VoiceStateData> VoiceStates; // opt*
|
||||
// std::vector<MemberData> Members; // opt* - incomplete anyways
|
||||
std::optional<std::vector<ChannelData>> Channels; // *
|
||||
// std::vector<PresenceUpdateData> Presences; // opt*
|
||||
std::optional<int> MaxPresences; // null
|
||||
std::optional<int> MaxMembers;
|
||||
std::string VanityURL; // null
|
||||
std::string Description; // null
|
||||
std::string BannerHash; // null
|
||||
int PremiumTier;
|
||||
std::optional<std::string> VanityURL; // null
|
||||
std::optional<std::string> Description; // null
|
||||
std::optional<std::string> BannerHash; // null
|
||||
std::optional<int> PremiumTier;
|
||||
std::optional<int> PremiumSubscriptionCount;
|
||||
std::string PreferredLocale;
|
||||
Snowflake PublicUpdatesChannelID; // null
|
||||
std::optional<std::string> PreferredLocale;
|
||||
std::optional<Snowflake> PublicUpdatesChannelID; // null
|
||||
std::optional<int> MaxVideoChannelUsers;
|
||||
std::optional<int> ApproximateMemberCount;
|
||||
std::optional<int> ApproximatePresenceCount;
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
#include "invite.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, Invite &m) {
|
||||
void from_json(const nlohmann::json &j, InviteData &m) {
|
||||
JS_D("code", m.Code);
|
||||
JS_O("guild", m.Guild);
|
||||
JS_O("channel", m.Channel);
|
||||
JS_O("inviter", m.Inviter);
|
||||
JS_O("approximate_member_count", m.Members);
|
||||
|
||||
if (j.contains("guild")) {
|
||||
auto x = j.at("guild");
|
||||
x.at("id").get_to(m.Guild.ID);
|
||||
x.at("name").get_to(m.Guild.Name);
|
||||
}
|
||||
JS_O("target_user", m.TargetUser);
|
||||
JS_O("target_user_type", m.TargetUserType);
|
||||
JS_O("approximate_presence_count", m.PresenceCount);
|
||||
JS_O("approximate_member_count", m.MemberCount);
|
||||
JS_O("uses", m.Uses);
|
||||
JS_O("max_uses", m.MaxUses);
|
||||
JS_O("max_age", m.MaxAge);
|
||||
JS_O("temporary", m.IsTemporary);
|
||||
JS_O("created_at", m.CreatedAt);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,25 @@
|
||||
#include "guild.hpp"
|
||||
#include <string>
|
||||
|
||||
class Invite {
|
||||
public:
|
||||
std::string Code; //
|
||||
GuildData Guild; // opt
|
||||
ChannelData Channel; // opt
|
||||
UserData Inviter; // opt
|
||||
int Members = -1; // opt
|
||||
|
||||
friend void from_json(const nlohmann::json &j, Invite &m);
|
||||
enum class ETargetUserType {
|
||||
STREAM = 1
|
||||
};
|
||||
|
||||
class InviteData {
|
||||
public:
|
||||
std::string Code;
|
||||
std::optional<GuildData> Guild;
|
||||
std::optional<ChannelData> Channel;
|
||||
std::optional<UserData> Inviter;
|
||||
std::optional<UserData> TargetUser;
|
||||
std::optional<ETargetUserType> TargetUserType;
|
||||
std::optional<int> PresenceCount;
|
||||
std::optional<int> MemberCount;
|
||||
std::optional<int> Uses;
|
||||
std::optional<int> MaxUses;
|
||||
std::optional<int> MaxAge;
|
||||
std::optional<bool> IsTemporary;
|
||||
std::optional<std::string> CreatedAt;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteData &m);
|
||||
};
|
||||
|
||||
@@ -262,3 +262,23 @@ void from_json(const nlohmann::json &j, GuildBanAddObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteCreateObject &m) {
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_D("code", m.Code);
|
||||
JS_D("created_at", m.CreatedAt);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_O("inviter", m.Inviter);
|
||||
JS_D("max_age", m.MaxAge);
|
||||
JS_D("max_uses", m.MaxUses);
|
||||
JS_O("target_user", m.TargetUser);
|
||||
JS_O("target_user_type", m.TargetUserType);
|
||||
JS_D("temporary", m.IsTemporary);
|
||||
JS_D("uses", m.Uses);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteDeleteObject &m) {
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("code", m.Code);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ enum class GatewayEvent : int {
|
||||
TYPING_START,
|
||||
GUILD_BAN_REMOVE,
|
||||
GUILD_BAN_ADD,
|
||||
INVITE_CREATE,
|
||||
INVITE_DELETE,
|
||||
};
|
||||
|
||||
struct GatewayMessage {
|
||||
@@ -370,3 +372,27 @@ struct GuildBanAddObject {
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildBanAddObject &m);
|
||||
};
|
||||
|
||||
struct InviteCreateObject {
|
||||
Snowflake ChannelID;
|
||||
std::string Code;
|
||||
std::string CreatedAt;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::optional<UserData> Inviter;
|
||||
int MaxAge;
|
||||
int MaxUses;
|
||||
UserData TargetUser;
|
||||
std::optional<ETargetUserType> TargetUserType;
|
||||
bool IsTemporary;
|
||||
int Uses;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteCreateObject &m);
|
||||
};
|
||||
|
||||
struct InviteDeleteObject {
|
||||
Snowflake ChannelID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::string Code;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteDeleteObject &m);
|
||||
};
|
||||
|
||||
@@ -148,12 +148,23 @@ void Store::SetGuild(Snowflake id, const GuildData &guild) {
|
||||
Bind(m_set_guild_stmt, 11, guild.VerificationLevel);
|
||||
Bind(m_set_guild_stmt, 12, guild.DefaultMessageNotifications);
|
||||
std::vector<Snowflake> snowflakes;
|
||||
for (const auto &x : guild.Roles) snowflakes.push_back(x.ID);
|
||||
Bind(m_set_guild_stmt, 13, nlohmann::json(snowflakes).dump());
|
||||
if (guild.Roles.has_value()) {
|
||||
for (const auto &x : *guild.Roles) snowflakes.push_back(x.ID);
|
||||
Bind(m_set_guild_stmt, 13, nlohmann::json(snowflakes).dump());
|
||||
} else {
|
||||
Bind(m_set_guild_stmt, 13, "[]"s);
|
||||
}
|
||||
snowflakes.clear();
|
||||
for (const auto &x : guild.Emojis) snowflakes.push_back(x.ID);
|
||||
Bind(m_set_guild_stmt, 14, nlohmann::json(snowflakes).dump());
|
||||
Bind(m_set_guild_stmt, 15, nlohmann::json(guild.Features).dump());
|
||||
if (guild.Emojis.has_value()) {
|
||||
for (const auto &x : *guild.Emojis) snowflakes.push_back(x.ID);
|
||||
Bind(m_set_guild_stmt, 14, nlohmann::json(snowflakes).dump());
|
||||
} else {
|
||||
Bind(m_set_guild_stmt, 14, "[]"s);
|
||||
}
|
||||
if (guild.Features.has_value())
|
||||
Bind(m_set_guild_stmt, 15, nlohmann::json(*guild.Features).dump());
|
||||
else
|
||||
Bind(m_set_guild_stmt, 15, "[]"s);
|
||||
Bind(m_set_guild_stmt, 16, guild.MFALevel);
|
||||
Bind(m_set_guild_stmt, 17, guild.ApplicationID);
|
||||
Bind(m_set_guild_stmt, 18, guild.IsWidgetEnabled);
|
||||
@@ -430,11 +441,13 @@ std::optional<GuildData> Store::GetGuild(Snowflake id) const {
|
||||
Get(m_get_guild_stmt, 11, ret.DefaultMessageNotifications);
|
||||
std::string tmp;
|
||||
Get(m_get_guild_stmt, 12, tmp);
|
||||
ret.Roles.emplace();
|
||||
for (const auto &id : nlohmann::json::parse(tmp).get<std::vector<Snowflake>>())
|
||||
ret.Roles.emplace_back().ID = id;
|
||||
ret.Roles->emplace_back().ID = id;
|
||||
Get(m_get_guild_stmt, 13, tmp);
|
||||
ret.Emojis.emplace();
|
||||
for (const auto &id : nlohmann::json::parse(tmp).get<std::vector<Snowflake>>())
|
||||
ret.Emojis.emplace_back().ID = id;
|
||||
ret.Emojis->emplace_back().ID = id;
|
||||
Get(m_get_guild_stmt, 14, tmp);
|
||||
ret.Features = nlohmann::json::parse(tmp).get<std::vector<std::string>>();
|
||||
Get(m_get_guild_stmt, 15, ret.MFALevel);
|
||||
|
||||
35
util.cpp
35
util.cpp
@@ -64,6 +64,41 @@ std::string HumanReadableBytes(uint64_t bytes) {
|
||||
return std::to_string(bytes) + x[order];
|
||||
}
|
||||
|
||||
int GetTimezoneOffset() {
|
||||
std::time_t secs;
|
||||
std::time(&secs);
|
||||
std::tm *tptr = std::localtime(&secs);
|
||||
std::time_t local_secs = std::mktime(tptr);
|
||||
tptr = std::gmtime(&secs);
|
||||
std::time_t gmt_secs = std::mktime(tptr);
|
||||
return local_secs - gmt_secs;
|
||||
}
|
||||
|
||||
std::string FormatISO8601(const std::string &in, int extra_offset, const std::string &fmt) {
|
||||
int yr, mon, day, hr, min, sec, tzhr, tzmin;
|
||||
float milli;
|
||||
std::sscanf(in.c_str(), "%d-%d-%dT%d:%d:%d%f+%d:%d",
|
||||
&yr, &mon, &day, &hr, &min, &sec, &milli, &tzhr, &tzmin);
|
||||
std::tm tm;
|
||||
tm.tm_year = yr - 1900;
|
||||
tm.tm_mon = mon - 1;
|
||||
tm.tm_mday = day;
|
||||
tm.tm_hour = hr;
|
||||
tm.tm_min = min;
|
||||
tm.tm_sec = sec;
|
||||
tm.tm_wday = 0;
|
||||
tm.tm_yday = 0;
|
||||
tm.tm_isdst = -1;
|
||||
int offset = GetTimezoneOffset();
|
||||
tm.tm_sec += offset + extra_offset;
|
||||
mktime(&tm);
|
||||
std::stringstream ss;
|
||||
const static std::locale locale("");
|
||||
ss.imbue(locale);
|
||||
ss << std::put_time(&tm, fmt.c_str());
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void ScrollListBoxToSelected(Gtk::ListBox &list) {
|
||||
auto cb = [&list]() -> bool {
|
||||
const auto selected = list.get_selected_row();
|
||||
|
||||
1
util.hpp
1
util.hpp
@@ -45,6 +45,7 @@ std::string GetExtension(std::string url);
|
||||
bool IsURLViewableImage(const std::string &url);
|
||||
std::vector<uint8_t> ReadWholeFile(std::string path);
|
||||
std::string HumanReadableBytes(uint64_t bytes);
|
||||
std::string FormatISO8601(const std::string &in, int extra_offset = 0, const std::string &fmt = "%x %X");
|
||||
|
||||
template<typename T>
|
||||
struct Bitwise {
|
||||
|
||||
127
windows/guildsettings/invitespane.cpp
Normal file
127
windows/guildsettings/invitespane.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "invitespane.hpp"
|
||||
#include "../../abaddon.hpp"
|
||||
|
||||
GuildSettingsInvitesPane::GuildSettingsInvitesPane(Snowflake id)
|
||||
: GuildID(id)
|
||||
, m_model(Gtk::ListStore::create(m_columns))
|
||||
, m_menu_delete("Delete") {
|
||||
set_name("guild-invites-pane");
|
||||
set_hexpand(true);
|
||||
set_vexpand(true);
|
||||
|
||||
m_view.signal_button_press_event().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnTreeButtonPress), false);
|
||||
|
||||
m_menu_delete.signal_activate().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnMenuDelete));
|
||||
m_menu.append(m_menu_delete);
|
||||
m_menu.show_all();
|
||||
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto self_id = discord.GetUserData().ID;
|
||||
|
||||
if (discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD))
|
||||
discord.FetchGuildInvites(GuildID, sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInvitesFetch));
|
||||
|
||||
discord.signal_invite_create().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInviteCreate));
|
||||
discord.signal_invite_delete().connect(sigc::mem_fun(*this, &GuildSettingsInvitesPane::OnInviteDelete));
|
||||
|
||||
m_view.show();
|
||||
add(m_view);
|
||||
|
||||
m_view.set_model(m_model);
|
||||
m_view.append_column("Code", m_columns.m_col_code);
|
||||
m_view.append_column("Expires", m_columns.m_col_expires);
|
||||
m_view.append_column("Created by", m_columns.m_col_inviter);
|
||||
m_view.append_column("Uses", m_columns.m_col_uses);
|
||||
m_view.append_column("Max uses", m_columns.m_col_max_uses);
|
||||
m_view.append_column("Grants temporary membership", m_columns.m_col_temporary);
|
||||
|
||||
for (const auto column : m_view.get_columns())
|
||||
column->set_resizable(true);
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::AppendInvite(const InviteData &invite) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
auto &row = *m_model->append();
|
||||
row[m_columns.m_col_code] = invite.Code;
|
||||
if (invite.Inviter.has_value())
|
||||
row[m_columns.m_col_inviter] = invite.Inviter->Username + "#" + invite.Inviter->Discriminator;
|
||||
|
||||
if (invite.MaxAge.has_value())
|
||||
if (*invite.MaxAge == 0)
|
||||
row[m_columns.m_col_expires] = "Never";
|
||||
else
|
||||
row[m_columns.m_col_expires] = FormatISO8601(*invite.CreatedAt, *invite.MaxAge);
|
||||
|
||||
row[m_columns.m_col_uses] = *invite.Uses;
|
||||
if (*invite.MaxUses == 0)
|
||||
row[m_columns.m_col_max_uses] = "Unlimited";
|
||||
else
|
||||
row[m_columns.m_col_max_uses] = std::to_string(*invite.MaxUses);
|
||||
|
||||
row[m_columns.m_col_temporary] = *invite.IsTemporary ? "Yes" : "No";
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::OnInviteFetch(const std::optional<InviteData> &invite) {
|
||||
if (!invite.has_value()) return;
|
||||
AppendInvite(*invite);
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::OnInvitesFetch(const std::vector<InviteData> &invites) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
for (const auto &invite : invites)
|
||||
AppendInvite(invite);
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::OnInviteCreate(const InviteData &invite) {
|
||||
if (invite.Guild->ID == GuildID)
|
||||
OnInviteFetch(std::make_optional(invite));
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::OnInviteDelete(const InviteDeleteObject &data) {
|
||||
if (*data.GuildID == GuildID)
|
||||
for (auto &row : m_model->children())
|
||||
if (row[m_columns.m_col_code] == data.Code)
|
||||
m_model->erase(row);
|
||||
}
|
||||
|
||||
void GuildSettingsInvitesPane::OnMenuDelete() {
|
||||
auto selected_row = *m_view.get_selection()->get_selected();
|
||||
if (selected_row) {
|
||||
auto code = static_cast<Glib::ustring>(selected_row[m_columns.m_col_code]);
|
||||
auto cb = [this](const bool success) {
|
||||
if (!success) {
|
||||
Gtk::MessageDialog dlg("Failed to delete invite", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.run();
|
||||
}
|
||||
};
|
||||
Abaddon::Get().GetDiscordClient().DeleteInvite(code, sigc::track_obj(cb, *this));
|
||||
}
|
||||
}
|
||||
|
||||
bool GuildSettingsInvitesPane::OnTreeButtonPress(GdkEventButton *event) {
|
||||
if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_SECONDARY) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto self_id = discord.GetUserData().ID;
|
||||
const auto can_manage = discord.HasGuildPermission(self_id, GuildID, Permission::MANAGE_GUILD);
|
||||
m_menu_delete.set_sensitive(can_manage);
|
||||
auto selection = m_view.get_selection();
|
||||
Gtk::TreeModel::Path path;
|
||||
if (m_view.get_path_at_pos(event->x, event->y, path)) {
|
||||
m_view.get_selection()->select(path);
|
||||
m_menu.popup_at_pointer(reinterpret_cast<GdkEvent *>(event));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GuildSettingsInvitesPane::ModelColumns::ModelColumns() {
|
||||
add(m_col_code);
|
||||
add(m_col_expires);
|
||||
add(m_col_inviter);
|
||||
add(m_col_temporary);
|
||||
add(m_col_uses);
|
||||
add(m_col_max_uses);
|
||||
}
|
||||
39
windows/guildsettings/invitespane.hpp
Normal file
39
windows/guildsettings/invitespane.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <gtkmm.h>
|
||||
#include "../../discord/objects.hpp"
|
||||
|
||||
class GuildSettingsInvitesPane : public Gtk::ScrolledWindow {
|
||||
public:
|
||||
GuildSettingsInvitesPane(Snowflake id);
|
||||
|
||||
private:
|
||||
void AppendInvite(const InviteData &invite);
|
||||
void OnInviteFetch(const std::optional<InviteData> &invite);
|
||||
void OnInvitesFetch(const std::vector<InviteData> &invites);
|
||||
void OnInviteCreate(const InviteData &invite);
|
||||
void OnInviteDelete(const InviteDeleteObject &data);
|
||||
void OnMenuDelete();
|
||||
bool OnTreeButtonPress(GdkEventButton *event);
|
||||
|
||||
Gtk::TreeView m_view;
|
||||
|
||||
Snowflake GuildID;
|
||||
|
||||
class ModelColumns : public Gtk::TreeModel::ColumnRecord {
|
||||
public:
|
||||
ModelColumns();
|
||||
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_code;
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_expires;
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_inviter;
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_temporary;
|
||||
Gtk::TreeModelColumn<int> m_col_uses;
|
||||
Gtk::TreeModelColumn<Glib::ustring> m_col_max_uses;
|
||||
};
|
||||
|
||||
ModelColumns m_columns;
|
||||
Glib::RefPtr<Gtk::ListStore> m_model;
|
||||
|
||||
Gtk::Menu m_menu;
|
||||
Gtk::MenuItem m_menu_delete;
|
||||
};
|
||||
@@ -5,7 +5,8 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
: m_main(Gtk::ORIENTATION_VERTICAL)
|
||||
, GuildID(id)
|
||||
, m_pane_info(id)
|
||||
, m_pane_bans(id) {
|
||||
, m_pane_bans(id)
|
||||
, m_pane_invites(id) {
|
||||
auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto guild = *discord.GetGuild(id);
|
||||
|
||||
@@ -36,7 +37,10 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
|
||||
m_pane_info.show();
|
||||
m_pane_bans.show();
|
||||
m_pane_invites.show();
|
||||
|
||||
m_stack.set_transition_duration(100);
|
||||
m_stack.set_transition_type(Gtk::STACK_TRANSITION_TYPE_CROSSFADE);
|
||||
m_stack.set_margin_top(10);
|
||||
m_stack.set_margin_bottom(10);
|
||||
m_stack.set_margin_left(10);
|
||||
@@ -44,6 +48,7 @@ GuildSettingsWindow::GuildSettingsWindow(Snowflake id)
|
||||
|
||||
m_stack.add(m_pane_info, "info", "Info");
|
||||
m_stack.add(m_pane_bans, "bans", "Bans");
|
||||
m_stack.add(m_pane_invites, "invites", "Invites");
|
||||
m_stack.show();
|
||||
|
||||
m_main.add(m_switcher);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../discord/snowflake.hpp"
|
||||
#include "guildsettings/infopane.hpp"
|
||||
#include "guildsettings/banspane.hpp"
|
||||
#include "guildsettings/invitespane.hpp"
|
||||
|
||||
class GuildSettingsWindow : public Gtk::Window {
|
||||
public:
|
||||
@@ -15,6 +16,7 @@ private:
|
||||
|
||||
GuildSettingsInfoPane m_pane_info;
|
||||
GuildSettingsBansPane m_pane_bans;
|
||||
GuildSettingsInvitesPane m_pane_invites;
|
||||
|
||||
Snowflake GuildID;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user