diff --git a/src/Commands/AboutCommandGroup.cs b/src/Commands/AboutCommandGroup.cs index 70f4bcf..760d96b 100644 --- a/src/Commands/AboutCommandGroup.cs +++ b/src/Commands/AboutCommandGroup.cs @@ -72,8 +72,7 @@ public class AboutCommandGroup : CommandGroup { .WithColour(ColorsList.Cyan) .WithImageUrl("https://cdn.upload.systems/uploads/JFAaX5vr.png") .Build(); - if (!embed.IsDefined(out var built)) return Result.FromError(embed); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Commands/BanCommandGroup.cs b/src/Commands/BanCommandGroup.cs index d2c1c76..902c753 100644 --- a/src/Commands/BanCommandGroup.cs +++ b/src/Commands/BanCommandGroup.cs @@ -7,13 +7,13 @@ using Remora.Commands.Attributes; using Remora.Commands.Groups; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Abstractions.Rest; -using Remora.Discord.API.Objects; using Remora.Discord.Commands.Attributes; using Remora.Discord.Commands.Conditions; using Remora.Discord.Commands.Contexts; using Remora.Discord.Commands.Feedback.Services; using Remora.Discord.Extensions.Embeds; using Remora.Discord.Extensions.Formatting; +using Remora.Rest.Core; using Remora.Results; namespace Boyfriend.Commands; @@ -57,7 +57,7 @@ public class BanCommandGroup : CommandGroup { /// A feedback sending result which may or may not have succeeded. A successful result does not mean that the user /// was banned and vice-versa. /// - /// + /// [Command("ban", "бан")] [DiscordDefaultMemberPermissions(DiscordPermission.BanMembers)] [DiscordDefaultDMPermission(false)] @@ -66,122 +66,95 @@ public class BanCommandGroup : CommandGroup { [RequireBotDiscordPermissions(DiscordPermission.BanMembers)] [Description("Ban user")] [UsedImplicitly] - public async Task BanUserAsync( + public async Task ExecuteBan( [Description("User to ban")] IUser target, [Description("Ban reason")] string reason, [Description("Ban duration")] TimeSpan? duration = null) { if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId)) return Result.FromError( new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context")); - // The current user's avatar is used when sending error messages var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken); if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult); + var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken); + if (!userResult.IsDefined(out var user)) + return Result.FromError(userResult); + var guildResult = await _guildApi.GetGuildAsync(guildId.Value, ct: CancellationToken); + if (!guildResult.IsDefined(out var guild)) + return Result.FromError(guildResult); - var data = await _dataService.GetData(guildId.Value, CancellationToken); + return await BanUserAsync(target, reason, duration, guild, channelId.Value, user, currentUser); + } + + private async Task BanUserAsync( + IUser target, string reason, TimeSpan? duration, IGuild guild, Snowflake channelId, + IUser user, IUser currentUser) { + var data = await _dataService.GetData(guild.ID, CancellationToken); var cfg = data.Settings; Messages.Culture = GuildSettings.Language.Get(cfg); - var existingBanResult = await _guildApi.GetGuildBanAsync(guildId.Value, target.ID, CancellationToken); + var existingBanResult = await _guildApi.GetGuildBanAsync(guild.ID, target.ID, CancellationToken); if (existingBanResult.IsDefined()) { - var embed = new EmbedBuilder().WithSmallTitle(Messages.UserAlreadyBanned, currentUser) + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserAlreadyBanned, currentUser) .WithColour(ColorsList.Red).Build(); - if (!embed.IsDefined(out var alreadyBuilt)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, CancellationToken); } var interactionResult - = await _utility.CheckInteractionsAsync(guildId.Value, userId.Value, target.ID, "Ban", CancellationToken); + = await _utility.CheckInteractionsAsync(guild.ID, user.ID, target.ID, "Ban", CancellationToken); if (!interactionResult.IsSuccess) return Result.FromError(interactionResult); - Result responseEmbed; if (interactionResult.Entity is not null) { - responseEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser) + var errorEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser) .WithColour(ColorsList.Red).Build(); - } else { - var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken); - if (!userResult.IsDefined(out var user)) - return Result.FromError(userResult); - var builder = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason)); - if (duration is not null) - builder.Append( - string.Format( - Messages.DescriptionActionExpiresAt, - Markdown.Timestamp(DateTimeOffset.UtcNow.Add(duration.Value)))); - var description = builder.ToString(); - - var dmChannelResult = await _userApi.CreateDMAsync(target.ID, CancellationToken); - if (dmChannelResult.IsDefined(out var dmChannel)) { - var guildResult = await _guildApi.GetGuildAsync(guildId.Value, ct: CancellationToken); - if (!guildResult.IsDefined(out var guild)) - return Result.FromError(guildResult); - - var dmEmbed = new EmbedBuilder().WithGuildTitle(guild) - .WithTitle(Messages.YouWereBanned) - .WithDescription(description) - .WithActionFooter(user) - .WithCurrentTimestamp() - .WithColour(ColorsList.Red) - .Build(); - - if (!dmEmbed.IsDefined(out var dmBuilt)) - return Result.FromError(dmEmbed); - await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: CancellationToken); - } - - var banResult = await _guildApi.CreateGuildBanAsync( - guildId.Value, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(), - ct: CancellationToken); - if (!banResult.IsSuccess) - return Result.FromError(banResult.Error); - var memberData = data.GetMemberData(target.ID); - memberData.BannedUntil - = duration is not null ? DateTimeOffset.UtcNow.Add(duration.Value) : DateTimeOffset.MaxValue; - memberData.Roles.Clear(); - - responseEmbed = new EmbedBuilder().WithSmallTitle( - string.Format(Messages.UserBanned, target.GetTag()), target) - .WithColour(ColorsList.Green).Build(); - - if ((!GuildSettings.PublicFeedbackChannel.Get(cfg).Empty() - && GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value) - || (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty() - && GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)) { - var logEmbed = new EmbedBuilder().WithSmallTitle( - string.Format(Messages.UserBanned, target.GetTag()), target) - .WithDescription(description) - .WithActionFooter(user) - .WithCurrentTimestamp() - .WithColour(ColorsList.Red) - .Build(); - - if (!logEmbed.IsDefined(out var logBuilt)) - return Result.FromError(logEmbed); - - var builtArray = new[] { logBuilt }; - // Not awaiting to reduce response time - if (GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value) - _ = _channelApi.CreateMessageAsync( - GuildSettings.PublicFeedbackChannel.Get(cfg), embeds: builtArray, - ct: CancellationToken); - if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != GuildSettings.PublicFeedbackChannel.Get(cfg) - && GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value) - _ = _channelApi.CreateMessageAsync( - GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: builtArray, - ct: CancellationToken); - } + return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, CancellationToken); } - if (!responseEmbed.IsDefined(out var built)) - return Result.FromError(responseEmbed); + var builder = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason)); + if (duration is not null) + builder.Append( + string.Format( + Messages.DescriptionActionExpiresAt, + Markdown.Timestamp(DateTimeOffset.UtcNow.Add(duration.Value)))); + var title = string.Format(Messages.UserBanned, target.GetTag()); + var description = builder.ToString(); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + var dmChannelResult = await _userApi.CreateDMAsync(target.ID, CancellationToken); + if (dmChannelResult.IsDefined(out var dmChannel)) { + var dmEmbed = new EmbedBuilder().WithGuildTitle(guild) + .WithTitle(Messages.YouWereBanned) + .WithDescription(description) + .WithActionFooter(user) + .WithCurrentTimestamp() + .WithColour(ColorsList.Red) + .Build(); + + if (!dmEmbed.IsDefined(out var dmBuilt)) + return Result.FromError(dmEmbed); + await _channelApi.CreateMessageAsync(dmChannel.ID, embeds: new[] { dmBuilt }, ct: CancellationToken); + } + + var banResult = await _guildApi.CreateGuildBanAsync( + guild.ID, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(), + ct: CancellationToken); + if (!banResult.IsSuccess) + return Result.FromError(banResult.Error); + var memberData = data.GetMemberData(target.ID); + memberData.BannedUntil + = duration is not null ? DateTimeOffset.UtcNow.Add(duration.Value) : DateTimeOffset.MaxValue; + memberData.Roles.Clear(); + + var embed = new EmbedBuilder().WithSmallTitle( + title, target) + .WithColour(ColorsList.Green).Build(); + + _utility.LogActionAsync(cfg, channelId, title, target, description, user, CancellationToken); + + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } /// @@ -196,7 +169,7 @@ public class BanCommandGroup : CommandGroup { /// A feedback sending result which may or may not have succeeded. A successful result does not mean that the user /// was unbanned and vice-versa. /// - /// + /// /// [Command("unban")] [DiscordDefaultMemberPermissions(DiscordPermission.BanMembers)] @@ -206,79 +179,53 @@ public class BanCommandGroup : CommandGroup { [RequireBotDiscordPermissions(DiscordPermission.BanMembers)] [Description("Unban user")] [UsedImplicitly] - public async Task UnbanUserAsync( + public async Task ExecuteUnban( [Description("User to unban")] IUser target, [Description("Unban reason")] string reason) { if (!_context.TryGetContextIDs(out var guildId, out var channelId, out var userId)) return Result.FromError( new ArgumentNullError(nameof(_context), "Unable to retrieve necessary IDs from command context")); - // The current user's avatar is used when sending error messages var currentUserResult = await _userApi.GetCurrentUserAsync(CancellationToken); if (!currentUserResult.IsDefined(out var currentUser)) return Result.FromError(currentUserResult); - - var cfg = await _dataService.GetSettings(guildId.Value, CancellationToken); - Messages.Culture = GuildSettings.Language.Get(cfg); - - var existingBanResult = await _guildApi.GetGuildBanAsync(guildId.Value, target.ID, CancellationToken); - if (!existingBanResult.IsDefined()) { - var embed = new EmbedBuilder().WithSmallTitle(Messages.UserNotBanned, currentUser) - .WithColour(ColorsList.Red).Build(); - - if (!embed.IsDefined(out var alreadyBuilt)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken); - } - // Needed to get the tag and avatar var userResult = await _userApi.GetUserAsync(userId.Value, CancellationToken); if (!userResult.IsDefined(out var user)) return Result.FromError(userResult); + return await UnbanUserAsync(target, reason, guildId.Value, channelId.Value, user, currentUser); + } + + private async Task UnbanUserAsync( + IUser target, string reason, Snowflake guildId, Snowflake channelId, IUser user, IUser currentUser) { + var cfg = await _dataService.GetSettings(guildId, CancellationToken); + Messages.Culture = GuildSettings.Language.Get(cfg); + + var existingBanResult = await _guildApi.GetGuildBanAsync(guildId, target.ID, CancellationToken); + if (!existingBanResult.IsDefined()) { + var errorEmbed = new EmbedBuilder().WithSmallTitle(Messages.UserNotBanned, currentUser) + .WithColour(ColorsList.Red).Build(); + + return await _feedbackService.SendContextualEmbedResultAsync(errorEmbed, CancellationToken); + } + var unbanResult = await _guildApi.RemoveGuildBanAsync( - guildId.Value, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(), + guildId, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(), ct: CancellationToken); if (!unbanResult.IsSuccess) return Result.FromError(unbanResult.Error); - var responseEmbed = new EmbedBuilder().WithSmallTitle( + var embed = new EmbedBuilder().WithSmallTitle( string.Format(Messages.UserUnbanned, target.GetTag()), target) .WithColour(ColorsList.Green).Build(); - if ((!GuildSettings.PublicFeedbackChannel.Get(cfg).Empty() - && GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value) - || (!GuildSettings.PrivateFeedbackChannel.Get(cfg).Empty() - && GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value)) { - var logEmbed = new EmbedBuilder().WithSmallTitle( - string.Format(Messages.UserUnbanned, target.GetTag()), target) - .WithDescription(string.Format(Messages.DescriptionActionReason, reason)) - .WithActionFooter(user) - .WithCurrentTimestamp() - .WithColour(ColorsList.Green) - .Build(); + var title = string.Format(Messages.UserUnbanned, target.GetTag()); + var description = string.Format(Messages.DescriptionActionReason, reason); + var logResult = _utility.LogActionAsync(cfg, channelId, title, target, description, user, CancellationToken); + if (!logResult.IsSuccess) + return Result.FromError(logResult.Error); - if (!logEmbed.IsDefined(out var logBuilt)) - return Result.FromError(logEmbed); - - var builtArray = new[] { logBuilt }; - - // Not awaiting to reduce response time - if (GuildSettings.PublicFeedbackChannel.Get(cfg) != channelId.Value) - _ = _channelApi.CreateMessageAsync( - GuildSettings.PublicFeedbackChannel.Get(cfg), embeds: builtArray, - ct: CancellationToken); - if (GuildSettings.PrivateFeedbackChannel.Get(cfg) != GuildSettings.PublicFeedbackChannel.Get(cfg) - && GuildSettings.PrivateFeedbackChannel.Get(cfg) != channelId.Value) - _ = _channelApi.CreateMessageAsync( - GuildSettings.PrivateFeedbackChannel.Get(cfg), embeds: builtArray, - ct: CancellationToken); - } - - if (!responseEmbed.IsDefined(out var built)) - return Result.FromError(responseEmbed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Commands/ClearCommandGroup.cs b/src/Commands/ClearCommandGroup.cs index ede4d0b..306ab86 100644 --- a/src/Commands/ClearCommandGroup.cs +++ b/src/Commands/ClearCommandGroup.cs @@ -117,8 +117,7 @@ public class ClearCommandGroup : CommandGroup { var embed = new EmbedBuilder().WithSmallTitle(title, currentUser) .WithColour(ColorsList.Green).Build(); - if (!embed.IsDefined(out var built)) return Result.FromError(embed); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Commands/KickCommandGroup.cs b/src/Commands/KickCommandGroup.cs index 5809677..56154bd 100644 --- a/src/Commands/KickCommandGroup.cs +++ b/src/Commands/KickCommandGroup.cs @@ -83,10 +83,7 @@ public class KickCommandGroup : CommandGroup { var embed = new EmbedBuilder().WithSmallTitle(Messages.UserNotFoundShort, currentUser) .WithColour(ColorsList.Red).Build(); - if (!embed.IsDefined(out var alreadyBuilt)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } var interactionResult @@ -162,9 +159,6 @@ public class KickCommandGroup : CommandGroup { } } - if (!responseEmbed.IsDefined(out var built)) - return Result.FromError(responseEmbed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken); } } diff --git a/src/Commands/MuteCommandGroup.cs b/src/Commands/MuteCommandGroup.cs index 4338d09..eff7029 100644 --- a/src/Commands/MuteCommandGroup.cs +++ b/src/Commands/MuteCommandGroup.cs @@ -84,10 +84,7 @@ public class MuteCommandGroup : CommandGroup { var embed = new EmbedBuilder().WithSmallTitle(Messages.UserNotFoundShort, currentUser) .WithColour(ColorsList.Red).Build(); - if (!embed.IsDefined(out var alreadyBuilt)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } var interactionResult @@ -154,10 +151,7 @@ public class MuteCommandGroup : CommandGroup { } } - if (!responseEmbed.IsDefined(out var built)) - return Result.FromError(responseEmbed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken); } /// @@ -202,10 +196,7 @@ public class MuteCommandGroup : CommandGroup { var embed = new EmbedBuilder().WithSmallTitle(Messages.UserNotFoundShort, currentUser) .WithColour(ColorsList.Red).Build(); - if (!embed.IsDefined(out var alreadyBuilt)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(alreadyBuilt, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } var interactionResult @@ -258,9 +249,6 @@ public class MuteCommandGroup : CommandGroup { ct: CancellationToken); } - if (!responseEmbed.IsDefined(out var built)) - return Result.FromError(responseEmbed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(responseEmbed, CancellationToken); } } diff --git a/src/Commands/PingCommandGroup.cs b/src/Commands/PingCommandGroup.cs index ad05451..5819336 100644 --- a/src/Commands/PingCommandGroup.cs +++ b/src/Commands/PingCommandGroup.cs @@ -77,8 +77,7 @@ public class PingCommandGroup : CommandGroup { .WithColour(latency < 250 ? ColorsList.Green : latency < 500 ? ColorsList.Yellow : ColorsList.Red) .WithCurrentTimestamp() .Build(); - if (!embed.IsDefined(out var built)) return Result.FromError(embed); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Commands/RemindCommandGroup.cs b/src/Commands/RemindCommandGroup.cs index 28fc0ed..954e84a 100644 --- a/src/Commands/RemindCommandGroup.cs +++ b/src/Commands/RemindCommandGroup.cs @@ -71,9 +71,6 @@ public class RemindCommandGroup : CommandGroup { .WithColour(ColorsList.Green) .Build(); - if (!embed.IsDefined(out var built)) - return Result.FromError(embed); - - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Commands/SettingsCommandGroup.cs b/src/Commands/SettingsCommandGroup.cs index 5c4a505..d2d28be 100644 --- a/src/Commands/SettingsCommandGroup.cs +++ b/src/Commands/SettingsCommandGroup.cs @@ -90,9 +90,8 @@ public class SettingsCommandGroup : CommandGroup { .WithDescription(builder.ToString()) .WithColour(ColorsList.Default) .Build(); - if (!embed.IsDefined(out var built)) return Result.FromError(embed); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } /// @@ -132,9 +131,8 @@ public class SettingsCommandGroup : CommandGroup { .WithDescription(setResult.Error.Message) .WithColour(ColorsList.Red) .Build(); - if (!failedEmbed.IsDefined(out var failedBuilt)) return Result.FromError(failedEmbed); - return (Result)await _feedbackService.SendContextualEmbedAsync(failedBuilt, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(failedEmbed, CancellationToken); } var builder = new StringBuilder(); @@ -147,8 +145,7 @@ public class SettingsCommandGroup : CommandGroup { .WithDescription(builder.ToString()) .WithColour(ColorsList.Green) .Build(); - if (!embed.IsDefined(out var built)) return Result.FromError(embed); - return (Result)await _feedbackService.SendContextualEmbedAsync(built, ct: CancellationToken); + return await _feedbackService.SendContextualEmbedResultAsync(embed, CancellationToken); } } diff --git a/src/Extensions.cs b/src/Extensions.cs index 18a3c06..a9e0d48 100644 --- a/src/Extensions.cs +++ b/src/Extensions.cs @@ -7,9 +7,11 @@ using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Objects; using Remora.Discord.Commands.Contexts; using Remora.Discord.Commands.Extensions; +using Remora.Discord.Commands.Feedback.Services; using Remora.Discord.Extensions.Embeds; using Remora.Discord.Extensions.Formatting; using Remora.Rest.Core; +using Remora.Results; namespace Boyfriend; @@ -51,10 +53,9 @@ public static class Extensions { /// The builder to add the small title to. /// The text of the small title. /// The user whose avatar to use in the small title. - /// The URL that will be opened if a user clicks on the small title. /// The builder with the added small title in the author field. public static EmbedBuilder WithSmallTitle( - this EmbedBuilder builder, string text, IUser? avatarSource = null, string? url = default) { + this EmbedBuilder builder, string text, IUser? avatarSource = null) { Uri? avatarUrl = null; if (avatarSource is not null) { var avatarUrlResult = CDN.GetUserAvatarUrl(avatarSource, imageSize: 256); @@ -64,7 +65,7 @@ public static class Extensions { : CDN.GetDefaultUserAvatarUrl(avatarSource, imageSize: 256).Entity; } - builder.Author = new EmbedAuthorBuilder(text, url, avatarUrl?.AbsoluteUri); + builder.Author = new EmbedAuthorBuilder(text, iconUrl: avatarUrl?.AbsoluteUri); return builder; } @@ -191,7 +192,35 @@ public static class Extensions { && context.TryGetUserID(out userId); } + /// + /// Checks whether this Snowflake has any value set. + /// + /// The Snowflake to check. + /// true if the Snowflake has no value set or it's set to 0, false otherwise. public static bool Empty(this Snowflake snowflake) { return snowflake.Value is 0; } + + /// + /// Checks whether this snowflake is empty (see ) or it's equal to + /// + /// + /// The Snowflake to check for emptiness + /// The Snowflake to check for equality with . + /// + /// true if is empty or is equal to , false + /// otherwise. + /// + /// + public static bool EmptyOrEqualTo(this Snowflake snowflake, Snowflake anotherSnowflake) { + return snowflake.Empty() || snowflake == anotherSnowflake; + } + + public static async Task SendContextualEmbedResultAsync( + this FeedbackService feedback, Result embedResult, CancellationToken ct = default) { + if (!embedResult.IsDefined(out var embed)) + return Result.FromError(embedResult); + + return (Result)await feedback.SendContextualEmbedAsync(embed, ct: ct); + } } diff --git a/src/Services/UtilityService.cs b/src/Services/UtilityService.cs index 3b6a2bf..b1ae347 100644 --- a/src/Services/UtilityService.cs +++ b/src/Services/UtilityService.cs @@ -4,6 +4,7 @@ using Boyfriend.Data; using Microsoft.Extensions.Hosting; using Remora.Discord.API.Abstractions.Objects; using Remora.Discord.API.Abstractions.Rest; +using Remora.Discord.Extensions.Embeds; using Remora.Discord.Extensions.Formatting; using Remora.Rest.Core; using Remora.Results; @@ -15,15 +16,18 @@ namespace Boyfriend.Services; /// of some Discord APIs. /// public class UtilityService : IHostedService { + private readonly IDiscordRestChannelAPI _channelApi; private readonly IDiscordRestGuildScheduledEventAPI _eventApi; private readonly IDiscordRestGuildAPI _guildApi; private readonly IDiscordRestUserAPI _userApi; public UtilityService( - IDiscordRestGuildAPI guildApi, IDiscordRestUserAPI userApi, IDiscordRestGuildScheduledEventAPI eventApi) { + IDiscordRestChannelAPI channelApi, IDiscordRestGuildScheduledEventAPI eventApi, IDiscordRestGuildAPI guildApi, + IDiscordRestUserAPI userApi) { + _channelApi = channelApi; + _eventApi = eventApi; _guildApi = guildApi; _userApi = userApi; - _eventApi = eventApi; } public Task StartAsync(CancellationToken ct) { @@ -132,4 +136,51 @@ public class UtilityService : IHostedService { .Aggregate(builder, (current, user) => current.Append($"{Mention.User(user.User)} ")); return builder.ToString(); } + + /// + /// Logs an action in the and + /// . + /// + /// The guild configuration. + /// The ID of the channel where the action was executed. + /// The title for the embed. + /// The user whose avatar will be displayed next to the of the embed. + /// The description of the embed. + /// The user who performed the action. + /// The cancellation token for this operation. + /// + public Result LogActionAsync( + JsonNode cfg, Snowflake channelId, string title, IUser avatar, string description, + IUser user, CancellationToken ct = default) { + var publicChannel = GuildSettings.PublicFeedbackChannel.Get(cfg); + var privateChannel = GuildSettings.PrivateFeedbackChannel.Get(cfg); + if (GuildSettings.PublicFeedbackChannel.Get(cfg).EmptyOrEqualTo(channelId) + && GuildSettings.PrivateFeedbackChannel.Get(cfg).EmptyOrEqualTo(channelId)) + return Result.FromSuccess(); + + var logEmbed = new EmbedBuilder().WithSmallTitle(title, avatar) + .WithDescription(description) + .WithActionFooter(user) + .WithCurrentTimestamp() + .WithColour(ColorsList.Green) + .Build(); + + if (!logEmbed.IsDefined(out var logBuilt)) + return Result.FromError(logEmbed); + + var builtArray = new[] { logBuilt }; + + // Not awaiting to reduce response time + if (publicChannel != channelId.Value) + _ = _channelApi.CreateMessageAsync( + publicChannel, embeds: builtArray, + ct: ct); + if (privateChannel != publicChannel + && privateChannel != channelId.Value) + _ = _channelApi.CreateMessageAsync( + privateChannel, embeds: builtArray, + ct: ct); + + return Result.FromSuccess(); + } }