aboutsummaryrefslogtreecommitdiff
path: root/src/commands/moderation/ban.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/moderation/ban.ts')
-rw-r--r--src/commands/moderation/ban.ts224
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);
+ }
+ }
+}