From 8657fbac26e5f3ffd91f3fd48c796b9894f0b14f Mon Sep 17 00:00:00 2001 From: Misha133 Date: Sun, 29 Jan 2023 00:15:11 +0300 Subject: [PATCH] somewhat working auto mod action executed event --- src/Discord.Net.Core/GatewayIntents.cs | 14 +- .../API/Gateway/AutoModActionExecutedEvent.cs | 38 +++++ .../BaseSocketClient.Events.cs | 17 ++- .../DiscordSocketClient.cs | 140 +++++++++++++----- .../Guilds/AutoModActionExecutedData.cs | 46 ++++++ 5 files changed, 219 insertions(+), 36 deletions(-) create mode 100644 src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs create mode 100644 src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs diff --git a/src/Discord.Net.Core/GatewayIntents.cs b/src/Discord.Net.Core/GatewayIntents.cs index e9dd8f814..b9d715659 100644 --- a/src/Discord.Net.Core/GatewayIntents.cs +++ b/src/Discord.Net.Core/GatewayIntents.cs @@ -48,13 +48,25 @@ namespace Discord /// This intent includes GUILD_SCHEDULED_EVENT_CREATE, GUILD_SCHEDULED_EVENT_UPDATE, GUILD_SCHEDULED_EVENT_DELETE, GUILD_SCHEDULED_EVENT_USER_ADD, GUILD_SCHEDULED_EVENT_USER_REMOVE /// GuildScheduledEvents = 1 << 16, + + /// + /// This intent includes AUTO_MODERATION_RULE_CREATE, AUTO_MODERATION_RULE_UPDATE, AUTO_MODERATION_RULE_DELETE + /// + AutoModerationConfiguration = 1 << 20, + + /// + /// This intent includes AUTO_MODERATION_ACTION_EXECUTION + /// + AutoModerationActionExecution = 1 << 21, + /// /// This intent includes all but and /// which are privileged and must be enabled in the Developer Portal. /// AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites | GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages | - DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents, + DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents | AutoModerationConfiguration | + AutoModerationActionExecution, /// /// This intent includes all of them, including privileged ones. /// diff --git a/src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs new file mode 100644 index 000000000..44d2834d3 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; +namespace Discord.API.Gateway; + +internal class AutoModActionExecutedEvent +{ + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + + [JsonProperty("action")] + public Discord.API.AutoModAction Action { get; set; } + + [JsonProperty("rule_id")] + public ulong RuleId { get; set; } + + [JsonProperty("rule_trigger_type")] + public AutoModTriggerType TriggerType { get; set; } + + [JsonProperty("user_id")] + public ulong UserId { get; set; } + + [JsonProperty("channel_id")] + public Optional ChannelId { get; set; } + + [JsonProperty("message_id")] + public Optional MessageId { get; set; } + + [JsonProperty("alert_system_message_id")] + public Optional AlertSystemMessageId { get; set; } + + [JsonProperty("content")] + public string Content { get; set; } + + [JsonProperty("matched_keyword")] + public Optional MatchedKeyword { get; set; } + + [JsonProperty("matched_content")] + public Optional MatchedContent { get; set; } +} diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index ffa075396..815703867 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -902,14 +902,27 @@ namespace Discord.WebSocket } internal readonly AsyncEvent> _autoModRuleCreated = new AsyncEvent>(); - public event Func AutoModRuleDelted + public event Func, SocketAutoModRule, Task> AutoModRuleUpdated + { + add => _autoModRuleUpdated.Add(value); + remove => _autoModRuleUpdated.Remove(value); + } + internal readonly AsyncEvent, SocketAutoModRule, Task>> _autoModRuleUpdated = new (); + + public event Func AutoModRuleDeleted { add => _autoModRuleDeleted.Add(value); remove => _autoModRuleDeleted.Remove(value); } internal readonly AsyncEvent> _autoModRuleDeleted = new AsyncEvent>(); - //public event Func + public event Func AutoModActionExecuted + { + add => _autoModActionExecuted.Add(value); + remove => _autoModActionExecuted.Remove(value); + } + internal readonly AsyncEvent> _autoModActionExecuted = new (); + #endregion } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 096eaf157..63f4ef221 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -6,8 +6,10 @@ using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; using Discord.Utils; + using Newtonsoft.Json; using Newtonsoft.Json.Linq; + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -16,6 +18,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; + using GameModel = Discord.API.Game; namespace Discord.WebSocket @@ -333,7 +336,8 @@ namespace Discord.WebSocket await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; - while (_heartbeatTimes.TryDequeue(out _)) { } + while (_heartbeatTimes.TryDequeue(out _)) + { } _lastMessageTime = 0; await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); @@ -344,7 +348,8 @@ namespace Discord.WebSocket //Clear large guild queue await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false); - while (_largeGuilds.TryDequeue(out _)) { } + while (_largeGuilds.TryDequeue(out _)) + { } //Raise virtual GUILD_UNAVAILABLEs await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false); @@ -461,7 +466,7 @@ namespace Discord.WebSocket { var commands = (await ApiClient.GetGlobalApplicationCommandsAsync(withLocalizations, locale, options)).Select(x => SocketApplicationCommand.Create(this, x)); - foreach(var command in commands) + foreach (var command in commands) { State.AddCommand(command); } @@ -490,7 +495,7 @@ namespace Discord.WebSocket //Purge our previous commands State.PurgeCommands(x => x.IsGlobalCommand); - foreach(var entity in entities) + foreach (var entity in entities) { State.AddCommand(entity); } @@ -531,7 +536,7 @@ namespace Discord.WebSocket if (sticker != null) return sticker; - foreach(var guild in Guilds) + foreach (var guild in Guilds) { sticker = await guild.GetStickerAsync(id, CacheMode.CacheOnly).ConfigureAwait(false); @@ -544,7 +549,7 @@ namespace Discord.WebSocket var model = await ApiClient.GetStickerAsync(id, options).ConfigureAwait(false); - if(model == null) + if (model == null) return null; @@ -750,7 +755,7 @@ namespace Discord.WebSocket await _gatewayLogger.WarningAsync("You're using the GuildPresences intent without listening to the PresenceUpdate event, consider removing the intent from your config.").ConfigureAwait(false); } - if(!_gatewayIntents.HasFlag(GatewayIntents.GuildPresences) && + if (!_gatewayIntents.HasFlag(GatewayIntents.GuildPresences) && ((_shardedClient is null && _presenceUpdated.HasSubscribers) || (_shardedClient is not null && _shardedClient._presenceUpdated.HasSubscribers))) { @@ -767,7 +772,7 @@ namespace Discord.WebSocket _guildScheduledEventUserAdd.HasSubscribers; bool shardedClientHasGuildScheduledEventsSubscribers = - _shardedClient is not null && + _shardedClient is not null && (_shardedClient._guildScheduledEventCancelled.HasSubscribers || _shardedClient._guildScheduledEventUserRemove.HasSubscribers || _shardedClient._guildScheduledEventCompleted.HasSubscribers || @@ -783,7 +788,7 @@ namespace Discord.WebSocket await _gatewayLogger.WarningAsync("You're using the GuildScheduledEvents gateway intent without listening to any events related to that intent, consider removing the intent from your config.").ConfigureAwait(false); } - if(!_gatewayIntents.HasFlag(GatewayIntents.GuildScheduledEvents) && + if (!_gatewayIntents.HasFlag(GatewayIntents.GuildScheduledEvents) && ((_shardedClient is null && hasGuildScheduledEventsSubscribers) || (_shardedClient is not null && shardedClientHasGuildScheduledEventsSubscribers))) { @@ -2238,12 +2243,12 @@ namespace Discord.WebSocket await TimedInvokeAsync(_requestToSpeak, nameof(RequestToSpeak), stage, guildUser); return; } - if(before.IsSuppressed && !after.IsSuppressed) + if (before.IsSuppressed && !after.IsSuppressed) { await TimedInvokeAsync(_speakerAdded, nameof(SpeakerAdded), stage, guildUser); return; } - if(!before.IsSuppressed && after.IsSuppressed) + if (!before.IsSuppressed && after.IsSuppressed) { await TimedInvokeAsync(_speakerRemoved, nameof(SpeakerRemoved), stage, guildUser); } @@ -2349,7 +2354,7 @@ namespace Discord.WebSocket case "INTERACTION_CREATE": { await _gatewayLogger.DebugAsync("Received Dispatch (INTERACTION_CREATE)").ConfigureAwait(false); - + var data = (payload as JToken).ToObject(_serializer); var guild = data.GuildId.IsSpecified ? GetGuild(data.GuildId.Value) : null; @@ -2366,7 +2371,7 @@ namespace Discord.WebSocket : State.GetOrAddUser(data.Member.Value.User.Id, (_) => SocketGlobalUser.Create(this, State, data.Member.Value.User)); SocketChannel channel = null; - if(data.ChannelId.IsSpecified) + if (data.ChannelId.IsSpecified) { channel = State.GetChannel(data.ChannelId.Value); @@ -2507,7 +2512,7 @@ namespace Discord.WebSocket { threadChannel.Update(State, data); - if(data.ThreadMember.IsSpecified) + if (data.ThreadMember.IsSpecified) threadChannel.AddOrUpdateThreadMember(data.ThreadMember.Value, guild.CurrentUser); } else @@ -2570,7 +2575,7 @@ namespace Discord.WebSocket var guild = State.GetGuild(data.GuildId.Value); - if(guild == null) + if (guild == null) { await UnknownGuildAsync(type, data.GuildId.Value).ConfigureAwait(false); return; @@ -2591,17 +2596,17 @@ namespace Discord.WebSocket var guild = State.GetGuild(data.GuildId); - if(guild == null) + if (guild == null) { await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); return; } - foreach(var thread in data.Threads) + foreach (var thread in data.Threads) { var entity = guild.ThreadChannels.FirstOrDefault(x => x.Id == thread.Id); - if(entity == null) + if (entity == null) { entity = (SocketThreadChannel)guild.AddChannel(State, thread); } @@ -2610,7 +2615,7 @@ namespace Discord.WebSocket entity.Update(State, thread); } - foreach(var member in data.Members.Where(x => x.Id.Value == entity.Id)) + foreach (var member in data.Members.Where(x => x.Id.Value == entity.Id)) { var guildMember = guild.GetUser(member.Id.Value); @@ -2653,7 +2658,7 @@ namespace Discord.WebSocket var thread = (SocketThreadChannel)guild.GetChannel(data.Id); - if(thread == null) + if (thread == null) { await UnknownChannelAsync(type, data.Id); return; @@ -2671,13 +2676,13 @@ namespace Discord.WebSocket if (data.AddedMembers.IsSpecified) { List newThreadMembers = new List(); - foreach(var threadMember in data.AddedMembers.Value) + foreach (var threadMember in data.AddedMembers.Value) { SocketGuildUser guildMember; guildMember = guild.GetUser(threadMember.UserId.Value); - if(guildMember == null) + if (guildMember == null) { await UnknownGuildUserAsync("THREAD_MEMBERS_UPDATE", threadMember.UserId.Value, guild.Id); } @@ -2691,15 +2696,15 @@ namespace Discord.WebSocket if (leftUsers != null) { - foreach(var threadUser in leftUsers) + foreach (var threadUser in leftUsers) { await TimedInvokeAsync(_threadMemberLeft, nameof(ThreadMemberLeft), threadUser).ConfigureAwait(false); } } - if(joinUsers != null) + if (joinUsers != null) { - foreach(var threadUser in joinUsers) + foreach (var threadUser in joinUsers) { await TimedInvokeAsync(_threadMemberJoined, nameof(ThreadMemberJoined), threadUser).ConfigureAwait(false); } @@ -2718,7 +2723,7 @@ namespace Discord.WebSocket var guild = State.GetGuild(data.GuildId); - if(guild == null) + if (guild == null) { await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); return; @@ -2726,7 +2731,7 @@ namespace Discord.WebSocket var stageChannel = guild.GetStageChannel(data.ChannelId); - if(stageChannel == null) + if (stageChannel == null) { await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false); return; @@ -2792,15 +2797,16 @@ namespace Discord.WebSocket var after = guild.AddOrUpdateEvent(data); - if((before != null ? before.Status != GuildScheduledEventStatus.Completed : true) && data.Status == GuildScheduledEventStatus.Completed) + if ((before != null ? before.Status != GuildScheduledEventStatus.Completed : true) && data.Status == GuildScheduledEventStatus.Completed) { await TimedInvokeAsync(_guildScheduledEventCompleted, nameof(GuildScheduledEventCompleted), after).ConfigureAwait(false); } - else if((before != null ? before.Status != GuildScheduledEventStatus.Active : false) && data.Status == GuildScheduledEventStatus.Active) + else if ((before != null ? before.Status != GuildScheduledEventStatus.Active : false) && data.Status == GuildScheduledEventStatus.Active) { await TimedInvokeAsync(_guildScheduledEventStarted, nameof(GuildScheduledEventStarted), after).ConfigureAwait(false); } - else await TimedInvokeAsync(_guildScheduledEventUpdated, nameof(GuildScheduledEventUpdated), beforeCacheable, after).ConfigureAwait(false); + else + await TimedInvokeAsync(_guildScheduledEventUpdated, nameof(GuildScheduledEventUpdated), beforeCacheable, after).ConfigureAwait(false); } break; case "GUILD_SCHEDULED_EVENT_DELETE": @@ -2830,7 +2836,7 @@ namespace Discord.WebSocket var guild = State.GetGuild(data.GuildId); - if(guild == null) + if (guild == null) { await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); return; @@ -2899,9 +2905,76 @@ namespace Discord.WebSocket } break; - case "AUTO_MODERATION_RULE_EXECUTE": + case "AUTO_MODERATION_ACTION_EXECUTION": { + var data = (payload as JToken).ToObject(_serializer); + + var guild = State.GetGuild(data.GuildId); + var action = new AutoModRuleAction(data.Action.Type, + data.Action.Metadata.IsSpecified + ? data.Action.Metadata.Value.ChannelId.IsSpecified + ? data.Action.Metadata.Value.ChannelId.Value + : null + : null, + data.Action.Metadata.IsSpecified + ? data.Action.Metadata.Value.DurationSeconds.IsSpecified + ? data.Action.Metadata.Value.DurationSeconds.Value + : null + : null); + + var member = guild.GetUser(data.UserId); + + var cacheableUser = new Cacheable(member, + data.UserId, + member is not null, + () => Task.FromResult((SocketGuildUser)null) + ); + + ISocketMessageChannel channel = null; + if (data.ChannelId.IsSpecified) + channel = GetChannel(data.ChannelId.Value) as ISocketMessageChannel; + + var cacheableChannel = new Cacheable(channel, + data.ChannelId.GetValueOrDefault(0), + channel != null, + async () => + { + if(data.ChannelId.IsSpecified) + return await GetChannelAsync(data.ChannelId.Value).ConfigureAwait(false) as ISocketMessageChannel; + return null; + }); + + + var cachedMsg = channel?.GetCachedMessage(data.MessageId.GetValueOrDefault(0)) as IUserMessage; + + var cacheableMessage = new Cacheable(cachedMsg, + data.MessageId.GetValueOrDefault(0), + cachedMsg is not null, + async () => + { + if(data.MessageId.IsSpecified) + return (await channel.GetMessageAsync(data.MessageId.Value).ConfigureAwait(false)) as IUserMessage; + return null; + }); + var rule = new Cacheable(null, data.RuleId, false, () => null); + + var eventData = new AutoModActionExecutedData( + rule, + data.TriggerType, + cacheableUser, + cacheableChannel, + cacheableMessage, + data.AlertSystemMessageId.GetValueOrDefault(0), + data.Content, + data.MatchedContent.IsSpecified + ? data.MatchedContent.Value + : null, + data.MatchedKeyword.IsSpecified + ? data.MatchedKeyword.Value + : null); + + await TimedInvokeAsync(_autoModActionExecuted, nameof(AutoModActionExecuted), guild, action, eventData); } break; @@ -2930,7 +3003,8 @@ namespace Discord.WebSocket #region Others default: - if(!SuppressUnknownDispatchWarnings) await _gatewayLogger.WarningAsync($"Unknown Dispatch ({type})").ConfigureAwait(false); + if (!SuppressUnknownDispatchWarnings) + await _gatewayLogger.WarningAsync($"Unknown Dispatch ({type})").ConfigureAwait(false); break; #endregion } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs b/src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs new file mode 100644 index 000000000..65f131fd0 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs @@ -0,0 +1,46 @@ +using Discord.Rest; + +namespace Discord.WebSocket; + +public class AutoModActionExecutedData +{ + public Cacheable Rule { get; } + + public AutoModTriggerType TriggerType { get; } + + public Cacheable User { get; } + + public Cacheable Channel { get; } + + public Cacheable Message { get; } + + public ulong AlertMessageId { get; } + + public string Content { get; } + + public string MatchedContent { get; } + + public string MatchedKeyword { get; } + + internal AutoModActionExecutedData(Cacheable rule, + AutoModTriggerType triggerType, + Cacheable user, + Cacheable channel, + Cacheable message, + ulong alertMessageId, + string content, + string matchedContent, + string matchedKeyword + ) + { + Rule = rule; + TriggerType = triggerType; + User = user; + Channel = channel; + Message = message; + AlertMessageId = alertMessageId; + Content = content; + MatchedContent = matchedContent; + MatchedKeyword = matchedKeyword; + } +}