diff --git a/Attributes/HiddenAttribute.cs b/Attributes/HiddenAttribute.cs new file mode 100644 index 00000000..0ba5930e --- /dev/null +++ b/Attributes/HiddenAttribute.cs @@ -0,0 +1,5 @@ +namespace Cliptok.Attributes +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class HiddenAttribute : Attribute; +} \ No newline at end of file diff --git a/Commands/BanCmds.cs b/Commands/BanCmds.cs index fa4f946d..267b52d9 100644 --- a/Commands/BanCmds.cs +++ b/Commands/BanCmds.cs @@ -177,57 +177,6 @@ public async Task BanInfoSlashCommand( await ctx.RespondAsync(embed: await BanHelpers.BanStatusEmbed(targetUser, ctx.Guild), ephemeral: !isPublic); } - [Command("massbantextcmd")] - [TextAlias("massban", "bigbonk")] - [Description("Ban multiple users from the server at once.")] - [AllowedProcessors(typeof(TextCommandProcessor))] - [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] - public async Task MassBanCmd(TextCommandContext ctx, [Description("The list of users to ban, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) - { - List inputString = input.Replace("\n", " ").Replace("\r", "").Split(' ').ToList(); - List users = new(); - string reason = ""; - foreach (var word in inputString) - { - if (ulong.TryParse(word, out var id)) - users.Add(id); - else - reason += $"{word} "; - } - reason = reason.Trim(); - - if (users.Count == 1 || users.Count == 0) - { - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a massban with a single user. Please use `!ban`."); - return; - } - - List> taskList = new(); - int successes = 0; - - await ctx.RespondAsync("Processing, please wait."); - var loading = await ctx.GetResponseAsync(); - - foreach (ulong user in users) - { - if (string.IsNullOrWhiteSpace(reason)) - taskList.Add(BanSilently(ctx.Guild, user)); - else - taskList.Add(BanSilently(ctx.Guild, user, $"Mass ban: {reason}")); - } - - var tasks = await Task.WhenAll(taskList); - - foreach (var task in taskList) - { - if (task.Result) - successes += 1; - } - - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Banned} **{successes}**/{users.Count} users were banned successfully."); - await loading.DeleteAsync(); - } - [Command("bantextcmd")] [TextAlias("ban", "tempban", "bonk", "isekaitruck")] [Description("Bans a user that you have permission to ban, deleting all their messages in the process. See also: bankeep.")] diff --git a/Commands/DehoistCmds.cs b/Commands/DehoistCmds.cs index 157075f2..22cb896b 100644 --- a/Commands/DehoistCmds.cs +++ b/Commands/DehoistCmds.cs @@ -135,82 +135,5 @@ public async Task PermadehoistStatusSlashCmd(CommandContext ctx, [Parameter("mem await ctx.RespondAsync($"{Program.cfgjson.Emoji.Off} {discordUser.Mention} is not permadehoisted.", mentions: false); } } - - [Command("massdehoisttextcmd")] - [TextAlias("massdehoist")] - [Description("Dehoist everyone on the server who has a bad name. This may take a while and can exhaust rate limits.")] - [AllowedProcessors(typeof(TextCommandProcessor))] - [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] - public async Task MassDehoist(TextCommandContext ctx) - { - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Loading} Working on it. This will take a while."); - var msg = await ctx.GetResponseAsync(); - - var (totalMembers, failedMembers) = await DehoistHelpers.MassDehoistAsync(ctx.Guild, ctx.User); - - _ = msg.DeleteAsync(); - await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().WithContent($"{Program.cfgjson.Emoji.Success} Successfully dehoisted {totalMembers - failedMembers} of {totalMembers} member(s)! (Check Audit Log for details)").WithReply(ctx.Message.Id, true, false)); - } - - [Command("massundehoisttextcmd")] - [TextAlias("massundehoist")] - [Description("Remove the dehoist for users attached via a txt file.")] - [AllowedProcessors(typeof(TextCommandProcessor))] - [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] - public async Task MassUndhoist(TextCommandContext ctx) - { - int failedCount = 0; - - if (ctx.Message.Attachments.Count == 0) - { - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Please upload an attachment as well."); - } - else - { - string strList; - using (HttpClient client = new()) - { - strList = await client.GetStringAsync(ctx.Message.Attachments[0].Url); - } - - var list = strList.Split(' '); - - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Loading} Working on it. This will take a while."); - var msg = await ctx.GetResponseAsync(); - - foreach (string strID in list) - { - ulong id = Convert.ToUInt64(strID); - DiscordMember member = default; - try - { - member = await ctx.Guild.GetMemberAsync(id); - } - catch (DSharpPlus.Exceptions.NotFoundException) - { - failedCount++; - continue; - } - - if (member.DisplayName[0] == DehoistHelpers.dehoistCharacter && !member.MemberFlags.Value.HasFlag(DiscordMemberFlags.AutomodQuarantinedUsername) && !member.MemberFlags.Value.HasFlag(DiscordMemberFlags.AutomodQuarantinedGuildTag)) - { - var newNickname = member.Nickname[1..]; - await member.ModifyAsync(a => - { - a.Nickname = newNickname; - a.AuditLogReason = $"[Mass undehoist by {DiscordHelpers.UniqueUsername(ctx.User)}]"; - } - ); - } - else - { - failedCount++; - } - } - - await msg.ModifyAsync($"{Program.cfgjson.Emoji.Success} Successfully undehoisted {list.Length - failedCount} of {list.Length} member(s)! (Check Audit Log for details)"); - - } - } } } \ No newline at end of file diff --git a/Commands/GlobalCmds.cs b/Commands/GlobalCmds.cs index 234c6433..5f7ad2e4 100644 --- a/Commands/GlobalCmds.cs +++ b/Commands/GlobalCmds.cs @@ -23,7 +23,8 @@ public static async Task Help(CommandContext ctx, [Description("Command to provi IEnumerable cmds = ctx.Extension.Commands.Values.Where(cmd => cmd.Attributes.Any(attr => attr is AllowedProcessorsAttribute apAttr - && apAttr.Processors.Contains(typeof(TextCommandProcessor)))); + && apAttr.Processors.Contains(typeof(TextCommandProcessor))) + && !cmd.Attributes.Any(attr => attr is HiddenAttribute && command == "")); if (commandSplit.Length != 0 && commandSplit[0] != "") { diff --git a/Commands/KickCmds.cs b/Commands/KickCmds.cs index 23ce2fe8..48068295 100644 --- a/Commands/KickCmds.cs +++ b/Commands/KickCmds.cs @@ -35,7 +35,7 @@ public class KickCmds { if (DiscordHelpers.AllowedToMod(await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id), member)) { - await KickAndLogAsync(member, reason, ctx.Member); + await KickHelpers.KickAndLogAsync(member, reason, ctx.Member); await ctx.Channel.SendMessageAsync($"{Program.cfgjson.Emoji.Ejected} {target.Mention} has been kicked: **{reason}**"); if (ctx is SlashCommandContext) await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Done!", ephemeral: true); @@ -53,95 +53,5 @@ public class KickCmds return; } } - - [Command("masskicktextcmd")] - [TextAlias("masskick")] - [Description("Kick multiple users from the server at once.")] - [AllowedProcessors(typeof(TextCommandProcessor))] - [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] - public async Task MassKickCmd(TextCommandContext ctx, [Description("The list of users to kick, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) - { - - List inputString = input.Replace("\n", " ").Replace("\r", "").Split(' ').ToList(); - List users = new(); - string reason = ""; - foreach (var word in inputString) - { - if (ulong.TryParse(word, out var id)) - users.Add(id); - else - reason += $"{word} "; - } - reason = reason.Trim(); - - if (users.Count == 1 || users.Count == 0) - { - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a masskick with a single user. Please use `!kick`."); - return; - } - - List> taskList = new(); - int successes = 0; - - await ctx.RespondAsync("Processing, please wait."); - var loading = await ctx.GetResponseAsync(); - - foreach (ulong user in users) - { - try - { - var member = await ctx.Guild.GetMemberAsync(user); - if (member is not null) - { - - taskList.Add(SafeKickAndLogAsync(member, $"Mass kick{(string.IsNullOrWhiteSpace(reason) ? "" : $": {reason}")}", ctx.Member)); - } - } - catch - { - // not successful, move on - } - } - - var tasks = await Task.WhenAll(taskList); - - foreach (var task in taskList) - { - if (task.Result) - successes += 1; - } - - await ctx.RespondAsync($"{Program.cfgjson.Emoji.Deleted} **{successes}**/{users.Count} users were kicked successfully."); - await loading.DeleteAsync(); - } - - - public async static Task KickAndLogAsync(DiscordMember target, string reason, DiscordMember moderator) - { - await target.RemoveAsync(reason); - await LogChannelHelper.LogMessageAsync("mod", - new DiscordMessageBuilder() - .WithContent($"{Program.cfgjson.Emoji.Ejected} {target.Mention} was kicked by {moderator.Mention}.\nReason: **{reason}**") - .WithAllowedMentions(Mentions.None) - ); - } - - public async static Task SafeKickAndLogAsync(DiscordMember target, string reason, DiscordMember moderator) - { - try - { - await target.RemoveAsync(reason); - await LogChannelHelper.LogMessageAsync("mod", - new DiscordMessageBuilder() - .WithContent($"{Program.cfgjson.Emoji.Ejected} {target.Mention} was kicked by {moderator.Mention}.\nReason: **{reason}**") - .WithAllowedMentions(Mentions.None) - ); - return true; - } - catch - { - return false; - } - } } } \ No newline at end of file diff --git a/Commands/MassCmds.cs b/Commands/MassCmds.cs new file mode 100644 index 00000000..41bfe2b8 --- /dev/null +++ b/Commands/MassCmds.cs @@ -0,0 +1,389 @@ +namespace Cliptok.Commands +{ + [Command("mass")] + [Description("Commands for performing mass actions.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.TrialModerator), RequirePermissions(DiscordPermission.ModerateMembers)] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + public static class MassCmds + { + [Command("ban")] + [TextAlias("bigbonk")] + [Description("Ban multiple users from the server at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + public static async Task MassBanCmd(CommandContext ctx, [Parameter("input"), Description("The list of users to ban, separated by spaces, optionally followed by a reason."), RemainingText] string input) + { + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + + var (users, reason) = ParseInput(input); + + if (users.Count == 1 || users.Count == 0) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a massban with a single user. Please use `!ban`."); + return; + } + + List> taskList = new(); + int successes = 0; + + DiscordMessage loading = default; + if (ctx is TextCommandContext) + { + await ctx.RespondAsync("Processing, please wait."); + loading = await ctx.GetResponseAsync(); + } + + foreach (ulong user in users) + { + if (string.IsNullOrWhiteSpace(reason)) + taskList.Add(BanHelpers.BanSilently(ctx.Guild, user, ctx.User.Id)); + else + taskList.Add(BanHelpers.BanSilently(ctx.Guild, user, ctx.User.Id, $"Mass ban: {reason}")); + } + + var tasks = await Task.WhenAll(taskList); + + foreach (var task in taskList) + { + if (task.Result) + successes += 1; + } + + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Banned} **{successes}**/{users.Count} users were banned successfully."); + if (ctx is TextCommandContext) + await loading.DeleteAsync(); + } + + [Command("kick")] + [Description("Kick multiple users from the server at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + public static async Task MassKickCmd(CommandContext ctx, [Parameter("input"), Description("The list of users to kick, separated by spaces, optionally followed by a reason."), RemainingText] string input) + { + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + + var (users, reason) = ParseInput(input); + + if (users.Count == 1 || users.Count == 0) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a masskick with a single user. Please use `!kick`."); + return; + } + + List> taskList = new(); + int successes = 0; + + DiscordMessage loading = default; + if (ctx is TextCommandContext) + { + await ctx.RespondAsync("Processing, please wait."); + loading = await ctx.GetResponseAsync(); + } + + foreach (ulong user in users) + { + try + { + var member = await ctx.Guild.GetMemberAsync(user); + if (member is not null) + { + + taskList.Add(KickHelpers.SafeKickAndLogAsync(member, $"Mass kick{(string.IsNullOrWhiteSpace(reason) ? "" : $": {reason}")}", ctx.Member)); + } + } + catch + { + // not successful, move on + } + } + + var tasks = await Task.WhenAll(taskList); + + foreach (var task in taskList) + { + if (task.Result) + successes += 1; + } + + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Deleted} **{successes}**/{users.Count} users were kicked successfully."); + if (ctx is TextCommandContext) + await loading.DeleteAsync(); + } + + [Command("mute")] + [Description("Mute multiple users at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + public static async Task MassMuteCmd(CommandContext ctx, [Parameter("input"), Description("The list of users to mute, separated by spaces, optionally followed by a reason."), RemainingText] string input) + { + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + + var (users, reason) = ParseInput(input); + + if (users.Count == 1 || users.Count == 0) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a massmute with a single user. Please use `!mute`."); + return; + } + + List> taskList = new(); + int successes = 0; + + DiscordMessage loading = default; + if (ctx is TextCommandContext) + { + await ctx.RespondAsync("Processing, please wait."); + loading = await ctx.GetResponseAsync(); + } + + foreach (ulong user in users) + { + if (string.IsNullOrWhiteSpace(reason)) + taskList.Add(MuteHelpers.MuteSilently(ctx.Guild, user, ctx.User.Id)); + else + taskList.Add(MuteHelpers.MuteSilently(ctx.Guild, user, ctx.User.Id, $"Mass mute: {reason}")); + } + + var tasks = await Task.WhenAll(taskList); + + foreach (var task in taskList) + { + if (task.Result) + successes += 1; + } + + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Muted} **{successes}**/{users.Count} users were muted successfully."); + if (ctx is TextCommandContext) + await loading.DeleteAsync(); + } + + [Command("unmute")] + [Description("Unmute multiple users at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + public static async Task MassUnmuteCmd(CommandContext ctx, [Parameter("input"), Description("The list of users to unmute, separated by spaces, optionally followed by a reason."), RemainingText] string input) + { + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + + var (users, reason) = ParseInput(input); + + if (users.Count == 1 || users.Count == 0) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Not accepting a massunmute with a single user. Please use `!unmute`."); + return; + } + + List> taskList = new(); + int successes = 0; + + DiscordMessage loading = default; + if (ctx is TextCommandContext) + { + await ctx.RespondAsync("Processing, please wait."); + loading = await ctx.GetResponseAsync(); + } + + foreach (ulong user in users) + { + if (string.IsNullOrWhiteSpace(reason)) + taskList.Add(MuteHelpers.UnmuteSilently(ctx.Guild, user)); + else + taskList.Add(MuteHelpers.UnmuteSilently(ctx.Guild, user, $"Mass unmute: {reason}")); + } + + var tasks = await Task.WhenAll(taskList); + + foreach (var task in taskList) + { + if (task.Result) + successes += 1; + } + + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} **{successes}**/{users.Count} users were unmuted successfully."); + if (ctx is TextCommandContext) + await loading.DeleteAsync(); + } + + [Command("dehoist")] + [Description("Dehoist everyone on the server with a bad name. This may take a while and can exhaust rate limits.")] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + public static async Task MassDehoist(CommandContext ctx) + { + DiscordMessage msg = default; + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + else + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Loading} Working on it. This will take a while."); + msg = await ctx.GetResponseAsync(); + } + + var (totalMembers, failedMembers) = await DehoistHelpers.MassDehoistAsync(ctx.Guild, ctx.User); + + if (ctx is TextCommandContext) + _ = msg.DeleteAsync(); + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully dehoisted {totalMembers - failedMembers} of {totalMembers} member(s)! (Check Audit Log for details)"); + } + + [Command("undehoist")] + [Description("Remove the dehoist for users attached via a txt file.")] + [AllowedProcessors(typeof(SlashCommandProcessor), typeof(TextCommandProcessor))] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + public static async Task MassUndehoist(CommandContext ctx, [Parameter("file"), Description("A text file containing the list of users to undehoist.")] DiscordAttachment file = null) + { + DiscordAttachment attachment; + if (ctx is TextCommandContext tctx && tctx.Message.Attachments.Count > 0) + attachment = tctx.Message.Attachments[0]; + else + attachment = file; + + int failedCount = 0; + + if (attachment is null) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Please upload an attachment as well."); + } + else + { + string strList; + using (HttpClient client = new()) + { + strList = await client.GetStringAsync(attachment.Url); + } + + var list = strList.Split(' '); + + DiscordMessage msg = default; + if (ctx is SlashCommandContext) + await ctx.DeferResponseAsync(); + else + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Loading} Working on it. This will take a while."); + msg = await ctx.GetResponseAsync(); + } + + foreach (string strID in list) + { + ulong id = Convert.ToUInt64(strID); + DiscordMember member = default; + try + { + member = await ctx.Guild.GetMemberAsync(id); + } + catch (DSharpPlus.Exceptions.NotFoundException) + { + failedCount++; + continue; + } + + if (member.DisplayName[0] == DehoistHelpers.dehoistCharacter && !member.MemberFlags.Value.HasFlag(DiscordMemberFlags.AutomodQuarantinedUsername)) + { + var newNickname = member.Nickname[1..]; + await member.ModifyAsync(a => + { + a.Nickname = newNickname; + a.AuditLogReason = $"[Mass undehoist by {DiscordHelpers.UniqueUsername(ctx.User)}]"; + } + ); + } + else + { + failedCount++; + } + } + + if (ctx is TextCommandContext) + await msg.DeleteAsync(); + + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Success} Successfully undehoisted {list.Length - failedCount} of {list.Length} member(s)! (Check Audit Log for details)"); + + } + } + + private static (List users, string reason) ParseInput(string input) + { + List inputString = input.Replace("\n", " ").Replace("\r", "").Split(' ').ToList(); + List users = new(); + string reason = ""; + foreach (var word in inputString) + { + if (ulong.TryParse(word.Replace("<@", "").Replace(">", ""), out var id)) + users.Add(id); + else + reason += $"{word} "; + } + reason = reason.Trim(); + + return (users, reason); + } + } + + // An attempt at aliases... + public static class MassTextAliases + { + [Command("massban")] + [Description("Ban multiple users from the server at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(TextCommandProcessor))] + [Hidden] + public static async Task MassBanAliasCmd(TextCommandContext ctx, [Description("The list of users to ban, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) + { + await MassCmds.MassBanCmd(ctx, input); + } + + [Command("masskick")] + [Description("Kick multiple users from the server at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(TextCommandProcessor))] + [Hidden] + public static async Task MassKickAliasCmd(TextCommandContext ctx, [Description("The list of users to kick, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) + { + await MassCmds.MassKickCmd(ctx, input); + } + + [Command("massmute")] + [Description("Mute multiple users at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(TextCommandProcessor))] + [Hidden] + public static async Task MassMuteAliasCmd(CommandContext ctx, [Description("The list of users to mute, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) + { + await MassCmds.MassMuteCmd(ctx, input); + } + + [Command("massunmute")] + [Description("Unmute multiple users at once.")] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [AllowedProcessors(typeof(TextCommandProcessor))] + [Hidden] + public static async Task MassUnmuteAliasCmd(CommandContext ctx, [Description("The list of users to mute, separated by newlines or spaces, optionally followed by a reason."), RemainingText] string input) + { + await MassCmds.MassUnmuteCmd(ctx, input); + } + + [Command("massdehoist")] + [Description("Dehoist everyone on the server with a bad name. This may take a while and can exhaust rate limits.")] + [AllowedProcessors(typeof(TextCommandProcessor))] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [Hidden] + public static async Task MassDehoistAliasCmd(TextCommandContext ctx) + { + await MassCmds.MassDehoist(ctx); + } + + [Command("massundehoist")] + [Description("Remove the dehoist for users attached via a txt file.")] + [AllowedProcessors(typeof(TextCommandProcessor))] + [HomeServer, RequireHomeserverPerm(ServerPermLevel.Moderator)] + [Hidden] + public static async Task MassUndehoistAliasCmd(TextCommandContext ctx) + { + await MassCmds.MassUndehoist(ctx); + } + } +} \ No newline at end of file diff --git a/Events/MemberEvents.cs b/Events/MemberEvents.cs index 2ccd8beb..e65e6366 100644 --- a/Events/MemberEvents.cs +++ b/Events/MemberEvents.cs @@ -71,7 +71,7 @@ public static async Task GuildMemberAdded(DiscordClient client, GuildMemberAdded { if (avatars.Contains(e.Member.AvatarHash)) { - var _ = BanHelpers.BanSilently(e.Guild, e.Member.Id, "Secret sauce"); + var _ = BanHelpers.BanSilently(e.Guild, e.Member.Id, client.CurrentUser.Id, "Secret sauce"); await LogChannelHelper.LogMessageAsync("investigations", $"{cfgjson.Emoji.Banned} Raid-banned {e.Member.Mention} for matching avatar: {e.Member.AvatarUrl.Replace("1024", "128")}"); } } diff --git a/GlobalUsings.cs b/GlobalUsings.cs index e2a89d10..e4aa200d 100644 --- a/GlobalUsings.cs +++ b/GlobalUsings.cs @@ -1,4 +1,5 @@ -global using Cliptok.CommandChecks; +global using Cliptok.Attributes; +global using Cliptok.CommandChecks; global using Cliptok.Enums; global using Cliptok.Events; global using Cliptok.Helpers; diff --git a/Helpers/BanHelpers.cs b/Helpers/BanHelpers.cs index 1958602b..77170354 100644 --- a/Helpers/BanHelpers.cs +++ b/Helpers/BanHelpers.cs @@ -199,11 +199,23 @@ public async static Task UnbanUserAsync(DiscordGuild guild, DiscordUser ta return true; } - public static async Task BanSilently(DiscordGuild targetGuild, ulong targetUserId, string reason = "Mass ban") + public static async Task BanSilently(DiscordGuild targetGuild, ulong targetUserId, ulong moderatorId, string reason = "Mass ban") { try { await targetGuild.BanMemberAsync(targetUserId, TimeSpan.FromDays(7), reason); + + MemberPunishment newBan = new() + { + MemberId = targetUserId, + ModId = moderatorId, + ServerId = targetGuild.Id, + ExpireTime = null, + ActionTime = DateTime.Now, + Reason = reason + }; + + await Program.redis.HashSetAsync("bans", targetUserId, JsonConvert.SerializeObject(newBan)); // Remove user message tracking if (await Program.redis.SetContainsAsync("trackedUsers", targetUserId)) diff --git a/Helpers/KickHelpers.cs b/Helpers/KickHelpers.cs new file mode 100644 index 00000000..ded82ed0 --- /dev/null +++ b/Helpers/KickHelpers.cs @@ -0,0 +1,33 @@ +namespace Cliptok.Helpers +{ + public class KickHelpers + { + public async static Task KickAndLogAsync(DiscordMember target, string reason, DiscordMember moderator) + { + await target.RemoveAsync(reason); + await LogChannelHelper.LogMessageAsync("mod", + new DiscordMessageBuilder() + .WithContent($"{Program.cfgjson.Emoji.Ejected} {target.Mention} was kicked by {moderator.Mention}.\nReason: **{reason}**") + .WithAllowedMentions(Mentions.None) + ); + } + + public async static Task SafeKickAndLogAsync(DiscordMember target, string reason, DiscordMember moderator) + { + try + { + await target.RemoveAsync(reason); + await LogChannelHelper.LogMessageAsync("mod", + new DiscordMessageBuilder() + .WithContent($"{Program.cfgjson.Emoji.Ejected} {target.Mention} was kicked by {moderator.Mention}.\nReason: **{reason}**") + .WithAllowedMentions(Mentions.None) + ); + return true; + } + catch + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Helpers/MuteHelpers.cs b/Helpers/MuteHelpers.cs index d3793338..c61c5e92 100644 --- a/Helpers/MuteHelpers.cs +++ b/Helpers/MuteHelpers.cs @@ -318,6 +318,54 @@ public static (int MuteHours, int WarnsSinceThreshold) GetHoursToMuteFor(Diction return output; } + + public static async Task MuteSilently(DiscordGuild targetGuild, ulong targetUserId, ulong moderatorId, string reason = "Mass mute") + { + try + { + // this should pull from cache most of the time! + var mutedRole = await targetGuild.GetRoleAsync(Program.cfgjson.MutedRole); + var member = await targetGuild.GetMemberAsync(targetUserId); + await member.GrantRoleAsync(mutedRole, reason); + + MemberPunishment newMute = new() + { + MemberId = targetUserId, + ModId = moderatorId, + ServerId = targetGuild.Id, + ExpireTime = null, + ActionTime = DateTime.Now, + Reason = reason + }; + + await Program.redis.HashSetAsync("mutes", targetUserId, JsonConvert.SerializeObject(newMute)); + + return true; + } + catch + { + return false; + } + } + + public static async Task UnmuteSilently(DiscordGuild targetGuild, ulong targetUserId, string reason = "Mass unmute") + { + try + { + // this should pull from cache most of the time! + var mutedRole = await targetGuild.GetRoleAsync(Program.cfgjson.MutedRole); + var member = await targetGuild.GetMemberAsync(targetUserId); + await member.RevokeRoleAsync(mutedRole, reason); + + await Program.redis.HashDeleteAsync("mutes", targetUserId); + + return true; + } + catch + { + return false; + } + } public static async Task UnmuteUserAsync(DiscordUser targetUser, string reason = "", bool manual = true, DiscordUser modUser = default, bool isTqsUnmute = false) {