From 828abaf796056e36bbc2e159b8c53524f79e7a77 Mon Sep 17 00:00:00 2001 From: Nix Krystik Date: Tue, 11 Aug 2026 19:01:16 +0800 Subject: [PATCH] (rewrite): Mostly implemented `/ban`. `/ban` & `/hardban` will need to be worked on properly by Tarrgon who knows more about the hell that is the database code. --- src/commands/infractions/ban.ts | 115 --------------- src/commands/moderation/ban.ts | 137 ++++++++++++++++++ .../{infractions => moderation}/hardban.ts | 11 +- 3 files changed, 143 insertions(+), 120 deletions(-) delete mode 100644 src/commands/infractions/ban.ts create mode 100644 src/commands/moderation/ban.ts rename src/commands/{infractions => moderation}/hardban.ts (70%) diff --git a/src/commands/infractions/ban.ts b/src/commands/infractions/ban.ts deleted file mode 100644 index 2e53645..0000000 --- a/src/commands/infractions/ban.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { ChatInputCommandInteraction, Client, Guild, GuildMember, time, TimestampStyles, User } from 'discord.js'; -import { Database } from '../../shared/Database'; -import { AltData, comprehensiveAltLookupFromDiscord, deferInteraction, getIdFromInput } from '../../utils'; - -export default { - handler: async function (client: Client, interaction: ChatInputCommandInteraction) { - await deferInteraction(interaction); - - const input = interaction.options.getString('user', true); - - const idToUse = getIdFromInput(input); - const reason = interaction.options.getString('reason') ?? ''; - - const hours = (interaction.options.getNumber('hours') ?? 0) * 3.6e+6; - const minutes = (interaction.options.getNumber('minutes') ?? 0) * 60000; - const seconds = (interaction.options.getNumber('seconds') ?? 0) * 1000; - - const duration = hours + minutes + seconds; - - const deleteMessageDays = (interaction.options.getNumber('delete-message-days') ?? 0) * 86400; - - const fullBan = interaction.options.getBoolean('full-ban') ?? false; - - let banMember: GuildMember | null = null; - - try { - banMember = await interaction.guild.members.fetch(idToUse); - } catch (e) { - // Member not in server. - } - - const member = await interaction.guild.members.fetch(interaction.user.id); - - if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) { - return await interaction.editReply('You do not have permission to ban this user.'); - } - - if (banMember && !banMember.bannable) { - return await interaction.editReply('I do not have permission to ban this user.'); - } - - const expiresAt = new Date(Date.now() + duration); - - await Database.putBan(idToUse, duration > 0 ? expiresAt : null, fullBan); - - try { - await interaction.guild.bans.create(idToUse, { - reason: (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${interaction.user.username} (${interaction.user.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim(), - deleteMessageSeconds: deleteMessageDays - }); - } catch (e) { - console.error(e); - return await interaction.editReply("Error banning user (couldn't ban)."); - } - - if (fullBan) { - const alts = await comprehensiveAltLookupFromDiscord(idToUse, interaction.guild); - - await removeAllAlts([alts], interaction.guild, interaction.user, fullBan, reason, deleteMessageDays, duration, expiresAt); - } - - await interaction.editReply(`<@${idToUse}> (${idToUse}) has been ${fullBan ? 'full banned' : 'banned'}.`); - } -}; - -async function removeAllAlts(altData: AltData[], guild: Guild, moderator: User, fullBan: boolean, reason: string, deleteMessageDays: number, duration: number, expiresAt: Date) { - for (const data of altData) { - if (data.type == 'discord') { - try { - if (!data.banned) { - await guild.members.kick(data.thisId as string, (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${moderator.username} (${moderator.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim()); - } - } catch (e) { - console.error(e); - } - } - - await removeAllAlts(data.alts, guild, moderator, fullBan, reason, deleteMessageDays, duration, expiresAt); - } -} - -// External Imports -import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime'; - -// --------------- - -@SlashCommand( - new CommandBuilder('ban', 'Ban a user.') - .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) - .setContexts(Constants.InteractionContextType.Guild) - .setCommandType(Constants.ApplicationCommandType.ChatInput) - .setMemberPermission(Constants.PermissionFlagsBits.BanMembers) - .addUserOption('user', 'The user to ban.', true) - .addStringOption({ name: 'duration', description: 'The ban duration.', required: false }) - .addStringOption({ name: 'reason', description: 'The ban reason.', required: false }) - .addNumberOption({ name: 'cleanup', description: 'Number of days worth of messages to delete.', required: false }) -) -class BanCommand extends Command { - cooldown = 5; - userPermissions = [Constants.PermissionFlagsBits.BanMembers]; - - async handleCommand(context: CommandClient, interaction: CommandInteraction, fetchedData?: any) { - if (!interaction.inGuild() || !interaction.member.highestRole) return; - await interaction.defer(); - - const user = interaction.getRequiredMember('user'); - const duration = interaction.getString('duration'); - const reason = interaction.getString('reason') || 'No reason specified.'; - const messageDays = interaction.getNumber('cleanup') || 0; - - await context.banGuildMember(interaction.guild.id, user.id, messageDays, reason); - } -} - -export default new BanCommand('ban'); \ No newline at end of file diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts new file mode 100644 index 0000000..677d513 --- /dev/null +++ b/src/commands/moderation/ban.ts @@ -0,0 +1,137 @@ +import { ChatInputCommandInteraction, Client, Guild, GuildMember, time, TimestampStyles, User } from 'discord.js'; +import { Database } from '../../shared/Database'; +import { AltData, comprehensiveAltLookupFromDiscord, CreateDefaultEmbed, deferInteraction, getIdFromInput, SetSeverity } from '../../utils'; + +// export default { +// handler: async function (client: Client, interaction: ChatInputCommandInteraction) { +// await deferInteraction(interaction); + +// const input = interaction.options.getString('user', true); + +// const idToUse = getIdFromInput(input); +// const reason = interaction.options.getString('reason') ?? ''; + +// const hours = (interaction.options.getNumber('hours') ?? 0) * 3.6e+6; +// const minutes = (interaction.options.getNumber('minutes') ?? 0) * 60000; +// const seconds = (interaction.options.getNumber('seconds') ?? 0) * 1000; + +// const duration = hours + minutes + seconds; + +// const deleteMessageDays = (interaction.options.getNumber('delete-message-days') ?? 0) * 86400; + +// const fullBan = interaction.options.getBoolean('full-ban') ?? false; + +// let banMember: GuildMember | null = null; + +// try { +// banMember = await interaction.guild.members.fetch(idToUse); +// } catch (e) { +// // Member not in server. +// } + +// const member = await interaction.guild.members.fetch(interaction.user.id); + +// if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) { +// return await interaction.editReply('You do not have permission to ban this user.'); +// } + +// if (banMember && !banMember.bannable) { +// return await interaction.editReply('I do not have permission to ban this user.'); +// } + +// const expiresAt = new Date(Date.now() + duration); + +// await Database.putBan(idToUse, duration > 0 ? expiresAt : null, fullBan); + +// try { +// await interaction.guild.bans.create(idToUse, { +// reason: (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${interaction.user.username} (${interaction.user.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim(), +// deleteMessageSeconds: deleteMessageDays +// }); +// } catch (e) { +// console.error(e); +// return await interaction.editReply("Error banning user (couldn't ban)."); +// } + +// if (fullBan) { +// const alts = await comprehensiveAltLookupFromDiscord(idToUse, interaction.guild); + +// await removeAllAlts([alts], interaction.guild, interaction.user, fullBan, reason, deleteMessageDays, duration, expiresAt); +// } + +// await interaction.editReply(`<@${idToUse}> (${idToUse}) has been ${fullBan ? 'full banned' : 'banned'}.`); +// } +// }; + +// async function removeAllAlts(altData: AltData[], guild: Guild, moderator: User, fullBan: boolean, reason: string, deleteMessageDays: number, duration: number, expiresAt: Date) { +// for (const data of altData) { +// if (data.type == 'discord') { +// try { +// if (!data.banned) { +// await guild.members.kick(data.thisId as string, (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${moderator.username} (${moderator.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim()); +// } +// } catch (e) { +// console.error(e); +// } +// } + +// await removeAllAlts(data.alts, guild, moderator, fullBan, reason, deleteMessageDays, duration, expiresAt); +// } +// } + +// External Imports +import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime'; + +// --------------- + +@SlashCommand( + new CommandBuilder('ban', 'Ban a user, optionally cleanup 1 day worth of messages.') + .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) + .setContexts(Constants.InteractionContextType.Guild) + .setCommandType(Constants.ApplicationCommandType.ChatInput) + .setMemberPermission(Constants.PermissionFlagsBits.BanMembers) + .addUserOption('user', 'The user to ban.', true) + .addStringOption({ name: 'duration', description: '(optional): The duration of the ban. Formatted as 7d12h30m30s.', required: false }) + .addStringOption({ name: 'reason', description: '(optional): The reason for banning the user.', required: false }) + .addBooleanOption('clean', "(optional): Clean the user's messages upwards of a day.", false) + .addBooleanOption('purge', "(optional): Purge the user's messages from the database.", false) +) +class BanCommand extends Command { + cooldown = 5; + userPermissions = [Constants.PermissionFlagsBits.BanMembers]; + + async handleCommand(context: CommandClient, interaction: CommandInteraction, data: any) { + if (!interaction.inGuild()) return; + await interaction.defer(); + + const user = interaction.getRequiredMember('user'); + const duration = interaction.getString('duration'); + const reason = interaction.getString('reason') || 'No reason specified.'; + const clean = interaction.getBoolean('clean') || false; + const purge = interaction.getBoolean('purge') || false; + + // Nix -> Tarrgon: Could you pretty please hook up the database for this 🥺 + + await context.banGuildMember(interaction.guild.id, user.id, clean ? 1 : undefined, reason); + if (purge) await Database.purgeMessagesFrom(user.id); + + interaction.createMessage({ + embeds: [{ + ...CreateDefaultEmbed(context), + ...SetSeverity('success'), + + description: 'Executed ban action.', + + fields: [ + { name: 'User', value: `${user.username} (${user.mention})`, inline: false }, + { name: 'Duration', value: `${duration || 'Permanent'}`, inline: false }, + { name: 'Reason', value: `${user.username} (${user.mention})`, inline: false }, + { name: 'Clean', value: `${clean ? 'Yes' : 'No'}`, inline: true }, + { name: 'Purge', value: `${purge ? 'Yes' : 'No'}`, inline: true }, + ] + }] + }); + } +} + +export default new BanCommand('ban'); \ No newline at end of file diff --git a/src/commands/infractions/hardban.ts b/src/commands/moderation/hardban.ts similarity index 70% rename from src/commands/infractions/hardban.ts rename to src/commands/moderation/hardban.ts index 35d67fc..09ebf4b 100644 --- a/src/commands/infractions/hardban.ts +++ b/src/commands/moderation/hardban.ts @@ -10,21 +10,22 @@ import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, .setCommandType(Constants.ApplicationCommandType.ChatInput) .setMemberPermission(Constants.PermissionFlagsBits.BanMembers) .addUserOption('user', 'The user to ban.', true) - .addStringOption({ name: 'reason', description: 'The ban reason.', required: false }) - .addNumberOption({ name: 'cleanup', description: 'Number of days worth of messages to delete.', required: false }) + .addStringOption({ name: 'reason', description: '(optional): The reason for banning the user.', required: false }) + .addBooleanOption('clean', "(optional): Clean the user's messages upwards of a day.", false) + .addBooleanOption('purge', "(optional): Purge the user's messages from the database.", false) ) class HardBanCommand extends Command { cooldown = 5; userPermissions = [Constants.PermissionFlagsBits.BanMembers]; async handleCommand(context: CommandClient, interaction: CommandInteraction, fetchedData?: any) { - if (!interaction.inGuild() || !interaction.member.highestRole) return; + if (!interaction.inGuild()) return; await interaction.defer(); const user = interaction.getRequiredMember('user'); - const duration = interaction.getString('duration'); const reason = interaction.getString('reason') || 'No reason specified.'; - const messageDays = interaction.getNumber('cleanup') || 0; + const clean = interaction.getBoolean('clean') || false; + const purge = interaction.getBoolean('purge') || false; // Nix -> Tarrgon: Please implement the hard-ban here :3 }