Browse Source

somewhat working auto mod action executed event

pull/2578/head
Misha133 2 years ago
parent
commit
8657fbac26
5 changed files with 219 additions and 36 deletions
  1. +13
    -1
      src/Discord.Net.Core/GatewayIntents.cs
  2. +38
    -0
      src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs
  3. +15
    -2
      src/Discord.Net.WebSocket/BaseSocketClient.Events.cs
  4. +107
    -33
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  5. +46
    -0
      src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs

+ 13
- 1
src/Discord.Net.Core/GatewayIntents.cs View File

@@ -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
/// </summary>
GuildScheduledEvents = 1 << 16,

/// <summary>
/// This intent includes AUTO_MODERATION_RULE_CREATE, AUTO_MODERATION_RULE_UPDATE, AUTO_MODERATION_RULE_DELETE
/// </summary>
AutoModerationConfiguration = 1 << 20,

/// <summary>
/// This intent includes AUTO_MODERATION_ACTION_EXECUTION
/// </summary>
AutoModerationActionExecution = 1 << 21,

/// <summary>
/// This intent includes all but <see cref="GuildMembers"/> and <see cref="GuildPresences"/>
/// which are privileged and must be enabled in the Developer Portal.
/// </summary>
AllUnprivileged = Guilds | GuildBans | GuildEmojis | GuildIntegrations | GuildWebhooks | GuildInvites |
GuildVoiceStates | GuildMessages | GuildMessageReactions | GuildMessageTyping | DirectMessages |
DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents,
DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents | AutoModerationConfiguration |
AutoModerationActionExecution,
/// <summary>
/// This intent includes all of them, including privileged ones.
/// </summary>


+ 38
- 0
src/Discord.Net.WebSocket/API/Gateway/AutoModActionExecutedEvent.cs View File

@@ -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<ulong> ChannelId { get; set; }

[JsonProperty("message_id")]
public Optional<ulong> MessageId { get; set; }

[JsonProperty("alert_system_message_id")]
public Optional<ulong> AlertSystemMessageId { get; set; }

[JsonProperty("content")]
public string Content { get; set; }

[JsonProperty("matched_keyword")]
public Optional<string> MatchedKeyword { get; set; }

[JsonProperty("matched_content")]
public Optional<string> MatchedContent { get; set; }
}

+ 15
- 2
src/Discord.Net.WebSocket/BaseSocketClient.Events.cs View File

@@ -902,14 +902,27 @@ namespace Discord.WebSocket
}
internal readonly AsyncEvent<Func<SocketAutoModRule, Task>> _autoModRuleCreated = new AsyncEvent<Func<SocketAutoModRule, Task>>();

public event Func<SocketAutoModRule, Task> AutoModRuleDelted
public event Func<Cacheable<SocketAutoModRule, ulong>, SocketAutoModRule, Task> AutoModRuleUpdated
{
add => _autoModRuleUpdated.Add(value);
remove => _autoModRuleUpdated.Remove(value);
}
internal readonly AsyncEvent<Func<Cacheable<SocketAutoModRule, ulong>, SocketAutoModRule, Task>> _autoModRuleUpdated = new ();

public event Func<SocketAutoModRule, Task> AutoModRuleDeleted
{
add => _autoModRuleDeleted.Add(value);
remove => _autoModRuleDeleted.Remove(value);
}
internal readonly AsyncEvent<Func<SocketAutoModRule, Task>> _autoModRuleDeleted = new AsyncEvent<Func<SocketAutoModRule, Task>>();

//public event Func<SocketAutoModRule>
public event Func<SocketGuild, AutoModRuleAction, AutoModActionExecutedData, Task> AutoModActionExecuted
{
add => _autoModActionExecuted.Add(value);
remove => _autoModActionExecuted.Remove(value);
}
internal readonly AsyncEvent<Func<SocketGuild, AutoModRuleAction, AutoModActionExecutedData, Task>> _autoModActionExecuted = new ();
#endregion
}
}

+ 107
- 33
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -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<API.Interaction>(_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<SocketThreadUser> newThreadMembers = new List<SocketThreadUser>();
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<AutoModActionExecutedEvent>(_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<SocketGuildUser, ulong>(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<ISocketMessageChannel, ulong>(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<IUserMessage, ulong>(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<IAutoModRule, ulong>(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
}


+ 46
- 0
src/Discord.Net.WebSocket/Entities/Guilds/AutoModActionExecutedData.cs View File

@@ -0,0 +1,46 @@
using Discord.Rest;

namespace Discord.WebSocket;

public class AutoModActionExecutedData
{
public Cacheable<IAutoModRule, ulong> Rule { get; }

public AutoModTriggerType TriggerType { get; }

public Cacheable<SocketGuildUser, ulong> User { get; }

public Cacheable<ISocketMessageChannel, ulong> Channel { get; }

public Cacheable<IUserMessage, ulong> Message { get; }

public ulong AlertMessageId { get; }
public string Content { get; }

public string MatchedContent { get; }

public string MatchedKeyword { get; }

internal AutoModActionExecutedData(Cacheable<IAutoModRule, ulong> rule,
AutoModTriggerType triggerType,
Cacheable<SocketGuildUser, ulong> user,
Cacheable<ISocketMessageChannel, ulong> channel,
Cacheable<IUserMessage, ulong> 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;
}
}

Loading…
Cancel
Save