diff options
41 files changed, 380 insertions, 296 deletions
diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts index fdf153a..7cb7346 100644 --- a/src/commands/admin/roleAll.ts +++ b/src/commands/admin/roleAll.ts @@ -1,4 +1,5 @@ import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits, type GuildMember } from 'discord.js'; export default class RoleAllCommand extends BushCommand { @@ -39,7 +40,7 @@ export default class RoleAllCommand extends BushCommand { } public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'>; bots: ArgType<'boolean'> }) { - if (!message.inGuild()) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`); + assert(message.inGuild()); if (!message.member!.permissions.has(PermissionFlagsBits.Administrator) && !message.member!.user.isOwner()) return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`); if (message.util.isSlashMessage(message)) await message.interaction.deferReply(); diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts index 7ea1784..ace8802 100644 --- a/src/commands/dev/__template.ts +++ b/src/commands/dev/__template.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; import { ApplicationCommandOptionType } from 'discord.js'; export default class TemplateCommand extends BushCommand { @@ -40,7 +40,7 @@ export default class TemplateCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { required_argument: ArgType<'string'>; optional_argument: OptionalArgType<'string'> } + args: { required_argument: ArgType<'string'>; optional_argument: OptArgType<'string'> } ) { return await message.util.reply(`${util.emojis.error} Do not use the template command.`); args; diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts index 9365107..2d7b1f8 100644 --- a/src/commands/dev/test.ts +++ b/src/commands/dev/test.ts @@ -51,7 +51,7 @@ export default class TestCommand extends BushCommand { return await message.util.reply(responses[Math.floor(Math.random() * responses.length)]); } - if (['button', 'buttons'].includes(args?.feature?.toLowerCase())) { + if (['button', 'buttons'].includes(args.feature?.toLowerCase())) { const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents([ new ButtonBuilder({ style: ButtonStyle.Primary, customId: 'primaryButton', label: 'Primary' }), new ButtonBuilder({ style: ButtonStyle.Secondary, customId: 'secondaryButton', label: 'Secondary' }), @@ -60,7 +60,7 @@ export default class TestCommand extends BushCommand { new ButtonBuilder({ style: ButtonStyle.Link, label: 'Link', url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' }) ]); return await message.util.reply({ content: 'buttons', components: [buttonRow] }); - } else if (['embed', 'button embed'].includes(args?.feature?.toLowerCase())) { + } else if (['embed', 'button embed'].includes(args.feature?.toLowerCase())) { const embed = new EmbedBuilder() .addFields([{ name: 'Field Name', value: 'Field Content' }]) .setAuthor({ name: 'Author', iconURL: 'https://www.w3schools.com/w3css/img_snowtops.jpg', url: 'https://google.com/' }) @@ -79,7 +79,7 @@ export default class TestCommand extends BushCommand { new ButtonBuilder({ style: ButtonStyle.Link, label: 'Link', url: 'https://google.com/' }) ]); return await message.util.reply({ content: 'Test', embeds: [embed], components: [buttonRow] }); - } else if (['lots of buttons'].includes(args?.feature?.toLowerCase())) { + } else if (['lots of buttons'].includes(args.feature?.toLowerCase())) { const buttonRows: ActionRowBuilder<ButtonBuilder>[] = []; for (let a = 1; a <= 5; a++) { const row = new ActionRowBuilder<ButtonBuilder>(); @@ -91,13 +91,13 @@ export default class TestCommand extends BushCommand { buttonRows.push(row); } return await message.util.reply({ content: 'buttons', components: buttonRows }); - } else if (['paginate'].includes(args?.feature?.toLowerCase())) { + } else if (['paginate'].includes(args.feature?.toLowerCase())) { const embeds = []; for (let i = 1; i <= 5; i++) { embeds.push(new EmbedBuilder().setDescription(i.toString())); } return await ButtonPaginator.send(message, embeds); - } else if (['lots of embeds'].includes(args?.feature?.toLowerCase())) { + } else if (['lots of embeds'].includes(args.feature?.toLowerCase())) { const description = 'This is a description.'; const _avatar = message.author.avatarURL() ?? undefined; const author = { name: 'This is a author', iconURL: _avatar }; @@ -123,7 +123,7 @@ export default class TestCommand extends BushCommand { ButtonRows.push(row); } return await message.util.reply({ content: 'this is content', components: ButtonRows, embeds }); - } else if (['delete slash commands'].includes(args?.feature?.toLowerCase())) { + } else if (['delete slash commands'].includes(args.feature?.toLowerCase())) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This test can only be run in a guild.`); await client.guilds.fetch(); const promises: Promise<Collection<string, ApplicationCommand>>[] = []; @@ -136,16 +136,16 @@ export default class TestCommand extends BushCommand { await client.application!.commands.set([]); return await message.util.reply(`${util.emojis.success} Removed guild commands and global commands.`); - } else if (['drop down', 'drop downs', 'select menu', 'select menus'].includes(args?.feature?.toLowerCase())) { + } else if (['drop down', 'drop downs', 'select menu', 'select menus'].includes(args.feature?.toLowerCase())) { return message.util.reply(`${util.emojis.error} no`); - } else if (['sync automod'].includes(args?.feature?.toLowerCase())) { + } else if (['sync automod'].includes(args.feature?.toLowerCase())) { const row = (await Shared.findByPk(0))!; row.badLinks = badLinksArray; row.badLinksSecret = badLinksSecretArray; row.badWords = badWords; await row.save(); return await message.util.reply(`${util.emojis.success} Synced automod.`); - } else if (['modal'].includes(args?.feature?.toLowerCase())) { + } else if (['modal'].includes(args.feature?.toLowerCase())) { const m = await message.util.reply({ content: 'Click for modal', components: [ diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index 59a1001..4872497 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; import assert from 'assert'; import { GuildDefaultMessageNotifications, GuildExplicitContentFilter } from 'discord-api-types/v10'; import { @@ -43,7 +43,7 @@ export default class GuildInfoCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { guild: OptionalArgType<'guild'> | OptionalArgType<'snowflake'> } + args: { guild: OptArgType<'guild'> | OptArgType<'snowflake'> } ) { if (!args.guild && !message.inGuild()) { return await message.util.reply( diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index 086a77a..af0fa98 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -1,5 +1,5 @@ import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { EmbedBuilder, PermissionFlagsBits, type Message } from 'discord.js'; +import { EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class PingCommand extends BushCommand { public constructor() { @@ -16,41 +16,32 @@ export default class PingCommand extends BushCommand { } public override async exec(message: BushMessage) { - const sentMessage = (await message.util.send('Pong!')) as Message; - const timestamp: number = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; - const botLatency = `${'```'}\n ${Math.round(sentMessage.createdTimestamp - timestamp)}ms ${'```'}`; - const apiLatency = `${'```'}\n ${Math.round(message.client.ws.ping)}ms ${'```'}`; - const embed = new EmbedBuilder() - .setTitle('Pong! 🏓') - .addFields([ - { name: 'Bot Latency', value: botLatency, inline: true }, - { name: 'API Latency', value: apiLatency, inline: true } - ]) - .setFooter({ text: message.author.username, iconURL: message.author.displayAvatarURL() }) - .setColor(util.colors.default) - .setTimestamp(); - await sentMessage.edit({ - content: null, - embeds: [embed] - }); + const timestamp1 = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; + const msg = await message.util.reply('Pong!'); + const timestamp2 = msg.editedTimestamp ? msg.editedTimestamp : msg.createdTimestamp; + void this.command(message, timestamp2 - timestamp1); } public override async execSlash(message: BushSlashMessage) { - const timestamp1 = message.interaction.createdTimestamp; - await message.interaction.reply('Pong!'); - const timestamp2 = await message.interaction.fetchReply().then((m) => (m as Message).createdTimestamp); - const botLatency = `${'```'}\n ${Math.round(timestamp2 - timestamp1)}ms ${'```'}`; - const apiLatency = `${'```'}\n ${Math.round(client.ws.ping)}ms ${'```'}`; + const timestamp1 = message.createdTimestamp; + const msg = (await message.util.reply({ content: 'Pong!', fetchReply: true })) as BushMessage; + const timestamp2 = msg.editedTimestamp ? msg.editedTimestamp : msg.createdTimestamp; + void this.command(message, timestamp2 - timestamp1); + } + + private command(message: BushMessage | BushSlashMessage, msgLatency: number) { + const botLatency = util.format.codeBlock(`${Math.round(msgLatency)}ms`); + const apiLatency = util.format.codeBlock(`${Math.round(message.client.ws.ping)}ms`); const embed = new EmbedBuilder() .setTitle('Pong! 🏓') .addFields([ { name: 'Bot Latency', value: botLatency, inline: true }, { name: 'API Latency', value: apiLatency, inline: true } ]) - .setFooter({ text: message.interaction.user.username, iconURL: message.interaction.user.displayAvatarURL() }) + .setFooter({ text: message.author.username, iconURL: message.author.displayAvatarURL() }) .setColor(util.colors.default) .setTimestamp(); - await message.interaction.editReply({ + return message.util.reply({ content: null, embeds: [embed] }); diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index 043b660..b45f9b3 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -1,4 +1,4 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; export default class PronounsCommand extends BushCommand { @@ -34,11 +34,14 @@ export default class PronounsCommand extends BushCommand { const pronouns = await util.getPronounsOf(user); if (!pronouns) { - return await message.util.reply( - `${author ? 'You do' : `${util.discord.escapeMarkdown(user.tag)} does`} not appear to have any pronouns set. Please${ - author ? '' : ' tell them to' - } go to https://pronoundb.org/ and set ${author ? 'your' : 'their'} pronouns.` - ); + return await message.util.reply({ + content: `${ + author ? 'You do' : `${util.discord.escapeMarkdown(user.tag)} does` + } not appear to have any pronouns set. Please${author ? '' : ' tell them to'} go to https://pronoundb.org/ and set ${ + author ? 'your' : 'their' + } pronouns.`, + allowedMentions: AllowedMentions.none() + }); } else { return await message.util.reply({ embeds: [ diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index 5f4a1bd..cb2fc5f 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -47,7 +47,7 @@ export default class UserInfoCommand extends BushCommand { public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'> | ArgType<'snowflake'> }) { const user = - args?.user === undefined || args?.user === null + args.user === null ? message.author : typeof args.user === 'object' ? args.user diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index c79a4e3..f476ac1 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -48,6 +48,6 @@ export default class LeaderboardCommand extends BushCommand { const embeds = chunked.map((c) => new EmbedBuilder().setTitle(`${message.guild.name}'s Leaderboard`).setDescription(c.join('\n')) ); - return await ButtonPaginator.send(message, embeds, undefined, true, args?.page ?? undefined); + return await ButtonPaginator.send(message, embeds, undefined, true, args.page ?? undefined); } } diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 50742e9..3a9a916 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -7,7 +7,7 @@ import { type BushMessage, type BushSlashMessage, type BushUser, - type OptionalArgType + type OptArgType } from '#lib'; import { SimplifyNumber } from '@notenoughupdates/simplify-number'; import assert from 'assert'; @@ -46,7 +46,7 @@ export default class LevelCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: OptionalArgType<'user'> }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { user: OptArgType<'user'> }) { assert(message.inGuild()); if (!(await message.guild.hasFeature('leveling'))) diff --git a/src/commands/leveling/levelRoles.ts b/src/commands/leveling/levelRoles.ts index 3d95933..9fe7dd0 100644 --- a/src/commands/leveling/levelRoles.ts +++ b/src/commands/leveling/levelRoles.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { AllowedMentions, BushCommand, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -40,7 +40,7 @@ export default class LevelRolesCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { level: ArgType<'integer'>; role: OptionalArgType<'role'> } + args: { level: ArgType<'integer'>; role: OptArgType<'role'> } ) { assert(message.inGuild()); assert(message.member); @@ -49,7 +49,7 @@ export default class LevelRolesCommand extends BushCommand { return await reply(`${util.emojis.error} This command can only be run in servers with the leveling feature enabled.`); } - if (args.level < 1) return await reply(`${util.emojis.error} You cannot set a level role less that 1.`); + if (args.level < 1) return await reply(`${util.emojis.error} You cannot set a level role less than **1**.`); if (args.role) { if (args.role.managed) diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index 30bbb72..ac7df57 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -45,18 +45,11 @@ export default class SetLevelCommand extends BushCommand { if (isNaN(level) || !Number.isInteger(level)) return await message.util.reply(`${util.emojis.error} Provide a valid number to set the user's level to.`); if (level > 6553 || level < 0) - return await message.util.reply(`${util.emojis.error} You cannot set a level higher than **6553**.`); + return await message.util.reply(`${util.emojis.error} You cannot set a level higher than **6,553**.`); const [levelEntry] = await Level.findOrBuild({ - where: { - user: user.id, - guild: message.guild.id - }, - defaults: { - user: user.id, - guild: message.guild.id, - xp: 0 - } + where: { user: user.id, guild: message.guild.id }, + defaults: { user: user.id, guild: message.guild.id, xp: 0 } }); await levelEntry.update({ xp: Level.convertLevelToXp(level), user: user.id, guild: message.guild.id }); return await message.util.send({ diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index e26cdcc..1f7c981 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -50,17 +50,12 @@ export default class SetXpCommand extends BushCommand { ); const [levelEntry] = await Level.findOrBuild({ - where: { - user: user.id, - guild: message.guild.id - }, - defaults: { - user: user.id, - guild: message.guild.id - } + where: { user: user.id, guild: message.guild.id }, + defaults: { user: user.id, guild: message.guild.id } }); await levelEntry.update({ xp: xp, user: user.id, guild: message.guild.id }); + return await message.util.send({ content: `Successfully set <@${user.id}>'s xp to ${util.format.input( levelEntry.xp.toLocaleString() diff --git a/src/commands/moderation/_activePunishments.ts b/src/commands/moderation/_activePunishments.ts index cffc39f..e751493 100644 --- a/src/commands/moderation/_activePunishments.ts +++ b/src/commands/moderation/_activePunishments.ts @@ -1,78 +1,79 @@ -// import { BushCommand, ModLog, ModLogModel, type BushGuildMember, type BushMessage, type BushSlashMessage } from '#lib'; -// import { FindOptions, Op } from 'sequelize'; -// import { Permissions } from 'discord.js'; +/* import { BushCommand, ModLog, ModLogModel, type BushGuildMember, type BushMessage, type BushSlashMessage } from '#lib'; +import { FindOptions, Op } from 'sequelize'; +import { Permissions } from 'discord.js'; -// const punishmentTypes = ['ban', 'kick', 'mute', 'warn', 'role'] as const; +const punishmentTypes = ['ban', 'kick', 'mute', 'warn', 'role'] as const; -// export default class ActivePunishmentsCommand extends BushCommand { -// public constructor() { -// super('activePunishments', { -// aliases: ['active-punishments', 'ap'], -// category: 'moderation', -// description: 'Gets a list of all the active punishment in the server.', -// usage: [`active-punishments [--moderator <user>] [--type <${punishmentTypes.map((v) => `'${v}'`).join('|')}>]`], -// examples: ['active-punishments'], -// args: [ -// { -// id: 'moderator', -// description: 'Only show active punishments by this moderator.', -// type: 'user', -// match: 'option', -// prompt: 'Only show active punishments from what user?', -// optional: true, -// slashType: ApplicationCommandOptionType.User, -// slashResolve: 'Member' -// }, -// { -// id: 'type', -// description: 'Only show active punishments of this type.', -// customType: [...punishmentTypes], -// readableType: punishmentTypes.map((v) => `'${v}'`).join('|'), -// match: 'option', -// optional: true, -// slashType: ApplicationCommandOptionType.String, -// choices: punishmentTypes.map((v) => ({ name: v, value: v })) -// } -// ], -// slash: true, -// channel: 'guild', -// hidden: true, -// clientPermissions: (m) => util.clientSendAndPermCheck(m), -// userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) -// }); -// } -// -// public override async exec( -// message: BushMessage | BushSlashMessage, -// args: { moderator?: BushGuildMember; type: typeof punishmentTypes[number] } -// ) { -// const where: FindOptions<ModLogModel>['where'] = { guild: message.guild!.id }; -// if (args.moderator?.id) where.user = args.moderator.id; -// if (args.type) { -// switch (args.type) { -// case 'ban': -// where.type = { [Op.or]: ['PERM_BAN', 'TEMP_BAN', 'UNBAN'] }; -// break; -// case 'kick': -// where.type = { [Op.or]: ['KICK'] }; -// break; -// case 'mute': -// where.type = { [Op.or]: ['PERM_MUTE', 'TEMP_MUTE', 'UNMUTE'] }; -// break; -// case 'warn': -// where.type = { [Op.or]: ['WARN'] }; -// break; -// case 'role': -// where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; -// break; -// default: -// return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); -// } -// } +export default class ActivePunishmentsCommand extends BushCommand { + public constructor() { + super('activePunishments', { + aliases: ['active-punishments', 'ap'], + category: 'moderation', + description: 'Gets a list of all the active punishment in the server.', + usage: [`active-punishments [--moderator <user>] [--type <${punishmentTypes.map((v) => `'${v}'`).join('|')}>]`], + examples: ['active-punishments'], + args: [ + { + id: 'moderator', + description: 'Only show active punishments by this moderator.', + type: 'user', + match: 'option', + prompt: 'Only show active punishments from what user?', + optional: true, + slashType: ApplicationCommandOptionType.User, + slashResolve: 'Member' + }, + { + id: 'type', + description: 'Only show active punishments of this type.', + customType: [...punishmentTypes], + readableType: punishmentTypes.map((v) => `'${v}'`).join('|'), + match: 'option', + optional: true, + slashType: ApplicationCommandOptionType.String, + choices: punishmentTypes.map((v) => ({ name: v, value: v })) + } + ], + slash: true, + channel: 'guild', + hidden: true, + clientPermissions: (m) => util.clientSendAndPermCheck(m), + userPermissions: (m) => util.userGuildPermCheck(m, [PermissionFlagsBits.ManageMessages]) + }); + } -// const logs = await ModLog.findAll({ -// where, -// order: [['createdAt', 'ASC']] -// }); -// } -// } + public override async exec( + message: BushMessage | BushSlashMessage, + args: { moderator?: BushGuildMember; type: typeof punishmentTypes[number] } + ) { + const where: FindOptions<ModLogModel>['where'] = { guild: message.guild!.id }; + if (args.moderator?.id) where.user = args.moderator.id; + if (args.type) { + switch (args.type) { + case 'ban': + where.type = { [Op.or]: ['PERM_BAN', 'TEMP_BAN', 'UNBAN'] }; + break; + case 'kick': + where.type = { [Op.or]: ['KICK'] }; + break; + case 'mute': + where.type = { [Op.or]: ['PERM_MUTE', 'TEMP_MUTE', 'UNMUTE'] }; + break; + case 'warn': + where.type = { [Op.or]: ['WARN'] }; + break; + case 'role': + where.type = { [Op.or]: ['PERM_PUNISHMENT_ROLE', 'TEMP_PUNISHMENT_ROLE', 'REMOVE_PUNISHMENT_ROLE'] }; + break; + default: + return message.util.reply(`${util.emojis.error} You supplied an invalid case type to filter by.`); + } + } + + const logs = await ModLog.findAll({ + where, + order: [['createdAt', 'ASC']] + }); + } +} + */ diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 25102e0..14bbba6 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -6,7 +6,7 @@ import { type ArgType, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -72,8 +72,8 @@ export default class BanCommand extends BushCommand { message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'> | ArgType<'snowf |
