diff --git a/Commands/InteractionCommands/NicknameLockInteraction.cs b/Commands/InteractionCommands/NicknameLockInteraction.cs new file mode 100644 index 0000000..af83509 --- /dev/null +++ b/Commands/InteractionCommands/NicknameLockInteraction.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Cliptok.Commands.InteractionCommands +{ + public class NicknameLockInteraction : ApplicationCommandModule + { + [SlashCommandGroup("nicknamelock", "Prevent a member from changing their nickname.", defaultPermission: false)] + [SlashRequireHomeserverPerm(ServerPermLevel.TrialModerator), SlashCommandPermissions(DiscordPermissions.ManageNicknames)] + public class NicknameLockSlashCommands + { + [SlashCommand("enable", "Prevent a member from changing their nickname.")] + public async Task NicknameLockEnableSlashCmd(InteractionContext ctx, [Option("member", "The member to nickname lock.")] DiscordUser discordUser) + { + DiscordMember member = default; + + try + { + member = await ctx.Guild.GetMemberAsync(discordUser.Id); + } catch (Exception e) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Failed to find {discordUser.Mention} as a member! Are they in the server?\n```\n{e.Message}```", ephemeral: true); + return; + } + + var currentValue = await Program.db.HashGetAsync($"nicknamelock", discordUser.Id); + + if (currentValue.HasValue) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} {discordUser.Mention} is already nickname locked!", mentions: false); + } else + { + await Program.db.HashSetAsync("nicknamelock", discordUser.Id, member.DisplayName); + var msg = $"{Program.cfgjson.Emoji.On} Nickname locked {discordUser.Mention} as `{member.DisplayName}`!"; + await ctx.RespondAsync(msg, mentions: false); + await LogChannelHelper.LogMessageAsync("nicknames", msg); + } + } + + [SlashCommand("disable", "Allow a member to change their nickname again.")] + public async Task NicknameLockDisableSlashCmd(InteractionContext ctx, [Option("member", "The member to remove the nickname lock for.")] DiscordUser discordUser) + { + DiscordMember member = default; + + try + { + member = await ctx.Guild.GetMemberAsync(discordUser.Id); + } + catch (Exception e) + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} Failed to find {discordUser.Mention} as a member! Are they in the server?\n```\n{e.Message}```", ephemeral: true); + return; + } + + var currentValue = await Program.db.HashGetAsync($"nicknamelock", discordUser.Id); + + if (currentValue.HasValue) + { + await Program.db.HashDeleteAsync("nicknamelock", discordUser.Id); + var msg = $"{Program.cfgjson.Emoji.Off} Removed nickname lock for {discordUser.Mention}!"; + await ctx.RespondAsync(msg, mentions: false); + await LogChannelHelper.LogMessageAsync("nicknames", msg); + } + else + { + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Error} {discordUser.Mention} is not nickname locked!", mentions: false); + } + } + + [SlashCommand("status", "Check the status of nickname lock for a member.")] + public async Task NicknameLockStatusSlashCmd(InteractionContext ctx, [Option("member", "The member whose nickname lock status to check.")] DiscordUser discordUser) + { + if ((await Program.db.HashGetAsync("nicknamelock", discordUser.Id)).HasValue) + await ctx.RespondAsync($"{Program.cfgjson.Emoji.On} {discordUser.Mention} is nickname locked.", mentions: false); + else + await ctx.RespondAsync($"{Program.cfgjson.Emoji.Off} {discordUser.Mention} is not nickname locked.", mentions: false); + } + } + } + +} diff --git a/Events/MemberEvents.cs b/Events/MemberEvents.cs index 6c8c40d..6788c08 100644 --- a/Events/MemberEvents.cs +++ b/Events/MemberEvents.cs @@ -197,6 +197,25 @@ public static async Task GuildMemberUpdated(DiscordClient client, GuildMemberUpd if (differrence > 10 && !userMute.IsNull && !e.Member.Roles.Contains(muteRole)) db.HashDeleteAsync("mutes", e.Member.Id); + // Nickname lock check + var nicknamelock = await db.HashGetAsync("nicknamelock", e.Member.Id); + + if (nicknamelock.HasValue) + { + if (e.Member.DisplayName != nicknamelock) + { + var oldName = e.Member.DisplayName; + await e.Member.ModifyAsync(a => + { + a.Nickname = nicknamelock.ToString(); + a.AuditLogReason = "Nickname lock applied, reverting nickname change. Nice try though."; + }); + await LogChannelHelper.LogMessageAsync("nicknames", $"{cfgjson.Emoji.MessageEdit} Reverted nickname change from {e.Member.Mention}: `{oldName}`"); + } + // We don't want to run the dehoist checks on locked nicknames, else it may cause a fight between the two. + return; + } + DehoistHelpers.CheckAndDehoistMemberAsync(e.Member); // Persist permadehoists @@ -220,6 +239,25 @@ public static async Task UserUpdated(DiscordClient client, UserUpdatedEventArgs var member = await homeGuild.GetMemberAsync(e.UserAfter.Id); + // Nickname lock check + var nicknamelock = await db.HashGetAsync("nicknamelock", member.Id); + + if (nicknamelock.HasValue) + { + if (member.DisplayName != nicknamelock) + { + var oldName = member.DisplayName; + await member.ModifyAsync(async a => + { + a.Nickname = nicknamelock.ToString(); + a.AuditLogReason = "Nickname lock applied, reverting nickname change. Nice try though."; + }); + await LogChannelHelper.LogMessageAsync("nicknames", $"{cfgjson.Emoji.MessageEdit} Reverted nickname change from {member.Mention}: `{oldName}`"); + } + // We don't want to run the dehoist checks on locked nicknames, else it may cause a fight between the two. + return; + } + DehoistHelpers.CheckAndDehoistMemberAsync(member); ScamHelpers.UsernameCheckAsync(member); ; } diff --git a/config.json b/config.json index 28e2269..0a47d3b 100644 --- a/config.json +++ b/config.json @@ -286,6 +286,9 @@ }, "mentions": { "channelId": 1018252827254063224 + }, + "nicknames": { + "channelId": 1280688061528674314 } }, "botOwners": [