Skip to content

Commit

Permalink
Add DiscordLog service
Browse files Browse the repository at this point in the history
Responsible for logging actions in a private staff channel in Discord
  • Loading branch information
oliverbooth committed Mar 6, 2022
1 parent e4116e8 commit 6d6b19c
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
41 changes: 41 additions & 0 deletions Hammer/Resources/LoggerMessages.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>

<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">

</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="NoMutedRoleToRevoke" xml:space="preserve">
<value>Unable to revoke role! Muted role not found for {guild}</value>
</data>
<data name="TemporaryMuteExpired" xml:space="preserve">
<value>Temporary mute for {user} expired in {guild}</value>
</data>
<data name="LogChannelNotFound" xml:space="preserve">
<value>Log channel for {guild} was not found. Actions for this guild will NOT be logged in Discord!</value>
</data>
<data name="LogChannelNotDefined" xml:space="preserve">
<value>No log channel defined for {guild}. Actions for this guild will NOT be logged in Discord!</value>
</data>
<data name="LogChannelFound" xml:space="preserve">
<value>Log channel {channel} was found for {guild}</value>
</data>
</root>
141 changes: 141 additions & 0 deletions Hammer/Services/DiscordLogService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System.Diagnostics.CodeAnalysis;
using DisCatSharp;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using Hammer.Configuration;
using Hammer.Data;
using Hammer.Resources;
using Microsoft.Extensions.Hosting;
using NLog;
using SmartFormat;

namespace Hammer.Services;

/// <summary>
/// Represents a service which manages staff log channels, and allows the posting of embeds and messages in a guild's staff
/// log channel.
/// </summary>
internal sealed class DiscordLogService : BackgroundService
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
private readonly DiscordClient _discordClient;
private readonly ConfigurationService _configurationService;

/// <summary>
/// Initializes a new instance of the <see cref="DiscordLogService" /> class.
/// </summary>
public DiscordLogService(DiscordClient discordClient, ConfigurationService configurationService)
{
_discordClient = discordClient;
_configurationService = configurationService;
}

/// <summary>
/// Gets the log channel for a specified guild.
/// </summary>
/// <param name="guild">The guild whose log channel to retrieve.</param>
/// <param name="channel">
/// When this method returns, contains the log channel; or <see langword="null" /> if no such channel is found.
/// </param>
/// <returns><see langword="true" /> if the log channel was successfully found; otherwise, <see langword="false" />.</returns>
/// <exception cref="ArgumentNullException"><paramref name="guild" /> is <see langword="null" />.</exception>
public bool TryGetLogChannel(DiscordGuild guild, [NotNullWhen(true)] out DiscordChannel? channel)
{
if (guild is null)
throw new ArgumentNullException(nameof(guild));

ChannelConfiguration channelConfiguration = _configurationService.GetGuildConfiguration(guild).ChannelConfiguration;
channel = guild.GetChannel(channelConfiguration.LogChannelId);
return channel is not null;
}

/// <summary>
/// Logs a message to the staff log channel.
/// </summary>
/// <param name="guild">The guild in which to log.</param>
/// <param name="messageBuilder">The message builder.</param>
/// <param name="notificationOptions">
/// Optional. The staff notification options. Defaults to <see cref="StaffNotificationOptions.None" />.
/// </param>
public async Task<DiscordMessage?> LogAsync(
DiscordGuild guild,
DiscordMessageBuilder messageBuilder,
StaffNotificationOptions notificationOptions = StaffNotificationOptions.None
)
{
if (!TryGetLogChannel(guild, out DiscordChannel? logChannel))
return null;

string? mention = BuildMentionString(guild, notificationOptions);
if (!string.IsNullOrWhiteSpace(mention))
messageBuilder.WithContent(mention);

return await logChannel.SendMessageAsync(messageBuilder);
}

/// <summary>
/// Logs an embed to the staff log channel.
/// </summary>
/// <param name="guild">The guild in which to log.</param>
/// <param name="embed">The embed to log.</param>
/// <param name="notificationOptions">
/// Optional. The staff notification options. Defaults to <see cref="StaffNotificationOptions.None" />.
/// </param>
public async Task<DiscordMessage?> LogAsync(
DiscordGuild guild,
DiscordEmbed embed,
StaffNotificationOptions notificationOptions = StaffNotificationOptions.None
)
{
if (!TryGetLogChannel(guild, out DiscordChannel? logChannel))
return null;

return await logChannel.SendMessageAsync(BuildMentionString(guild, notificationOptions), embed);
}

/// <inheritdoc />
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_discordClient.GuildAvailable += DiscordClientOnGuildAvailable;
return Task.CompletedTask;
}

private Task DiscordClientOnGuildAvailable(DiscordClient sender, GuildCreateEventArgs e)
{
GuildConfiguration guildConfiguration = _configurationService.GetGuildConfiguration(e.Guild);
ulong logChannelId = guildConfiguration.ChannelConfiguration.LogChannelId;

if (logChannelId != 0)
{
if (e.Guild.GetChannel(logChannelId) is { } channel)
Logger.Warn(LoggerMessages.LogChannelFound.FormatSmart(new {channel, guild = e.Guild}));
else
Logger.Warn(LoggerMessages.LogChannelNotFound.FormatSmart(new {guild = e.Guild}));
}
else
{
Logger.Warn(LoggerMessages.LogChannelNotDefined.FormatSmart(new {guild = e.Guild}));
}

return Task.CompletedTask;
}

private string? BuildMentionString(DiscordGuild guild, StaffNotificationOptions notificationOptions)
{
if (!TryGetLogChannel(guild, out DiscordChannel? logChannel)) return null;
if (notificationOptions == StaffNotificationOptions.None) return null;

RoleConfiguration roleConfiguration = _configurationService.GetGuildConfiguration(logChannel.Guild).RoleConfiguration;
DiscordRole? administratorRole = logChannel.Guild.GetRole(roleConfiguration.AdministratorRoleId);
DiscordRole? moderatorRole = logChannel.Guild.GetRole(roleConfiguration.ModeratorRoleId);

var mentions = new List<string>();

if ((notificationOptions & StaffNotificationOptions.Administrator) != 0) mentions.Add(administratorRole.Mention);
if ((notificationOptions & StaffNotificationOptions.Moderator) != 0) mentions.Add(moderatorRole.Mention);
if ((notificationOptions & StaffNotificationOptions.Here) != 0) mentions.Add("@here");
if ((notificationOptions & StaffNotificationOptions.Everyone) != 0) mentions.Add("@everyone");

return string.Join(' ', mentions);
}
}

0 comments on commit 6d6b19c

Please sign in to comment.