refactor: clean up log channel resolution

This commit is contained in:
sam 2025-04-11 14:41:09 +02:00
parent 24f6aee57d
commit a4a6fb5d31
Signed by: sam
GPG key ID: 5F3C3C1B3166639D

View file

@ -43,7 +43,7 @@ public class WebhookExecutorService(
private readonly ILogger _logger = logger.ForContext<WebhookExecutorService>(); private readonly ILogger _logger = logger.ForContext<WebhookExecutorService>();
private readonly Snowflake _applicationId = DiscordSnowflake.New(config.Discord.ApplicationId); private readonly Snowflake _applicationId = DiscordSnowflake.New(config.Discord.ApplicationId);
private readonly ConcurrentDictionary<ulong, ConcurrentQueue<IEmbed>> _cache = new(); private readonly ConcurrentDictionary<ulong, ConcurrentQueue<IEmbed>> _cache = new();
private readonly ConcurrentDictionary<ulong, object> _locks = new(); private readonly ConcurrentDictionary<ulong, Lock> _locks = new();
private readonly ConcurrentDictionary<ulong, Timer> _timers = new(); private readonly ConcurrentDictionary<ulong, Timer> _timers = new();
private IUser? _selfUser; private IUser? _selfUser;
@ -189,7 +189,7 @@ public class WebhookExecutorService(
private List<IEmbed> TakeFromQueue(ulong channelId) private List<IEmbed> TakeFromQueue(ulong channelId)
{ {
var queue = _cache.GetOrAdd(channelId, []); var queue = _cache.GetOrAdd(channelId, []);
var channelLock = _locks.GetOrAdd(channelId, channelId); var channelLock = _locks.GetOrAdd(channelId, new Lock());
lock (channelLock) lock (channelLock)
{ {
var totalContentLength = 0; var totalContentLength = 0;
@ -293,10 +293,10 @@ public class WebhookExecutorService(
roleIds != null && logChannelType is LogChannelType.GuildMemberUpdate; roleIds != null && logChannelType is LogChannelType.GuildMemberUpdate;
if (isMessageLog) if (isMessageLog)
return GetMessageLogChannel(guild, logChannelType, channelId, userId); return GetLogChannelForMessageEvent(guild, logChannelType, channelId, userId);
if (isChannelLog) if (isChannelLog)
return GetChannelLogChannel(guild, logChannelType, channelId!.Value); return GetLogChannelForChannelEvent(guild, logChannelType, channelId!.Value);
if (isRoleLog && guild.IgnoredRoles.Contains(roleId!.Value.Value)) if (isRoleLog && guild.IgnoredRoles.Contains(roleId!.Value.Value))
return null; return null;
@ -305,77 +305,11 @@ public class WebhookExecutorService(
if (isMemberRoleUpdateLog && roleIds!.All(r => guild.IgnoredRoles.Contains(r.Value))) if (isMemberRoleUpdateLog && roleIds!.All(r => guild.IgnoredRoles.Contains(r.Value)))
return null; return null;
// If nothing is ignored, return the correct log channel! // If nothing is ignored, and this isn't a message or channel event, return the default log channel.
return GetDefaultLogChannel(guild, logChannelType); return GetDefaultLogChannel(guild, logChannelType);
} }
private ulong? GetChannelLogChannel( private ulong? GetLogChannelForMessageEvent(
Guild guild,
LogChannelType logChannelType,
Snowflake channelId
)
{
_logger.Verbose(
"Getting log channel for event {Event} in guild {GuildId} and channel {ChannelId}",
logChannelType,
guild.Id,
channelId
);
if (!channelCache.TryGet(channelId, out var channel))
{
_logger.Verbose(
"Channel with ID {ChannelId} is not cached, returning default log channel",
channelId
);
return GetDefaultLogChannel(guild, logChannelType);
}
Snowflake? categoryId;
if (
channel.Type
is ChannelType.AnnouncementThread
or ChannelType.PrivateThread
or ChannelType.PublicThread
)
{
// parent_id should always have a value for threads
channelId = channel.ParentID.Value!.Value;
if (!channelCache.TryGet(channelId, out var parentChannel))
{
_logger.Verbose(
"Parent channel for thread {ChannelId} is not in cache, returning the default log channel",
channelId
);
return GetDefaultLogChannel(guild, logChannelType);
}
categoryId = parentChannel.ParentID.Value;
}
else
{
channelId = channel.ID;
categoryId = channel.ParentID.Value;
}
// Check if the channel or its category is ignored
if (
guild.IgnoredChannels.Contains(channelId.Value)
|| (categoryId != null && guild.IgnoredChannels.Contains(categoryId.Value.Value))
)
{
_logger.Verbose(
"Channel {ChannelId} or its parent {CategoryId} is ignored",
channelId,
categoryId
);
return null;
}
_logger.Verbose("Returning default log channel for {EventType}", logChannelType);
return GetDefaultLogChannel(guild, logChannelType);
}
private ulong? GetMessageLogChannel(
Guild guild, Guild guild,
LogChannelType logChannelType, LogChannelType logChannelType,
Snowflake? channelId = null, Snowflake? channelId = null,
@ -415,41 +349,24 @@ public class WebhookExecutorService(
return GetDefaultLogChannel(guild, logChannelType); return GetDefaultLogChannel(guild, logChannelType);
} }
Snowflake? categoryId; if (!GetChannelAndParentId(channel, out var actualChannelId, out var categoryId))
if (
channel.Type
is ChannelType.AnnouncementThread
or ChannelType.PrivateThread
or ChannelType.PublicThread
)
{ {
// parent_id should always have a value for threads _logger.Verbose(
channelId = channel.ParentID.Value!.Value; "Could not get root channel and category ID for channel {ChannelId}, returning default log channel",
if (!channelCache.TryGet(channelId.Value, out var parentChannel)) channelId
{ );
_logger.Verbose( return GetDefaultLogChannel(guild, logChannelType);
"Parent channel for thread {ChannelId} is not in cache, returning the default log channel",
channelId
);
return GetDefaultLogChannel(guild, logChannelType);
}
categoryId = parentChannel.ParentID.Value;
}
else
{
channelId = channel.ID;
categoryId = channel.ParentID.Value;
} }
// Check if the channel or its category is ignored // Check if the channel or its category is ignored
if ( if (
guild.Messages.IgnoredChannels.Contains(channelId.Value.Value) guild.Messages.IgnoredChannels.Contains(actualChannelId.Value)
|| categoryId != null && guild.Messages.IgnoredChannels.Contains(categoryId.Value.Value) || categoryId != null && guild.Messages.IgnoredChannels.Contains(categoryId.Value.Value)
) )
{ {
_logger.Verbose( _logger.Verbose(
"Channel {ChannelId} or its parent {CategoryId} is ignored", "Channel {ChannelId} or its parent {CategoryId} is ignored",
channelId, actualChannelId,
categoryId categoryId
); );
return null; return null;
@ -459,8 +376,10 @@ public class WebhookExecutorService(
{ {
// Check the channel-local and category-local ignored users // Check the channel-local and category-local ignored users
var channelIgnoredUsers = var channelIgnoredUsers =
guild.Messages.IgnoredUsersPerChannel.GetValueOrDefault(channelId.Value.Value) guild.Messages.IgnoredUsersPerChannel.GetValueOrDefault(actualChannelId.Value)
?? []; ?? [];
// Obviously, we can only check for category-level ignored users if we actually got a category ID.
var categoryIgnoredUsers = var categoryIgnoredUsers =
( (
categoryId != null categoryId != null
@ -469,6 +388,8 @@ public class WebhookExecutorService(
) )
: [] : []
) ?? []; ) ?? [];
// Combine the ignored users in the channel and category, then check if the user is in there.
if (channelIgnoredUsers.Concat(categoryIgnoredUsers).Contains(userId.Value)) if (channelIgnoredUsers.Concat(categoryIgnoredUsers).Contains(userId.Value))
{ {
_logger.Verbose( _logger.Verbose(
@ -482,7 +403,7 @@ public class WebhookExecutorService(
} }
// These three events can be redirected to other channels. Redirects can be on a channel or category level. // These three events can be redirected to other channels. Redirects can be on a channel or category level.
// The events are only redirected if they're supposed to be logged in the first place. // The events are only redirected if they're supposed to be logged in the first place (i.e. GetDefaultLogChannel doesn't return 0)
if (GetDefaultLogChannel(guild, logChannelType) == 0) if (GetDefaultLogChannel(guild, logChannelType) == 0)
{ {
_logger.Verbose( _logger.Verbose(
@ -492,21 +413,21 @@ public class WebhookExecutorService(
return null; return null;
} }
var categoryRedirect = if (guild.Channels.Redirects.TryGetValue(actualChannelId.Value, out var channelRedirect))
categoryId != null
? guild.Channels.Redirects.GetValueOrDefault(categoryId.Value.Value)
: 0;
if (guild.Channels.Redirects.TryGetValue(channelId.Value.Value, out var channelRedirect))
{ {
_logger.Verbose( _logger.Verbose(
"Messages from channel {ChannelId} should be redirected to {RedirectId}", "Messages from channel {ChannelId} should be redirected to {RedirectId}",
channelId, actualChannelId,
channelRedirect channelRedirect
); );
return channelRedirect; return channelRedirect;
} }
var categoryRedirect =
categoryId != null
? guild.Channels.Redirects.GetValueOrDefault(categoryId.Value.Value)
: 0;
if (categoryRedirect != 0) if (categoryRedirect != 0)
{ {
_logger.Verbose( _logger.Verbose(
@ -514,6 +435,7 @@ public class WebhookExecutorService(
categoryId, categoryId,
categoryRedirect categoryRedirect
); );
return categoryRedirect;
} }
_logger.Verbose( _logger.Verbose(
@ -523,6 +445,92 @@ public class WebhookExecutorService(
return GetDefaultLogChannel(guild, logChannelType); return GetDefaultLogChannel(guild, logChannelType);
} }
private ulong? GetLogChannelForChannelEvent(
Guild guild,
LogChannelType logChannelType,
Snowflake channelId
)
{
_logger.Verbose(
"Getting log channel for event {Event} in guild {GuildId} and channel {ChannelId}",
logChannelType,
guild.Id,
channelId
);
if (!channelCache.TryGet(channelId, out var channel))
{
_logger.Verbose(
"Channel with ID {ChannelId} is not cached, returning default log channel",
channelId
);
return GetDefaultLogChannel(guild, logChannelType);
}
if (!GetChannelAndParentId(channel, out channelId, out var categoryId))
{
_logger.Verbose(
"Could not get root channel and category ID for channel {ChannelId}, returning default log channel",
channelId
);
return GetDefaultLogChannel(guild, logChannelType);
}
// Check if the channel or its category is ignored
if (
guild.IgnoredChannels.Contains(channelId.Value)
|| (categoryId != null && guild.IgnoredChannels.Contains(categoryId.Value.Value))
)
{
_logger.Verbose(
"Channel {ChannelId} or its parent {CategoryId} is ignored",
channelId,
categoryId
);
return null;
}
_logger.Verbose("Returning default log channel for {EventType}", logChannelType);
return GetDefaultLogChannel(guild, logChannelType);
}
private bool GetChannelAndParentId(
IChannel channel,
out Snowflake channelId,
out Snowflake? categoryId
)
{
if (
channel.Type
is ChannelType.AnnouncementThread
or ChannelType.PrivateThread
or ChannelType.PublicThread
)
{
// parent_id should always have a value for threads
channelId = channel.ParentID.Value!.Value;
if (!channelCache.TryGet(channelId, out var parentChannel))
{
_logger.Verbose(
"Parent channel for thread {ChannelId} is not in cache, returning the default log channel",
channelId
);
channelId = Snowflake.CreateTimestampSnowflake();
categoryId = null;
return false;
}
categoryId = parentChannel.ParentID.Value;
}
else
{
channelId = channel.ID;
categoryId = channel.ParentID.Value;
}
return true;
}
public static ulong GetDefaultLogChannel(Guild guild, LogChannelType logChannelType) => public static ulong GetDefaultLogChannel(Guild guild, LogChannelType logChannelType) =>
logChannelType switch logChannelType switch
{ {