diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs index 5732475..04ed9d3 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs @@ -11,10 +11,7 @@ using MareSynchronosShared.Services; using MareSynchronosShared.Utils.Configuration; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; using StackExchange.Redis; -using System.Text; -using System.Threading.Channels; namespace MareSynchronosServices.Discord; @@ -208,6 +205,8 @@ internal class DiscordBot : IHostedService await _interactionModule.RegisterCommandsToGuildAsync(guild.Id, true).ConfigureAwait(false); await CreateOrUpdateModal(guild).ConfigureAwait(false); + _botServices.UpdateGuild(guild); + await _botServices.LogToChannel("Bot startup complete.").ConfigureAwait(false); _ = UpdateVanityRoles(guild); _ = RemoveUsersNotInVanityRole(); } @@ -332,6 +331,7 @@ internal class DiscordBot : IHostedService try { _logger.LogInformation($"Cleaning up Vanity UIDs"); + await _botServices.LogToChannel("Cleaning up Vanity UIDs").ConfigureAwait(false); _logger.LogInformation("Getting application commands from guild {guildName}", guild.Name); var restGuild = await _discordClient.Rest.GetGuildAsync(guild.Id); @@ -356,6 +356,7 @@ internal class DiscordBot : IHostedService if (discordUser == null || !discordUser.RoleIds.Any(u => allowedRoleIds.Keys.Contains(u))) { _logger.LogInformation($"User {lodestoneAuth.User.UID} not in allowed roles, deleting alias"); + await _botServices.LogToChannel($"VANITY UID REMOVAL: {discordUser.Mention} - {lodestoneAuth.User.UID}").ConfigureAwait(false); lodestoneAuth.User.Alias = null; var secondaryUsers = await db.Auth.Include(u => u.User).Where(u => u.PrimaryUserUID == lodestoneAuth.User.UID).ToListAsync().ConfigureAwait(false); foreach (var secondaryUser in secondaryUsers) @@ -385,6 +386,8 @@ internal class DiscordBot : IHostedService if (lodestoneUser == null || discordUser == null || !discordUser.RoleIds.Any(u => allowedRoleIds.Keys.Contains(u))) { + await _botServices.LogToChannel($"VANITY GID REMOVAL: {discordUser.Mention} - {group.GID}").ConfigureAwait(false); + _logger.LogInformation($"User {lodestoneUser.User.UID} not in allowed roles, deleting group alias"); group.Alias = null; db.Update(group); diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBotServices.cs b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBotServices.cs index 0e744dc..5a73b34 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBotServices.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBotServices.cs @@ -1,6 +1,8 @@ using System.Collections.Concurrent; using Discord.Rest; using MareSynchronosShared.Metrics; +using MareSynchronosShared.Services; +using MareSynchronosShared.Utils.Configuration; namespace MareSynchronosServices.Discord; @@ -15,12 +17,18 @@ public class DiscordBotServices public ConcurrentDictionary ValidInteractions { get; } = new(); public Dictionary VanityRoles { get; set; } = new(); private readonly IServiceProvider _serviceProvider; + private readonly IConfigurationService _configuration; private CancellationTokenSource verificationTaskCts; + private RestGuild? _guild; + private ulong? _logChannelId; + private RestTextChannel? _logChannel; - public DiscordBotServices(ILogger logger, MareMetrics metrics) + public DiscordBotServices(ILogger logger, MareMetrics metrics, + IConfigurationService configuration) { Logger = logger; Metrics = metrics; + _configuration = configuration; } public ILogger Logger { get; init; } @@ -39,6 +47,29 @@ public class DiscordBotServices return Task.CompletedTask; } + public async Task LogToChannel(string msg) + { + if (_guild == null) return; + var logChannelId = _configuration.GetValueOrDefault(nameof(ServicesConfiguration.DiscordChannelForBotLog), null); + if (logChannelId == null) return; + if (logChannelId != _logChannelId) + { + try + { + _logChannelId = logChannelId; + _logChannel = await _guild.GetTextChannelAsync(logChannelId.Value).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.LogError(ex, "Could not get bot log channel"); + } + } + + if (_logChannel == null) return; + + await _logChannel.SendMessageAsync(msg).ConfigureAwait(false); + } + private async Task ProcessVerificationQueue() { verificationTaskCts = new CancellationTokenSource(); @@ -65,4 +96,9 @@ public class DiscordBotServices await Task.Delay(TimeSpan.FromSeconds(2), verificationTaskCts.Token).ConfigureAwait(false); } } + + internal void UpdateGuild(RestGuild guild) + { + _guild = guild; + } } \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Delete.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Delete.cs index 8f09cd4..14ac0bd 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Delete.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Delete.cs @@ -99,6 +99,8 @@ public partial class MareWizardModule AddHome(cb); await ModifyModalInteraction(eb, cb).ConfigureAwait(false); + + await _botServices.LogToChannel($"{Context.User.Mention} DELETE SUCCESS: {uid}").ConfigureAwait(false); } } catch (Exception ex) diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Recover.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Recover.cs index d9c39be..fc99936 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Recover.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Recover.cs @@ -82,5 +82,6 @@ public partial class MareWizardModule await db.SaveChangesAsync().ConfigureAwait(false); _botServices.Logger.LogInformation("User recovered: {userUID}:{hashedKey}", previousAuth.UserUID, hashedKey); + await _botServices.LogToChannel($"{Context.User.Mention} RECOVER SUCCESS: {previousAuth.UserUID}").ConfigureAwait(false); } } diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Register.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Register.cs index f7c91b4..93e375a 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Register.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Register.cs @@ -225,6 +225,7 @@ public partial class MareWizardModule { services.DiscordVerifiedUsers[userid] = true; _logger.LogInformation("Verified {userid} from lodestone {lodestone}", userid, services.DiscordLodestoneMapping[userid]); + await _botServices.LogToChannel($"<@{userid}> REGISTER VERIFY: Success.").ConfigureAwait(false); services.DiscordLodestoneMapping.TryRemove(userid, out _); } else @@ -232,11 +233,13 @@ public partial class MareWizardModule services.DiscordVerifiedUsers[userid] = false; _logger.LogInformation("Could not verify {userid} from lodestone {lodestone}, did not find authString: {authString}, status code was: {code}", userid, services.DiscordLodestoneMapping[userid], authString, response.StatusCode); + await _botServices.LogToChannel($"<@{userid}> REGISTER VERIFY: Failed: No Authstring. ({url})").ConfigureAwait(false); } } else { _logger.LogWarning("Could not verify {userid}, HttpStatusCode: {code}", userid, response.StatusCode); + await _botServices.LogToChannel($"<@{userid}> REGISTER VERIFY: Failed: HttpStatusCode {response.StatusCode}. ({url})").ConfigureAwait(false); } } } @@ -283,6 +286,8 @@ public partial class MareWizardModule _botServices.Logger.LogInformation("User registered: {userUID}:{hashedKey}", user.UID, hashedKey); + await _botServices.LogToChannel($"{Context.User.Mention} REGISTER COMPLETE: => {user.UID}").ConfigureAwait(false); + _botServices.DiscordVerifiedUsers.Remove(Context.User.Id, out _); return (user.UID, computedHash); diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Relink.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Relink.cs index 8d379cb..59e3709 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Relink.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Relink.cs @@ -219,6 +219,7 @@ public partial class MareWizardModule { services.DiscordVerifiedUsers[userid] = true; _logger.LogInformation("Relink: Verified {userid} from lodestone {lodestone}", userid, services.DiscordRelinkLodestoneMapping[userid]); + await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Success. ({url})").ConfigureAwait(false); services.DiscordRelinkLodestoneMapping.TryRemove(userid, out _); } else @@ -226,11 +227,13 @@ public partial class MareWizardModule services.DiscordVerifiedUsers[userid] = false; _logger.LogInformation("Relink: Could not verify {userid} from lodestone {lodestone}, did not find authString: {authString}, status code was: {code}", userid, services.DiscordRelinkLodestoneMapping[userid], authString, response.StatusCode); + await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Failed: No Authstring. ({url})").ConfigureAwait(false); } } else { _logger.LogWarning("Could not verify {userid}, HttpStatusCode: {code}", userid, response.StatusCode); + await _botServices.LogToChannel($"<@{userid}> RELINK VERIFY: Failed: HttpStatusCode {response.StatusCode}. ({url})").ConfigureAwait(false); } } } @@ -266,6 +269,8 @@ public partial class MareWizardModule await db.SaveChangesAsync().ConfigureAwait(false); + await _botServices.LogToChannel($"{Context.User.Mention} RELINK COMPLETE: => {user.UID}").ConfigureAwait(false); + return (user.UID, computedHash); } } diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Secondary.cs b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Secondary.cs index 40780c6..1dbf530 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Secondary.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/MareWizardModule.Secondary.cs @@ -84,6 +84,8 @@ public partial class MareWizardModule embed.WithDescription("A secondary UID for you was created, use the information below and add the secret key to the Mare setings in the Service Settings tab."); embed.AddField("UID", newUser.UID); embed.AddField("Secret Key", computedHash); + + await _botServices.LogToChannel($"{Context.User.Mention} SECONDARY SUCCESS: {newUser.UID}").ConfigureAwait(false); } } diff --git a/MareSynchronosServer/MareSynchronosShared/Utils/Configuration/ServicesConfiguration.cs b/MareSynchronosServer/MareSynchronosShared/Utils/Configuration/ServicesConfiguration.cs index aadbaa6..9a0bce4 100644 --- a/MareSynchronosServer/MareSynchronosShared/Utils/Configuration/ServicesConfiguration.cs +++ b/MareSynchronosServer/MareSynchronosShared/Utils/Configuration/ServicesConfiguration.cs @@ -8,6 +8,7 @@ public class ServicesConfiguration : MareConfigurationBase public ulong? DiscordChannelForMessages { get; set; } = null; public ulong? DiscordChannelForCommands { get; set; } = null; public ulong? DiscordRoleAprilFools2024 { get; set; } = null; + public ulong? DiscordChannelForBotLog { get; set; } = null!; public Uri MainServerAddress { get; set; } = null; public Dictionary VanityRoles { get; set; } = new Dictionary();