Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
OoLunar committed Jan 22, 2024
1 parent a830617 commit 58de895
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 44 deletions.
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ dotnet_style_qualification_for_property = true:error
dotnet_style_qualification_for_method = true:error
dotnet_style_qualification_for_event = true:error

# Disable primary constructors
dotnet_diagnostic.IDE0290.severity = none

# Disable "Rename type name <class name> so it does not end in "EventHandler"
dotnet_diagnostic.CA1711.severity = none

# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:error
dotnet_style_predefined_type_for_member_access = true:error
Expand Down
6 changes: 6 additions & 0 deletions src/Events/DiscordEventAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using System;

namespace NotifierRedirecter.Events;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public sealed class DiscordEventAttribute : Attribute;
49 changes: 49 additions & 0 deletions src/Events/DiscordEventManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;

namespace NotifierRedirecter.Events;

public sealed class DiscordEventManager
{
private readonly IServiceProvider ServiceProvider;
private readonly List<MethodInfo> EventHandlers = [];

public DiscordEventManager(IServiceProvider serviceProvider) => this.ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));

public void GatherEventHandlers(Assembly assembly)
{
ArgumentNullException.ThrowIfNull(assembly);
foreach (Type type in assembly.GetExportedTypes())
{
foreach (MethodInfo methodInfo in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
if (methodInfo.GetCustomAttribute<DiscordEventAttribute>() is not null)
{
this.EventHandlers.Add(methodInfo);
}
}
}
}

public void RegisterEventHandlers(object obj)
{
ArgumentNullException.ThrowIfNull(obj);
foreach (EventInfo eventInfo in obj.GetType().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static))
{
foreach (MethodInfo methodInfo in this.EventHandlers)
{
if (eventInfo.EventHandlerType!.GetGenericArguments().SequenceEqual(methodInfo.GetParameters().Select(parameter => parameter.ParameterType)))
{
Delegate handler = methodInfo.IsStatic
? Delegate.CreateDelegate(eventInfo.EventHandlerType, methodInfo)
: Delegate.CreateDelegate(eventInfo.EventHandlerType, ActivatorUtilities.CreateInstance(this.ServiceProvider, methodInfo.DeclaringType!), methodInfo);

eventInfo.AddEventHandler(obj, handler);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using DSharpPlus.Exceptions;
using Humanizer;

namespace NotifierRedirecter.Events;
namespace NotifierRedirecter.Events.Handlers;

public sealed class CommandErroredEventHandler
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace NotifierRedirecter.Events;
namespace NotifierRedirecter.Events.Handlers;

public sealed class GuildDownloadCompletedEventHandler(ILogger<GuildDownloadCompletedEventHandler>? logger = null)
public sealed class GuildDownloadCompletedEventHandler
{
private readonly ILogger<GuildDownloadCompletedEventHandler> _logger = logger ?? NullLogger<GuildDownloadCompletedEventHandler>.Instance;
private readonly ILogger<GuildDownloadCompletedEventHandler> _logger;

public GuildDownloadCompletedEventHandler(ILogger<GuildDownloadCompletedEventHandler>? logger = null) => this._logger = logger ?? NullLogger<GuildDownloadCompletedEventHandler>.Instance;
public Task ExecuteAsync(DiscordClient _, GuildDownloadCompletedEventArgs eventArgs)
{
foreach (DiscordGuild guild in eventArgs.Guilds.Values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,37 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace NotifierRedirecter.Events;
namespace NotifierRedirecter.Events.Handlers;

public sealed partial class MessageCreatedEventHandler(UserActivityTracker userActivityTracker, Database database, ILogger<MessageCreatedEventHandler>? logger = null)
public sealed partial class MessageCreatedEventHandler
{
private readonly ILogger<MessageCreatedEventHandler> _logger = logger ?? NullLogger<MessageCreatedEventHandler>.Instance;
private readonly ILogger<MessageCreatedEventHandler> _logger;
private readonly UserActivityTracker _userActivityTracker;
private readonly Database _database;

public async Task ExecuteAsync(DiscordClient _, MessageCreateEventArgs eventArgs)
public MessageCreatedEventHandler(UserActivityTracker userActivityTracker, Database database, ILogger<MessageCreatedEventHandler>? logger = null)
{
userActivityTracker.UpdateUser(eventArgs.Author.Id, eventArgs.Channel.Id);
this._userActivityTracker = userActivityTracker ?? throw new ArgumentNullException(nameof(userActivityTracker));
this._database = database ?? throw new ArgumentNullException(nameof(database));
this._logger = logger ?? NullLogger<MessageCreatedEventHandler>.Instance;
}

public async Task ExecuteAsync(DiscordClient _, MessageCreateEventArgs eventArgs)
{
this._userActivityTracker.UpdateUser(eventArgs.Author.Id, eventArgs.Channel.Id);
bool shouldSilence = eventArgs.Message.Flags?.HasFlag(MessageFlags.SupressNotifications) ?? false;
DiscordMessage message = eventArgs.Message;

// Explicitly cast to nullable to prevent erroneous compiler warning about it
// not being nullable.
DiscordMessage? reply = (DiscordMessage?)message.ReferencedMessage;

// Ensure the channel is a redirect channel
if (!database.IsRedirect(message.Channel.Id))
if (!this._database.IsRedirect(eventArgs.Message.Channel.Id))
{
return;
}

IEnumerable<DiscordUser> mentionedUsers = message.MentionedUsers;
if (reply is not null && reply.MentionedUsers.Contains(reply.Author))
// Explicitly cast to nullable to prevent erroneous compiler
// warning about it not being nullable.
DiscordMessage? reply = (DiscordMessage?)eventArgs.Message.ReferencedMessage;
IEnumerable<DiscordUser> mentionedUsers = eventArgs.Message.MentionedUsers;
if (reply is not null && reply.MentionedUsers.Contains(reply.Author) && reply.Author != eventArgs.Message.Author)
{
mentionedUsers = mentionedUsers.Prepend(eventArgs.Message.ReferencedMessage.Author);
}
Expand All @@ -43,7 +49,11 @@ public async Task ExecuteAsync(DiscordClient _, MessageCreateEventArgs eventArgs
{
// Check if the user has explicitly opted out of being pinged.
// Additionally check if the user has recently done activity within the channel.
if (user.IsBot || user == message.Author || await userActivityTracker.IsActiveAsync(user.Id, eventArgs.Channel.Id) || database.IsIgnoredUser(user.Id, eventArgs.Guild.Id, eventArgs.Channel.Id) || database.IsBlockedUser(user.Id, eventArgs.Guild.Id, eventArgs.Author.Id))
if (user.IsBot
|| user == eventArgs.Message.Author
|| await this._userActivityTracker.IsActiveAsync(user.Id, eventArgs.Channel.Id)
|| this._database.IsIgnoredUser(user.Id, eventArgs.Guild.Id, eventArgs.Channel.Id)
|| this._database.IsBlockedUser(user.Id, eventArgs.Guild.Id, eventArgs.Author.Id))
{
continue;
}
Expand All @@ -57,7 +67,7 @@ public async Task ExecuteAsync(DiscordClient _, MessageCreateEventArgs eventArgs
}
catch (NotFoundException error)
{
this._logger.LogDebug(error, "User {UserId} doesn't exist!", user.Id);
this._logger.LogDebug(error, "User {UserId} could not be found.", user.Id);
continue;
}
catch (DiscordException error)
Expand All @@ -76,7 +86,7 @@ public async Task ExecuteAsync(DiscordClient _, MessageCreateEventArgs eventArgs
try
{
DiscordMessageBuilder builder = new DiscordMessageBuilder()
.WithContent($"You were pinged by {message.Author.Mention} in {eventArgs.Channel.Mention}. [Jump! \u2197]({message.JumpLink})");
.WithContent($"You were pinged by {eventArgs.Message.Author.Mention} in {eventArgs.Channel.Mention}. [Jump! \u2197]({eventArgs.Message.JumpLink})");

if (shouldSilence)
{
Expand Down
18 changes: 18 additions & 0 deletions src/Events/Handlers/TypingStartedEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
using DSharpPlus;
using DSharpPlus.EventArgs;

namespace NotifierRedirecter.Events.Handlers;

public sealed class TypingStartedEventHandler
{
private readonly UserActivityTracker _userActivityTracker;

public TypingStartedEventHandler(UserActivityTracker userActivityTracker) => this._userActivityTracker = userActivityTracker ?? throw new ArgumentNullException(nameof(userActivityTracker));
public Task ExecuteAsync(DiscordClient _, TypingStartEventArgs eventArgs)
{
this._userActivityTracker.UpdateUser(eventArgs.User.Id, eventArgs.Channel.Id);
return Task.CompletedTask;
}
}
14 changes: 0 additions & 14 deletions src/Events/TypingStartedEventHandler.cs

This file was deleted.

33 changes: 24 additions & 9 deletions src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NotifierRedirecter.Events;
using NotifierRedirecter.Events.Handlers;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
Expand All @@ -35,8 +35,17 @@ public static async Task Main(string[] args)
serviceCollection.AddSingleton(configuration);
serviceCollection.AddLogging(logger =>
{
string loggingFormat = configuration.GetValue("Logging:Format", "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u4}] {SourceContext}: {Message:lj}{NewLine}{Exception}") ?? throw new InvalidOperationException("Logging:Format is null");
string filename = configuration.GetValue("Logging:Filename", "yyyy'-'MM'-'dd' 'HH'.'mm'.'ss") ?? throw new InvalidOperationException("Logging:Filename is null");
string? loggingFormat = configuration.GetValue<string?>("Logging:Format");
if (string.IsNullOrWhiteSpace(loggingFormat))
{
loggingFormat = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u4}] {SourceContext}: {Message:lj}{NewLine}{Exception}";
}

string? filename = configuration.GetValue<string?>("Logging:Filename");
if (!string.IsNullOrWhiteSpace(filename))
{
filename = "yyyy'-'MM'-'dd' 'HH'.'mm'.'ss";
}

// Log both to console and the file
LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
Expand Down Expand Up @@ -64,7 +73,7 @@ public static async Task Main(string[] args)
[ConsoleThemeStyle.LevelFatal] = "\x1b[97;91m"
}))
.WriteTo.File(
$"logs/{DateTime.Now.ToUniversalTime().ToString("yyyy'-'MM'-'dd' 'HH'.'mm'.'ss", CultureInfo.InvariantCulture)}-.log",
$"logs/{DateTime.Now.ToUniversalTime().ToString(filename, CultureInfo.InvariantCulture)}-.log",
formatProvider: CultureInfo.InvariantCulture,
outputTemplate: loggingFormat,
rollingInterval: RollingInterval.Day
Expand All @@ -73,12 +82,10 @@ public static async Task Main(string[] args)
// Allow specific namespace log level overrides, which allows us to hush output from things like the database basic SELECT queries on the Information level.
foreach (IConfigurationSection logOverride in configuration.GetSection("logging:overrides").GetChildren())
{
if (logOverride.Value is null || !Enum.TryParse(logOverride.Value, out LogEventLevel logEventLevel))
if (!string.IsNullOrWhiteSpace(logOverride.Value) && Enum.TryParse(logOverride.Value, out LogEventLevel logEventLevel))
{
continue;
loggerConfiguration.MinimumLevel.Override(logOverride.Key, logEventLevel);
}

loggerConfiguration.MinimumLevel.Override(logOverride.Key, logEventLevel);
}

logger.AddSerilog(loggerConfiguration.CreateLogger());
Expand All @@ -93,10 +100,18 @@ public static async Task Main(string[] args)
// Register the Discord sharded client to the service collection
serviceCollection.AddSingleton((serviceProvider) =>
{
ILogger<Program> logger = serviceProvider.GetRequiredService<ILogger<Program>>();
IConfiguration configuration = serviceProvider.GetRequiredService<IConfiguration>();
string? discordToken = configuration.GetValue<string>("discord:token");
if (string.IsNullOrWhiteSpace(discordToken))
{
logger.LogCritical("Discord:Token was not provided to the application. Please provide a token in the configuration file, through an environment variable, or as a command line argument.");
Environment.Exit(1);
}

DiscordClient client = new(new DiscordConfiguration()
{
Token = configuration.GetValue<string>("discord:token") ?? throw new InvalidOperationException("Discord bot token is null."),
Token = discordToken,
Intents = DiscordIntents.Guilds | DiscordIntents.GuildMessages | DiscordIntents.MessageContents,
LoggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(),
LogUnknownEvents = false
Expand Down
1 change: 0 additions & 1 deletion src/UserActivityTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public sealed class UserActivityTracker
private readonly ConcurrentDictionary<ulong, (ulong ChannelId, DateTimeOffset LastActivity)> _tracker = new();

public UserActivityTracker() => _ = this.CleanupInactiveUsersAsync();

public void UpdateUser(ulong userId, ulong channelId) => this._tracker.AddOrUpdate(userId, (channelId, DateTimeOffset.UtcNow), (_, _) => (channelId, DateTimeOffset.UtcNow));
public async ValueTask<bool> IsActiveAsync(ulong userId, ulong channelId)
{
Expand Down

0 comments on commit 58de895

Please sign in to comment.