From b6b215ee6f08e4e7d6d987ffd676cebeb8638a62 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 20 Jan 2022 02:45:28 -0500 Subject: add mark as unread/toggle mute for threads --- src/discord/discord.hpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/discord/discord.hpp') diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 1a6aa14..4a7593d 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -146,6 +146,8 @@ public: void MarkAllAsRead(sigc::slot callback); void MuteGuild(Snowflake id, sigc::slot callback); void UnmuteGuild(Snowflake id, sigc::slot callback); + void MuteThread(Snowflake id, sigc::slot callback); + void UnmuteThread(Snowflake id, sigc::slot callback); bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const; bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const; -- cgit v1.2.3 From 7f1d3df4a5d640ae3a885886139236e8f9c5555c Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 3 Mar 2022 23:45:30 -0500 Subject: start sending request guild members for unknown members --- src/abaddon.cpp | 19 ++++++++++++++++++- src/abaddon.hpp | 2 ++ src/discord/discord.cpp | 16 ++++++++++++++++ src/discord/discord.hpp | 14 ++++++++++++++ src/discord/objects.cpp | 13 +++++++++++++ src/discord/objects.hpp | 22 ++++++++++++++++++++++ 6 files changed, 85 insertions(+), 1 deletion(-) (limited to 'src/discord/discord.hpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 51f8052..b287e17 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -325,6 +325,22 @@ void Abaddon::ShowGuildVerificationGateDialog(Snowflake guild_id) { } } +void Abaddon::CheckMessagesForMembers(const ChannelData &chan, const std::vector &msgs) { + if (!chan.GuildID.has_value()) return; + + // TODO sql query + std::set fetch; + std::set ids; + for (const auto& msg : msgs) + ids.insert(msg.Author.ID); + for (const auto id : ids) { + const auto member = m_discord.GetMember(id, *chan.GuildID); + if (!member.has_value()) + fetch.insert(id); + } + m_discord.RequestMembers(*chan.GuildID, fetch.begin(), fetch.end()); +} + void Abaddon::SetupUserMenu() { m_user_menu = Gtk::manage(new Gtk::Menu); m_user_menu_insert_mention = Gtk::manage(new Gtk::MenuItem("Insert Mention")); @@ -536,7 +552,8 @@ void Abaddon::ActionChannelOpened(Snowflake id) { if (m_channels_requested.find(id) == m_channels_requested.end()) { // dont fire requests we know will fail if (can_access) { - m_discord.FetchMessagesInChannel(id, [this, id](const std::vector &msgs) { + m_discord.FetchMessagesInChannel(id, [channel, this, id](const std::vector &msgs) { + CheckMessagesForMembers(*channel, msgs); m_main_window->UpdateChatWindowContents(); m_channels_requested.insert(id); }); diff --git a/src/abaddon.hpp b/src/abaddon.hpp index d9d0bb0..311dcc5 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -93,6 +93,8 @@ public: protected: void ShowGuildVerificationGateDialog(Snowflake guild_id); + void CheckMessagesForMembers(const ChannelData &chan, const std::vector &msgs); + void SetupUserMenu(); void SaveState(); void LoadState(); diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 6ea0ea6..c11210d 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1464,6 +1464,9 @@ void DiscordClient::HandleGatewayMessage(std::string str) { case GatewayEvent::USER_GUILD_SETTINGS_UPDATE: { HandleGatewayUserGuildSettingsUpdate(m); } break; + case GatewayEvent::GUILD_MEMBERS_CHUNK: { + HandleGatewayGuildMembersChunk(m); + } break; } } break; default: @@ -2049,6 +2052,14 @@ void DiscordClient::HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &m } } +void DiscordClient::HandleGatewayGuildMembersChunk(const GatewayMessage &msg) { + GuildMembersChunkData data = msg.Data; + m_store.BeginTransaction(); + for (const auto &member : data.Members) + m_store.SetGuildMember(data.GuildID, member.User->ID, member); + m_store.EndTransaction(); +} + void DiscordClient::HandleGatewayReadySupplemental(const GatewayMessage &msg) { ReadySupplementalData data = msg.Data; for (const auto &p : data.MergedPresences.Friends) { @@ -2511,6 +2522,7 @@ void DiscordClient::LoadEventMap() { m_event_map["THREAD_MEMBER_LIST_UPDATE"] = GatewayEvent::THREAD_MEMBER_LIST_UPDATE; m_event_map["MESSAGE_ACK"] = GatewayEvent::MESSAGE_ACK; m_event_map["USER_GUILD_SETTINGS_UPDATE"] = GatewayEvent::USER_GUILD_SETTINGS_UPDATE; + m_event_map["GUILD_MEMBERS_CHUNK"] = GatewayEvent::GUILD_MEMBERS_CHUNK; } DiscordClient::type_signal_gateway_ready DiscordClient::signal_gateway_ready() { @@ -2677,6 +2689,10 @@ DiscordClient::type_signal_message_ack DiscordClient::signal_message_ack() { return m_signal_message_ack; } +DiscordClient::type_signal_guild_members_chunk DiscordClient::signal_guild_members_chunk() { + return m_signal_guild_members_chunk; +} + DiscordClient::type_signal_added_to_thread DiscordClient::signal_added_to_thread() { return m_signal_added_to_thread; } diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 4a7593d..63f8bd5 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -152,6 +152,16 @@ public: bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const; bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const; + // send op 8 to get member data for unknown members + template + void RequestMembers(Snowflake guild_id, Iter begin, Iter end) { + RequestGuildMembersMessage obj; + obj.GuildID = guild_id; + obj.Presences = false; + obj.UserIDs = { begin, end }; + m_websocket.Send(obj); + } + // real client doesn't seem to use the single role endpoints so neither do we template auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot callback) { @@ -262,6 +272,7 @@ private: void HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg); void HandleGatewayMessageAck(const GatewayMessage &msg); void HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &msg); + void HandleGatewayGuildMembersChunk(const GatewayMessage &msg); void HandleGatewayReadySupplemental(const GatewayMessage &msg); void HandleGatewayReconnect(const GatewayMessage &msg); void HandleGatewayInvalidSession(const GatewayMessage &msg); @@ -372,6 +383,7 @@ public: typedef sigc::signal type_signal_thread_update; typedef sigc::signal type_signal_thread_member_list_update; typedef sigc::signal type_signal_message_ack; + typedef sigc::signal type_signal_guild_members_chunk; // not discord dispatch events typedef sigc::signal type_signal_added_to_thread; @@ -427,6 +439,7 @@ public: type_signal_thread_update signal_thread_update(); type_signal_thread_member_list_update signal_thread_member_list_update(); type_signal_message_ack signal_message_ack(); + type_signal_guild_members_chunk signal_guild_members_chunk(); type_signal_added_to_thread signal_added_to_thread(); type_signal_removed_from_thread signal_removed_from_thread(); @@ -479,6 +492,7 @@ protected: type_signal_thread_update m_signal_thread_update; type_signal_thread_member_list_update m_signal_thread_member_list_update; type_signal_message_ack m_signal_message_ack; + type_signal_guild_members_chunk m_signal_guild_members_chunk; type_signal_removed_from_thread m_signal_removed_from_thread; type_signal_added_to_thread m_signal_added_to_thread; diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp index 0ee96dc..3c9f770 100644 --- a/src/discord/objects.cpp +++ b/src/discord/objects.cpp @@ -119,6 +119,14 @@ void to_json(nlohmann::json &j, const UpdateStatusMessage &m) { } } +void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m) { + j["op"] = GatewayOp::RequestGuildMembers; + j["d"] = nlohmann::json::object(); + j["d"]["guild_id"] = m.GuildID; + j["d"]["presences"] = m.Presences; + j["d"]["user_ids"] = m.UserIDs; +} + void from_json(const nlohmann::json &j, ReadStateEntry &m) { JS_ON("mention_count", m.MentionCount); JS_ON("last_message_id", m.LastMessageID); @@ -626,3 +634,8 @@ void to_json(nlohmann::json &j, const AckBulkData &m) { void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m) { m.Settings = j; } + +void from_json(const nlohmann::json &j, GuildMembersChunkData &m) { + JS_D("members", m.Members); + JS_D("guild_id", m.GuildID); +} diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp index fae592a..27542cc 100644 --- a/src/discord/objects.hpp +++ b/src/discord/objects.hpp @@ -99,6 +99,7 @@ enum class GatewayEvent : int { THREAD_MEMBER_LIST_UPDATE, MESSAGE_ACK, USER_GUILD_SETTINGS_UPDATE, + GUILD_MEMBERS_CHUNK, }; enum class GatewayCloseCode : uint16_t { @@ -245,6 +246,14 @@ struct UpdateStatusMessage { friend void to_json(nlohmann::json &j, const UpdateStatusMessage &m); }; +struct RequestGuildMembersMessage { + Snowflake GuildID; + bool Presences; + std::vector UserIDs; + + friend void to_json(nlohmann::json &j, const RequestGuildMembersMessage &m); +}; + struct ReadStateEntry { int MentionCount; Snowflake LastMessageID; @@ -841,3 +850,16 @@ struct UserGuildSettingsUpdateData { friend void from_json(const nlohmann::json &j, UserGuildSettingsUpdateData &m); }; + +struct GuildMembersChunkData { + /* + not needed so not deserialized + int ChunkCount; + int ChunkIndex; + std::vector NotFound; + */ + Snowflake GuildID; + std::vector Members; + + friend void from_json(const nlohmann::json &j, GuildMembersChunkData &m); +}; -- cgit v1.2.3 From 3583a5d2516f9b71d389ccb95224b21a3dae20a2 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Thu, 3 Mar 2022 23:57:48 -0500 Subject: dont request guild members if there are no user ids --- src/discord/discord.hpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/discord/discord.hpp') diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 63f8bd5..dfe82cd 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -155,6 +155,8 @@ public: // send op 8 to get member data for unknown members template void RequestMembers(Snowflake guild_id, Iter begin, Iter end) { + if (std::distance(begin, end) == 0) return; + RequestGuildMembersMessage obj; obj.GuildID = guild_id; obj.Presences = false; -- cgit v1.2.3 From af60bceada7e55011f6d5ed2f13fef354ced45ef Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 4 Mar 2022 23:03:09 -0500 Subject: optimize sql for getting unknown member ids --- src/abaddon.cpp | 18 ++++++++---------- src/discord/discord.hpp | 11 +++++++++++ src/discord/store.cpp | 25 +++++++++++++++++++++++++ src/discord/store.hpp | 3 +++ 4 files changed, 47 insertions(+), 10 deletions(-) (limited to 'src/discord/discord.hpp') diff --git a/src/abaddon.cpp b/src/abaddon.cpp index c9ff24e..55c6ee9 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -328,16 +328,14 @@ void Abaddon::ShowGuildVerificationGateDialog(Snowflake guild_id) { void Abaddon::CheckMessagesForMembers(const ChannelData &chan, const std::vector &msgs) { if (!chan.GuildID.has_value()) return; - // TODO sql query - std::set fetch; - std::set ids; - for (const auto &msg : msgs) - ids.insert(msg.Author.ID); - for (const auto id : ids) { - const auto member = m_discord.GetMember(id, *chan.GuildID); - if (!member.has_value()) - fetch.insert(id); - } + std::vector unknown; + std::transform(msgs.begin(), msgs.end(), + std::back_inserter(unknown), + [](const Message &msg) -> Snowflake { + return msg.Author.ID; + }); + + const auto fetch = m_discord.FilterUnknownMembersFrom(*chan.GuildID, unknown.begin(), unknown.end()); m_discord.RequestMembers(*chan.GuildID, fetch.begin(), fetch.end()); } diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index dfe82cd..6add18f 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -84,6 +84,17 @@ public: void GetArchivedPrivateThreads(Snowflake channel_id, sigc::slot callback); std::vector GetChildChannelIDs(Snowflake parent_id) const; + // get ids of given list of members for who we do not have the member data + template + std::unordered_set FilterUnknownMembersFrom(Snowflake guild_id, Iter begin, Iter end) { + std::unordered_set ret; + const auto known = m_store.GetMembersInGuild(guild_id); + for (auto iter = begin; iter != end; iter++) + if (known.find(*iter) == known.end()) + ret.insert(*iter); + return ret; + } + bool IsThreadJoined(Snowflake thread_id) const; bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const; diff --git a/src/discord/store.cpp b/src/discord/store.cpp index d96bb1f..8eb3613 100644 --- a/src/discord/store.cpp +++ b/src/discord/store.cpp @@ -576,6 +576,23 @@ std::vector Store::GetChannelIDsWithParentID(Snowflake channel_id) co return ret; } +std::unordered_set Store::GetMembersInGuild(Snowflake guild_id) const { + auto &s = m_stmt_get_guild_member_ids; + + s->Bind(1, guild_id); + + std::unordered_set ret; + while (s->FetchOne()) { + Snowflake x; + s->Get(0, x); + ret.insert(x); + } + + s->Reset(); + + return ret; +} + void Store::AddReaction(const MessageReactionAddObject &data, bool byself) { auto &s = m_stmt_add_reaction; @@ -2134,6 +2151,14 @@ bool Store::CreateStatements() { return false; } + m_stmt_get_guild_member_ids = std::make_unique(m_db, R"( + SELECT user_id FROM members WHERE guild_id = ? + )"); + if (!m_stmt_get_guild_member_ids->OK()) { + fprintf(stderr, "failed to prepare get guild member ids statement: %s\n", m_db.ErrStr()); + return false; + } + return true; } diff --git a/src/discord/store.hpp b/src/discord/store.hpp index 4320807..a1e5f81 100644 --- a/src/discord/store.hpp +++ b/src/discord/store.hpp @@ -44,6 +44,8 @@ public: std::vector GetPinnedMessages(Snowflake channel_id) const; std::vector GetActiveThreads(Snowflake channel_id) const; // public std::vector GetChannelIDsWithParentID(Snowflake channel_id) const; + std::unordered_set GetMembersInGuild(Snowflake guild_id) const; + // ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data void AddReaction(const MessageReactionAddObject &data, bool byself); void RemoveReaction(const MessageReactionRemoveObject &data, bool byself); @@ -302,5 +304,6 @@ private: STMT(sub_reaction); STMT(get_reactions); STMT(get_chan_ids_parent); + STMT(get_guild_member_ids); #undef STMT }; -- cgit v1.2.3