aboutsummaryrefslogtreecommitdiff
path: root/src/commands/moderation/ban.ts
blob: 5e3350fd02279485bc3c453ffe3f2c136d67a2e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import {
	AllowedMentions,
	banResponse,
	BushCommand,
	Moderation,
	type ArgType,
	type BushMessage,
	type BushSlashMessage,
	type OptionalArgType
} from '#lib';
import { ApplicationCommandOptionType, PermissionFlagsBits, type User } from 'discord.js';

export default class BanCommand extends BushCommand {
	public constructor() {
		super('ban', {
			aliases: ['ban', 'force-ban', 'dban'],
			category: 'moderation',
			description: 'Ban a member from the server.',
			usage: ['ban <member> [reasonAndDuration] [--days <days>]'],
			examples: ['ban ironm00n 1 day commands in #general --delete 7'],
			args: [
				{
					id: 'user',
					description: 'The user that will be banned.',
					type: util.arg.union('user', 'snowflake'),
					prompt: 'What user would you like to ban?',
					retry: '{error} Choose a valid user to ban.',
					slashType: ApplicationCommandOptionType.User
				},
				{
					id: 'reason_and_duration',
					description: 'The reason and duration of the ban.',
					type: 'contentWithDuration',
					match: 'rest',
					prompt: 'Why should this user be banned and for how long?',
					retry: '{error} Choose a valid ban reason and duration.',
					slashType: ApplicationCommandOptionType.String,
					optional: true
				},
				{
					id: 'days',
					description: 'The number of days of messages to delete when the user is banned, defaults to 0.',
					flag: ['--days', '--delete'],
					match: 'option',
					prompt: "How many days of the user's messages would you like to delete?",
					retry: '{error} Choose between 0 and 7 days to delete messages from the user for.',
					type: util.arg.range('integer', 0, 7, true),
					optional: true,
					slashType: ApplicationCommandOptionType.Integer,
					choices: [...Array(8).keys()].map((v) => ({ name: v.toString(), value: v }))
				},
				{
					id: 'force',
					description: 'Override permission checks.',
					flag: '--force',
					match: 'flag',
					optional: true,
					slashType: false,
					only: 'text',
					ownerOnly: true
				}
			],
			slash: true,
			channel: 'guild',
			clientPermissions: [PermissionFlagsBits.BanMembers],
			userPermissions: [PermissionFlagsBits.BanMembers]
		});
	}

	public override async exec(
		message: BushMessage | BushSlashMessage,
		args: {
			user: ArgType<'user'> | ArgType<'snowflake'>;
			reason_and_duration: OptionalArgType<'contentWithDuration'>;
			days: OptionalArgType<'integer'>;
			force: boolean;
		}
	) {
		if (args.reason_and_duration && typeof args.reason_and_duration === 'object') args.reason_and_duration.duration ??= 0;
		args.days ??= 0;

		if (!message.guild) return message.util.reply(`${util.emojis.error} This command cannot be used in dms.`);
		const member = message.guild!.members.cache.get((args.user as User)?.id ?? args.user);
		const user = member?.user ?? (await util.resolveNonCachedUser((args.user as User)?.id ?? args.user));
		if (!user) return message.util.reply(`${util.emojis.error} Invalid user.`);
		const useForce = args.force && message.author.isOwner();
		if (!message.member) throw new Error(`message.member is null`);

		const canModerateResponse = member ? await Moderation.permissionCheck(message.member, member, 'ban', true, useForce) : true;

		if (canModerateResponse !== true) {
			return await message.util.reply(canModerateResponse);
		}

		if (message.util.parsed?.alias === 'dban' && !args.days) args.days = 1;

		if (!Number.isInteger(args.days) || args.days! < 0 || args.days! > 7) {
			return message.util.reply(`${util.emojis.error} The delete days must be an integer between 0 and 7.`);
		}

		let time: number | null;
		if (args.reason_and_duration) {
			time =
				typeof args.reason_and_duration === 'string'
					? await util.arg.cast('duration', message, args.reason_and_duration)
					: args.reason_and_duration.duration;
		}
		const parsedReason = args.reason_and_duration?.contentWithoutTime ?? null;

		const responseCode = member
			? await member.bushBan({
					reason: parsedReason,
					moderator: message.member,
					duration: time! ?? 0,
					deleteDays: args.days
			  })
			: await message.guild.bushBan({
					user,
					reason: parsedReason,
					moderator: message.member,
					duration: time! ?? 0,
					deleteDays: args.days
			  });

		const responseMessage = (): string => {
			const victim = util.format.input(user.tag);
			switch (responseCode) {
				case banResponse.ALREADY_BANNED:
					return `${util.emojis.error} ${victim} is already banned.`;
				case banResponse.MISSING_PERMISSIONS:
					return `${util.emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`;
				case banResponse.ACTION_ERROR:
					return `${util.emojis.error} An error occurred while trying to ban ${victim}.`;
				case banResponse.PUNISHMENT_ENTRY_ADD_ERROR:
					return `${util.emojis.error} While banning ${victim}, there was an error creating a ban entry, please report this to my developers.`;
				case banResponse.MODLOG_ERROR:
					return `${util.emojis.error} While banning ${victim}, there was an error creating a modlog entry, please report this to my developers.`;
				case banResponse.DM_ERROR:
					return `${util.emojis.warn} Banned ${victim} however I could not send them a dm.`;
				case banResponse.SUCCESS:
					return `${util.emojis.success} Successfully banned ${victim}.`;
				default:
					return `${util.emojis.error} An error occurred: ${util.format.input(responseCode)}}`;
			}
		};
		return await message.util.reply({ content: responseMessage(), allowedMentions: AllowedMentions.none() });
	}
}