From 283af5740b73ab215504691cff07af846583ac82 Mon Sep 17 00:00:00 2001 From: Macintosh II Date: Thu, 21 Sep 2023 18:31:55 +0300 Subject: [PATCH] Add mute role support & fix /unmute totally didn't take 2 painful days Signed-off-by: Macintosh II --- src/Commands/MuteCommandGroup.cs | 133 +++++++++++++++++- src/Data/MemberData.cs | 3 + .../GuildMemberRolesUpdatedResponder.cs | 6 +- src/Services/Update/MemberUpdateService.cs | 13 ++ 4 files changed, 149 insertions(+), 6 deletions(-) diff --git a/src/Commands/MuteCommandGroup.cs b/src/Commands/MuteCommandGroup.cs index ce70d68..16f3d4c 100644 --- a/src/Commands/MuteCommandGroup.cs +++ b/src/Commands/MuteCommandGroup.cs @@ -101,11 +101,17 @@ public class MuteCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(embed, CancellationToken); } - return await MuteUserAsync( + if (GuildSettings.MuteRole.Get(data.Settings) != 0) + { + return await RoleMuteUserAsync( + target, reason, duration, guildId, data, channelId, user, currentUser, CancellationToken); + } + + return await TimeoutUserAsync( target, reason, duration, guildId, data, channelId, user, currentUser, CancellationToken); } - private async Task MuteUserAsync( + private async Task RoleMuteUserAsync( IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId, IUser user, IUser currentUser, CancellationToken ct = default) { @@ -125,10 +131,75 @@ public class MuteCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct); } + var until = DateTimeOffset.UtcNow.Add(duration); // >:) + var memberData = data.GetOrCreateMemberData(target.ID); + memberData.MutedUntil = DateTimeOffset.UtcNow.Add(duration); + var assignRoles = new List + { + GuildSettings.MuteRole.Get(data.Settings) + }; + var muteResult = await _guildApi.ModifyGuildMemberAsync( + guildId, target.ID, roles: assignRoles, + reason: $"({user.GetTag()}) {reason}".EncodeHeader(), ct: ct); + if (!muteResult.IsSuccess) + { + return Result.FromError(muteResult.Error); + } + + var title = string.Format(Messages.UserMuted, target.GetTag()); + var description = new StringBuilder().AppendLine(string.Format(Messages.DescriptionActionReason, reason)) + .Append( + string.Format( + Messages.DescriptionActionExpiresAt, Markdown.Timestamp(until))).ToString(); + + var logResult = _utility.LogActionAsync( + data.Settings, channelId, user, title, description, target, ColorsList.Red, ct: ct); + if (!logResult.IsSuccess) + { + return Result.FromError(logResult.Error); + } + + var embed = new EmbedBuilder().WithSmallTitle( + string.Format(Messages.UserMuted, target.GetTag()), target) + .WithColour(ColorsList.Green).Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct); + } + + private async Task TimeoutUserAsync( + IUser target, string reason, TimeSpan duration, Snowflake guildId, GuildData data, Snowflake channelId, + IUser user, IUser currentUser, CancellationToken ct = default) + { + if (duration.Days >= 28) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.BotCannotMuteTarget, currentUser) + .WithDescription(Messages.DurationRequiredForTimeOuts) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, CancellationToken); + } + + var interactionResult + = await _utility.CheckInteractionsAsync( + guildId, user.ID, target.ID, "Mute", ct); + if (!interactionResult.IsSuccess) + { + return Result.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct); + } + var until = DateTimeOffset.UtcNow.Add(duration); // >:) var muteResult = await _guildApi.ModifyGuildMemberAsync( guildId, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(), communicationDisabledUntil: until, ct: ct); + if (!muteResult.IsSuccess) { return Result.FromError(muteResult.Error); @@ -211,11 +282,63 @@ public class MuteCommandGroup : CommandGroup return await _feedback.SendContextualEmbedResultAsync(embed, CancellationToken); } - return await UnmuteUserAsync( + if (data.GetOrCreateMemberData(target.ID).MutedUntil is not null) + { + return await RemoveMuteRoleUserAsync( + target, reason, guildId, data, channelId, user, currentUser, CancellationToken); + } + + return await RemoveTimeoutUserAsync( target, reason, guildId, data, channelId, user, currentUser, CancellationToken); } - private async Task UnmuteUserAsync( + private async Task RemoveMuteRoleUserAsync( + IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user, + IUser currentUser, CancellationToken ct = default) + { + var interactionResult + = await _utility.CheckInteractionsAsync( + guildId, user.ID, target.ID, "Unmute", ct); + if (!interactionResult.IsSuccess) + { + return Result.FromError(interactionResult); + } + + if (interactionResult.Entity is not null) + { + var failedEmbed = new EmbedBuilder().WithSmallTitle(interactionResult.Entity, currentUser) + .WithColour(ColorsList.Red).Build(); + + return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct); + } + + var memberData = data.GetOrCreateMemberData(target.ID); + var unmuteResult = await _guildApi.ModifyGuildMemberAsync( + guildId, target.ID, roles: memberData.Roles.ConvertAll(r => r.ToSnowflake()), + reason: $"({user.GetTag()}) {reason}".EncodeHeader(), ct: ct); + memberData.MutedUntil = null; + if (!unmuteResult.IsSuccess) + { + return Result.FromError(unmuteResult.Error); + } + + var title = string.Format(Messages.UserUnmuted, target.GetTag()); + var description = string.Format(Messages.DescriptionActionReason, reason); + var logResult = _utility.LogActionAsync( + data.Settings, channelId, user, title, description, target, ColorsList.Green, ct: ct); + if (!logResult.IsSuccess) + { + return Result.FromError(logResult.Error); + } + + var embed = new EmbedBuilder().WithSmallTitle( + string.Format(Messages.UserUnmuted, target.GetTag()), target) + .WithColour(ColorsList.Green).Build(); + + return await _feedback.SendContextualEmbedResultAsync(embed, ct); + } + + private async Task RemoveTimeoutUserAsync( IUser target, string reason, Snowflake guildId, GuildData data, Snowflake channelId, IUser user, IUser currentUser, CancellationToken ct = default) { @@ -236,7 +359,7 @@ public class MuteCommandGroup : CommandGroup } var unmuteResult = await _guildApi.ModifyGuildMemberAsync( - guildId, target.ID, $"({user.GetTag()}) {reason}".EncodeHeader(), + guildId, target.ID, reason: $"({user.GetTag()}) {reason}".EncodeHeader(), communicationDisabledUntil: null, ct: ct); if (!unmuteResult.IsSuccess) { diff --git a/src/Data/MemberData.cs b/src/Data/MemberData.cs index 1ff2bc0..4de7f0a 100644 --- a/src/Data/MemberData.cs +++ b/src/Data/MemberData.cs @@ -1,3 +1,5 @@ +using Remora.Rest.Core; + namespace Boyfriend.Data; /// @@ -13,6 +15,7 @@ public sealed class MemberData public ulong Id { get; } public DateTimeOffset? BannedUntil { get; set; } + public DateTimeOffset? MutedUntil { get; set; } public List Roles { get; set; } = new(); public List Reminders { get; } = new(); } diff --git a/src/Responders/GuildMemberRolesUpdatedResponder.cs b/src/Responders/GuildMemberRolesUpdatedResponder.cs index dbd8b3a..eade781 100644 --- a/src/Responders/GuildMemberRolesUpdatedResponder.cs +++ b/src/Responders/GuildMemberRolesUpdatedResponder.cs @@ -23,7 +23,11 @@ public class GuildMemberUpdateResponder : IResponder public async Task RespondAsync(IGuildMemberUpdate gatewayEvent, CancellationToken ct = default) { var memberData = await _guildData.GetMemberData(gatewayEvent.GuildID, gatewayEvent.User.ID, ct); - memberData.Roles = gatewayEvent.Roles.ToList().ConvertAll(r => r.Value); + if (memberData.MutedUntil is null) + { + memberData.Roles = gatewayEvent.Roles.ToList().ConvertAll(r => r.Value); + } + return Result.FromSuccess(); } } diff --git a/src/Services/Update/MemberUpdateService.cs b/src/Services/Update/MemberUpdateService.cs index 631cc50..65a9808 100644 --- a/src/Services/Update/MemberUpdateService.cs +++ b/src/Services/Update/MemberUpdateService.cs @@ -91,6 +91,19 @@ public sealed partial class MemberUpdateService : BackgroundService return unbanResult; } + if (DateTimeOffset.UtcNow > data.MutedUntil) + { + var unmuteResult = await _guildApi.ModifyGuildMemberAsync( + guildId, id, roles: data.Roles.ConvertAll(r => r.ToSnowflake()), + reason: Messages.PunishmentExpired.EncodeHeader(), ct: ct); + if (unmuteResult.IsSuccess) + { + data.MutedUntil = null; + } + + return unmuteResult; + } + if (defaultRole.Value is not 0 && !data.Roles.Contains(defaultRole.Value)) { var addResult = await _guildApi.AddGuildMemberRoleAsync(