diff options
author | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-03-06 17:31:09 -0500 |
---|---|---|
committer | ouwou <26526779+ouwou@users.noreply.github.com> | 2023-03-06 17:31:09 -0500 |
commit | 64085fafec86b347c26bb92b758042a0bd4edc75 (patch) | |
tree | 33b7128717e2c63575ef0691c3575d430c3d9a05 /src | |
parent | 7611ad298af9f46c6ca69a425afed8e4148a47a9 (diff) | |
download | abaddon-portaudio-64085fafec86b347c26bb92b758042a0bd4edc75.tar.gz abaddon-portaudio-64085fafec86b347c26bb92b758042a0bd4edc75.zip |
add basic guild notifications
Diffstat (limited to 'src')
-rw-r--r-- | src/discord/discord.cpp | 17 | ||||
-rw-r--r-- | src/discord/discord.hpp | 3 | ||||
-rw-r--r-- | src/discord/guild.hpp | 10 | ||||
-rw-r--r-- | src/discord/message.cpp | 2 | ||||
-rw-r--r-- | src/discord/message.hpp | 2 | ||||
-rw-r--r-- | src/discord/objects.cpp | 8 | ||||
-rw-r--r-- | src/discord/objects.hpp | 12 | ||||
-rw-r--r-- | src/discord/store.cpp | 1 | ||||
-rw-r--r-- | src/notifications/notifications.cpp | 93 | ||||
-rw-r--r-- | src/notifications/notifications.hpp | 3 |
10 files changed, 139 insertions, 12 deletions
diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index cd5e2b3..f21e8a1 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -81,6 +81,18 @@ const UserData &DiscordClient::GetUserData() const { return m_user_data; } +const UserGuildSettingsData &DiscordClient::GetUserGuildSettings() const { + return m_user_guild_settings; +} + +std::optional<UserGuildSettingsEntry> DiscordClient::GetSettingsForGuild(Snowflake id) const { + for (const auto &entry : m_user_guild_settings.Entries) { + if (entry.GuildID == id) return entry; + } + + return std::nullopt; +} + std::vector<Snowflake> DiscordClient::GetUserSortedGuilds() const { // sort order is unfolder'd guilds sorted by id descending, then guilds in folders in array order // todo: make sure folder'd guilds are sorted properly @@ -1617,6 +1629,8 @@ void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) { m_session_id = data.SessionID; m_user_data = data.SelfUser; m_user_settings = data.Settings; + m_user_guild_settings = data.GuildSettings; + // TODO handle update HandleReadyReadState(data); HandleReadyGuildSettings(data); @@ -1632,7 +1646,7 @@ void DiscordClient::HandleGatewayMessageCreate(const GatewayMessage &msg) { m_last_message_id[data.ChannelID] = data.ID; if (data.Author.ID != GetUserData().ID) m_unread[data.ChannelID]; - if (data.DoesMention(GetUserData().ID)) { + if (data.DoesMentionEveryoneOrUser(GetUserData().ID)) { m_unread[data.ChannelID]++; } m_signal_message_create.emit(data); @@ -2034,6 +2048,7 @@ void DiscordClient::HandleGatewayMessageAck(const GatewayMessage &msg) { void DiscordClient::HandleGatewayUserGuildSettingsUpdate(const GatewayMessage &msg) { UserGuildSettingsUpdateData data = msg.Data; + const bool for_dms = !data.Settings.GuildID.IsValid(); const auto channels = for_dms ? GetPrivateChannels() : GetChannelsInGuild(data.Settings.GuildID); diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index 6f90659..9d13f65 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -56,6 +56,8 @@ public: std::unordered_set<Snowflake> GetGuilds() const; const UserData &GetUserData() const; + const UserGuildSettingsData &GetUserGuildSettings() const; + std::optional<UserGuildSettingsEntry> GetSettingsForGuild(Snowflake id) const; std::vector<Snowflake> GetUserSortedGuilds() const; std::vector<Message> GetMessagesForChannel(Snowflake id, size_t limit = 50) const; std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const; @@ -328,6 +330,7 @@ private: UserData m_user_data; UserSettings m_user_settings; + UserGuildSettingsData m_user_guild_settings; Store m_store; HTTPClient m_http; diff --git a/src/discord/guild.hpp b/src/discord/guild.hpp index 0428928..4895d30 100644 --- a/src/discord/guild.hpp +++ b/src/discord/guild.hpp @@ -8,6 +8,11 @@ #include <string> #include <unordered_set> +enum class DefaultNotificationLevel { + ALL_MESSAGES = 0, + ONLY_MENTIONS = 1, +}; + enum class GuildApplicationStatus { STARTED, PENDING, @@ -36,9 +41,6 @@ struct GuildApplicationData { // 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; @@ -55,7 +57,7 @@ struct GuildData { std::optional<bool> IsEmbedEnabled; // deprecated std::optional<Snowflake> EmbedChannelID; // null, deprecated std::optional<int> VerificationLevel; - std::optional<int> DefaultMessageNotifications; + std::optional<DefaultNotificationLevel> DefaultMessageNotifications; std::optional<int> ExplicitContentFilter; std::optional<std::vector<RoleData>> Roles; std::optional<std::vector<EmojiData>> Emojis; // only access id diff --git a/src/discord/message.cpp b/src/discord/message.cpp index ac12916..5e15fa9 100644 --- a/src/discord/message.cpp +++ b/src/discord/message.cpp @@ -264,7 +264,7 @@ bool Message::IsEdited() const { return m_edited; } -bool Message::DoesMention(Snowflake id) const noexcept { +bool Message::DoesMentionEveryoneOrUser(Snowflake id) const noexcept { if (DoesMentionEveryone) return true; return std::any_of(Mentions.begin(), Mentions.end(), [id](const UserData &user) { return user.ID == id; diff --git a/src/discord/message.hpp b/src/discord/message.hpp index 4e332b6..22a1c5c 100644 --- a/src/discord/message.hpp +++ b/src/discord/message.hpp @@ -212,7 +212,7 @@ struct Message { [[nodiscard]] bool IsDeleted() const; [[nodiscard]] bool IsEdited() const; - [[nodiscard]] bool DoesMention(Snowflake id) const noexcept; + [[nodiscard]] bool DoesMentionEveryoneOrUser(Snowflake id) const noexcept; private: bool m_deleted = false; diff --git a/src/discord/objects.cpp b/src/discord/objects.cpp index e43e05a..7f0d51e 100644 --- a/src/discord/objects.cpp +++ b/src/discord/objects.cpp @@ -199,6 +199,14 @@ void to_json(nlohmann::json &j, const UserGuildSettingsEntry &m) { j["version"] = m.Version; } +std::optional<UserGuildSettingsChannelOverride> UserGuildSettingsEntry::GetOverride(Snowflake channel_id) const { + for (const auto &override : ChannelOverrides) { + if (override.ChannelID == channel_id) return override; + } + + return std::nullopt; +} + void from_json(const nlohmann::json &j, UserGuildSettingsData &m) { JS_D("version", m.Version); JS_D("partial", m.IsPartial); diff --git a/src/discord/objects.hpp b/src/discord/objects.hpp index 9db9369..97da807 100644 --- a/src/discord/objects.hpp +++ b/src/discord/objects.hpp @@ -272,10 +272,16 @@ struct ReadStateData { friend void from_json(const nlohmann::json &j, ReadStateData &m); }; +enum class NotificationLevel { + ALL_MESSAGES = 0, + ONLY_MENTIONS = 1, + NO_MESSAGES = 2, +}; + struct UserGuildSettingsChannelOverride { bool Muted; MuteConfigData MuteConfig; - int MessageNotifications; + NotificationLevel MessageNotifications; bool Collapsed; Snowflake ChannelID; @@ -290,13 +296,15 @@ struct UserGuildSettingsEntry { bool Muted; MuteConfigData MuteConfig; bool MobilePush; - int MessageNotifications; + NotificationLevel MessageNotifications; bool HideMutedChannels; Snowflake GuildID; std::vector<UserGuildSettingsChannelOverride> ChannelOverrides; friend void from_json(const nlohmann::json &j, UserGuildSettingsEntry &m); friend void to_json(nlohmann::json &j, const UserGuildSettingsEntry &m); + + std::optional<UserGuildSettingsChannelOverride> GetOverride(Snowflake channel_id) const; }; struct UserGuildSettingsData { diff --git a/src/discord/store.cpp b/src/discord/store.cpp index 7f674c4..8393322 100644 --- a/src/discord/store.cpp +++ b/src/discord/store.cpp @@ -779,6 +779,7 @@ std::optional<GuildData> Store::GetGuild(Snowflake id) const { s->Get(1, r.Name); s->Get(2, r.Icon); s->Get(5, r.OwnerID); + s->Get(11, r.DefaultMessageNotifications); s->Get(20, r.IsUnavailable); s->Get(27, r.PremiumTier); diff --git a/src/notifications/notifications.cpp b/src/notifications/notifications.cpp index b1c4c2f..bda9f46 100644 --- a/src/notifications/notifications.cpp +++ b/src/notifications/notifications.cpp @@ -4,6 +4,72 @@ Notifications::Notifications() { } +bool CheckGuildMessage(const Message &message) { + auto &discord = Abaddon::Get().GetDiscordClient(); + if (!message.GuildID.has_value()) return false; + + const auto guild = discord.GetGuild(*message.GuildID); + if (!guild.has_value()) return false; + + const auto guild_settings = discord.GetSettingsForGuild(*message.GuildID); + + // if theres no guild settings then just use default message notifications level + // (there will be no channel settings) + if (!guild_settings.has_value()) { + if (guild->DefaultMessageNotifications.has_value()) { + switch (*guild->DefaultMessageNotifications) { + case DefaultNotificationLevel::ALL_MESSAGES: + return true; + case DefaultNotificationLevel::ONLY_MENTIONS: + return message.DoesMentionEveryoneOrUser(discord.GetUserData().ID); + default: + return false; + } + } + return false; + } else if (guild_settings->Muted) { + // if there are guild settings and the guild is muted then dont notify + return false; + } + + // if the channel category is muted then dont notify + const auto channel = discord.GetChannel(message.ChannelID); + std::optional<UserGuildSettingsChannelOverride> category_settings; + if (channel.has_value() && channel->ParentID.has_value()) { + category_settings = guild_settings->GetOverride(*channel->ParentID); + if (category_settings.has_value() && category_settings->Muted) { + return false; + } + } + + const auto channel_settings = guild_settings->GetOverride(message.ChannelID); + + // if there are guild settings but no channel settings then fallback to category or guild + + NotificationLevel level; + if (channel_settings.has_value()) { + level = channel_settings->MessageNotifications; + } else { + if (category_settings.has_value()) { + level = category_settings->MessageNotifications; + } else { + level = guild_settings->MessageNotifications; + } + } + + // there are channel settings, so use them + switch (level) { + case NotificationLevel::ALL_MESSAGES: + return true; + case NotificationLevel::ONLY_MENTIONS: + return message.DoesMentionEveryoneOrUser(discord.GetUserData().ID); + case NotificationLevel::NO_MESSAGES: + return false; + default: + return false; + } +} + void Notifications::CheckMessage(const Message &message) { // ignore if our status is do not disturb if (IsDND()) return; @@ -17,11 +83,13 @@ void Notifications::CheckMessage(const Message &message) { // notify messages in DMs const auto channel = discord.GetChannel(message.ChannelID); if (channel->IsDM()) { - NotifyMessage(message); + NotifyMessageDM(message); + } else if (CheckGuildMessage(message)) { + NotifyMessageGuild(message); } } -void Notifications::NotifyMessage(const Message &message) { +void Notifications::NotifyMessageDM(const Message &message) { Glib::ustring default_action = "app.go-to-channel"; default_action += "::"; default_action += std::to_string(message.ChannelID); @@ -30,6 +98,27 @@ void Notifications::NotifyMessage(const Message &message) { m_notifier.Notify(title, body, default_action); } +void Notifications::NotifyMessageGuild(const Message &message) { + Glib::ustring default_action = "app.go-to-channel"; + default_action += "::"; + default_action += std::to_string(message.ChannelID); + Glib::ustring title = message.Author.Username; + if (const auto channel = Abaddon::Get().GetDiscordClient().GetChannel(message.ChannelID); channel.has_value() && channel->Name.has_value()) { + if (channel->ParentID.has_value()) { + const auto category = Abaddon::Get().GetDiscordClient().GetChannel(*channel->ParentID); + if (category.has_value() && category->Name.has_value()) { + title += " (#" + *channel->Name + ", " + *category->Name + ")"; + } else { + title += " (#" + *channel->Name + ")"; + } + } else { + title += " (#" + *channel->Name + ")"; + } + } + const auto body = message.Content; + m_notifier.Notify(title, body, default_action); +} + bool Notifications::IsDND() const { auto &discord = Abaddon::Get().GetDiscordClient(); const auto status = discord.GetUserStatus(discord.GetUserData().ID); diff --git a/src/notifications/notifications.hpp b/src/notifications/notifications.hpp index fb71349..5498938 100644 --- a/src/notifications/notifications.hpp +++ b/src/notifications/notifications.hpp @@ -10,7 +10,8 @@ public: void CheckMessage(const Message &message); private: - void NotifyMessage(const Message &message); + void NotifyMessageDM(const Message &message); + void NotifyMessageGuild(const Message &message); [[nodiscard]] bool IsDND() const; |