diff options
Diffstat (limited to 'src/commands/moderation/ban.ts')
-rw-r--r-- | src/commands/moderation/ban.ts | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts new file mode 100644 index 0000000..feb020b --- /dev/null +++ b/src/commands/moderation/ban.ts @@ -0,0 +1,224 @@ +import { User } from 'discord.js'; +import { Guild } from '../../lib/models'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Ban, Modlog, ModlogType } from '../../lib/models'; +import moment from 'moment'; +import { Message } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { ApplicationCommandOptionType } from 'discord-api-types'; + +const durationAliases: Record<string, string[]> = { + weeks: ['w', 'weeks', 'week', 'wk', 'wks'], + days: ['d', 'days', 'day'], + hours: ['h', 'hours', 'hour', 'hr', 'hrs'], + minutes: ['m', 'min', 'mins', 'minutes', 'minute'], + months: ['mo', 'month', 'months'] +}; +const durationRegex = + /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; + +export default class BanCommand extends BushCommand { + constructor() { + super('ban', { + aliases: ['ban'], + category: 'moderation', + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to ban?', + retry: 'Invalid response. What user would you like to ban?' + } + }, + { + id: 'reason', + match: 'rest' + }, + { + id: 'time', + match: 'option', + flag: '--time' + } + ], + clientPermissions: ['BAN_MEMBERS'], + userPermissions: ['BAN_MEMBERS'], + description: { + content: + 'Ban a member and log it in modlogs (with optional time to unban)', + usage: 'ban <member> <reason> [--time]', + examples: [ + 'ban @Tyman being cool', + 'ban @Tyman being cool --time 7days' + ] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to ban', + required: true + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'reason', + description: 'The reason to show in modlogs and audit log', + required: false + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'time', + description: + 'The time the user should be banned for (default permanent)', + required: false + } + ] + }); + } + async *genResponses( + message: Message | CommandInteraction, + user: User, + reason?: string, + time?: string + ): AsyncIterable<string> { + const duration = moment.duration(); + let modlogEnry: Modlog; + let banEntry: Ban; + const translatedTime: string[] = []; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + try { + if (time) { + const parsed = [...time.matchAll(durationRegex)]; + if (parsed.length < 1) { + yield 'Invalid time.'; + return; + } + for (const part of parsed) { + const translated = Object.keys(durationAliases).find((k) => + durationAliases[k].includes(part[2]) + ); + translatedTime.push(part[1] + ' ' + translated); + duration.add( + Number(part[1]), + translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' + ); + } + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.TEMPBAN, + duration: duration.asMilliseconds(), + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + expires: new Date(new Date().getTime() + duration.asMilliseconds()), + modlog: modlogEnry.id + }); + } else { + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.BAN, + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + modlog: modlogEnry.id + }); + } + await modlogEnry.save(); + await banEntry.save(); + } catch (e) { + console.error(e); + yield 'Error saving to database. Please report this to a developer.'; + return; + } + try { + await user.send( + `You were banned in ${message.guild.name} ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\`` + ); + } catch (e) { + yield 'Error sending message to user'; + } + await message.guild.members.ban(user, { + reason: `Banned by ${ + message instanceof CommandInteraction + ? message.user.tag + : message.author.tag + } with ${reason ? `reason ${reason}` : 'no reason'}` + }); + yield `Banned <@!${user.id}> ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\``; + } catch { + yield 'Error banning :/'; + await banEntry.destroy(); + await modlogEnry.destroy(); + return; + } + } + async exec( + message: Message, + { user, reason, time }: { user: User; reason?: string; time?: string } + ): Promise<void> { + for await (const response of this.genResponses( + message, + user, + reason, + time + )) { + await message.util.send(response); + } + } + + async execSlash( + message: CommandInteraction, + { + user, + reason, + time + }: { + user: SlashCommandOption<undefined>; + reason: SlashCommandOption<string>; + time: SlashCommandOption<string>; + } + ): Promise<void> { + for await (const response of this.genResponses( + message, + user.user, + reason?.value, + time?.value + )) { + await message.reply(response); + } + } +} |