diff options
29 files changed, 230 insertions, 192 deletions
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/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..6886337 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); 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<'snowflake'>; - reason_and_duration: OptionalArgType<'contentWithDuration'> | string; - days: OptionalArgType<'integer'>; + reason_and_duration: OptArgType<'contentWithDuration'> | string; + days: OptArgType<'integer'>; force: boolean; } ) { diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/block.ts index 554ef2b..722f08b 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/block.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'; @@ -61,7 +61,7 @@ export default class BlockCommand extends BushCommand { message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'>; - reason_and_duration: OptionalArgType<'contentWithDuration'> | string; + reason_and_duration: OptArgType<'contentWithDuration'> | string; force?: ArgType<'boolean'>; } ) { diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts index 68d7edc..d60a5b0 100644 --- a/src/commands/moderation/evidence.ts +++ b/src/commands/moderation/evidence.ts @@ -1,4 +1,4 @@ -import { BushCommand, ModLog, OptionalArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, ModLog, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; import assert from 'assert'; import { ArgumentGeneratorReturn } from 'discord-akairo'; import { ArgumentTypeCasterReturn } from 'discord-akairo/dist/src/struct/commands/arguments/Argument.js'; @@ -64,7 +64,7 @@ export default class EvidenceCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { case_id: caseID, evidence }: { case_id: string; evidence: OptionalArgType<'string'> } + { case_id: caseID, evidence }: { case_id: string; evidence: OptArgType<'string'> } ) { assert(message.inGuild()); @@ -85,7 +85,7 @@ export default class EvidenceCommand extends BushCommand { return message.util.reply(`${util.emojis.success} Successfully updated the evidence for case ${util.format.input(caseID)}.`); } - public static getEvidence(message: BushMessage | BushSlashMessage, evidenceArg: OptionalArgType<'string'>): null | string { + public static getEvidence(message: BushMessage | BushSlashMessage, evidenceArg: OptArgType<'string'>): null | string { if (evidenceArg && (message as BushMessage).attachments?.size) { void message.util.reply(`${util.emojis.error} Please either attach an image or a reason not both.`); return null; diff --git a/src/commands/moderation/lockdown.ts b/src/commands/moderation/lockdown.ts index 36f3240..f298ec9 100644 --- a/src/commands/moderation/lockdown.ts +++ b/src/commands/moderation/lockdown.ts @@ -8,7 +8,7 @@ import { type ArgType, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, ChannelType, Collection, PermissionFlagsBits } from 'discord.js'; @@ -67,8 +67,8 @@ export default class LockdownCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'> | OptionalArgType<'threadChannel'>; - reason: OptionalArgType<'string'>; + channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; + reason: OptArgType<'string'>; all: ArgType<'boolean'>; } ) { @@ -78,8 +78,8 @@ export default class LockdownCommand extends BushCommand { public static async lockdownOrUnlockdown( message: BushMessage | BushSlashMessage, args: { - channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'> | OptionalArgType<'threadChannel'>; - reason: OptionalArgType<'string'>; + channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; + reason: OptArgType<'string'>; all: ArgType<'boolean'>; }, action: 'lockdown' | 'unlockdown' diff --git a/src/commands/moderation/massBan.ts b/src/commands/moderation/massBan.ts index 568b6be..f1d85ed 100644 --- a/src/commands/moderation/massBan.ts +++ b/src/commands/moderation/massBan.ts @@ -5,7 +5,7 @@ import { type ArgType, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Collection, PermissionFlagsBits } from 'discord.js'; @@ -62,7 +62,7 @@ export default class MassBanCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { users: ArgType<'string'>; reason: OptionalArgType<'string'>; days: OptionalArgType<'integer'> } + args: { users: ArgType<'string'>; reason: OptArgType<'string'>; days: OptArgType<'integer'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/massEvidence.ts b/src/commands/moderation/massEvidence.ts index 62421da..67f5a25 100644 --- a/src/commands/moderation/massEvidence.ts +++ b/src/commands/moderation/massEvidence.ts @@ -1,4 +1,4 @@ -import { BushCommand, ModLog, type ArgType, type BushMessage, type BushSlashMessage, type OptionalArgType } from '#lib'; +import { BushCommand, ModLog, type ArgType, type BushMessage, type BushSlashMessage, type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; import { EvidenceCommand } from '../index.js'; @@ -45,7 +45,7 @@ export default class MassEvidenceCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { users: ArgType<'string'>; evidence: OptionalArgType<'string'> } + args: { users: ArgType<'string'>; evidence: OptArgType<'string'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index e32ece2..d846817 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.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'; @@ -60,7 +60,7 @@ export default class MuteCommand extends BushCommand { message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'>; - reason_and_duration: OptionalArgType<'contentWithDuration'> | string; + reason_and_duration: OptArgType<'contentWithDuration'> | string; force?: ArgType<'boolean'>; } ) { diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 8580f2f..a87b2bf 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -6,7 +6,7 @@ import { type ArgType, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import { type ArgumentGeneratorReturn } from 'discord-akairo'; import { ApplicationCommandOptionType, PermissionFlagsBits, type Snowflake } from 'discord.js'; @@ -123,7 +123,7 @@ export default class RoleCommand extends BushCommand { action: 'add' | 'remove'; member: ArgType<'member'>; role: ArgType<'role'>; - duration?: OptionalArgType<'duration'>; + duration?: OptArgType<'duration'>; force?: boolean; } ) { diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index 9973d61..e6ac6d0 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -5,7 +5,7 @@ import { type ArgType, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -47,7 +47,7 @@ export default class UnbanCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason }: { user: ArgType<'user'>; reason: OptionalArgType<'string'> } + { user, reason }: { user: ArgType<'user'>; reason: OptArgType<'string'> } ) { assert(message.inGuild()); diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/unblock.ts index 34b2075..22c9949 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/unblock.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'; @@ -58,7 +58,7 @@ export default class UnblockCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: ArgType<'boolean'> } + args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'boolean'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/unlockdown.ts b/src/commands/moderation/unlockdown.ts index 4694518..253ce37 100644 --- a/src/commands/moderation/unlockdown.ts +++ b/src/commands/moderation/unlockdown.ts @@ -1,5 +1,5 @@ import { LockdownCommand } from '#commands'; -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, ChannelType, PermissionFlagsBits } from 'discord.js'; export default class UnlockdownCommand extends BushCommand { @@ -55,8 +55,8 @@ export default class UnlockdownCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'> | OptionalArgType<'threadChannel'>; - reason: OptionalArgType<'string'>; + channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; + reason: OptArgType<'string'>; all: ArgType<'boolean'>; } ) { diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index de16cb5..094cadd 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -7,7 +7,7 @@ import { type BushGuildMember, type BushMessage, type BushSlashMessage, - type OptionalArgType + type OptArgType } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; @@ -59,7 +59,7 @@ export default class UnmuteCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason, force = false }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean } + { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: boolean } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/untimeout.ts b/src/commands/moderation/untimeout.ts index 636b178..cbaf7d1 100644 --- a/src/commands/moderation/untimeout.ts +++ b/src/commands/moderation/untimeout.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'; @@ -58,7 +58,7 @@ export default class UntimeoutCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: ArgType<'boolean'> } + args: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: ArgType<'boolean'> } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 3ab4b0b..87baf4a 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.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'; @@ -57,7 +57,7 @@ export default class WarnCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason, force = false }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean } + { user, reason, force = false }: { user: ArgType<'user'>; reason: OptArgType<'string'>; force?: boolean } ) { assert(message.inGuild()); assert(message.member); diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index 5564279..a37388b 100644 --- a/src/commands/moulberry-bush/capes.ts +++ b/src/commands/moulberry-bush/capes.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, DeleteButton, type BushMessage, type OptionalArgType } from '#lib'; +import { BushCommand, ButtonPaginator, DeleteButton, type BushMessage, type OptArgType } from '#lib'; import assert from 'assert'; import { APIEmbed } from 'discord-api-types/v10'; import { ApplicationCommandOptionType, AutocompleteInteraction, PermissionFlagsBits } from 'discord.js'; @@ -34,7 +34,7 @@ export default class CapesCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { cape: OptionalArgType<'string'> }) { + public override async exec(message: BushMessage, args: { cape: OptArgType<'string'> }) { const { tree: neuFileTree }: GithubTreeApi = await got .get('https://api.github.com/repos/Moulberry/NotEnoughUpdates/git/trees/master?recursive=1') .json(); diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index 31f59d7..df6ba88 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, BushSlashMessage, type BushMessage, type OptionalArgType } from '#lib'; +import { AllowedMentions, BushCommand, BushSlashMessage, type BushMessage, type OptArgType } from '#lib'; import { ApplicationCommandOptionType, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; const rules = [ @@ -93,7 +93,7 @@ export default class RuleCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { rule, user }: { rule: OptionalArgType<'integer'>; user: OptionalArgType<'user'> } + { rule, user }: { rule: OptArgType<'integer'>; user: OptArgType<'user'> } ) { const rulesEmbed = new EmbedBuilder() .setColor(0xef3929) diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index d603222..765fb24 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,41 +1,38 @@ -import { BushCommand, type ArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; import assert from 'assert'; import { type ArgumentGeneratorReturn, type ArgumentType, type ArgumentTypeCaster } from 'discord-akairo'; -import { ApplicationCommandOptionType, PermissionFlagsBits } from 'discord.js'; +import { ApplicationCommandOptionType, PermissionFlagsBits, type Attachment } from 'discord.js'; import _ from 'lodash'; +import { Stream } from 'stream'; import { URL } from 'url'; assert(_); +// so I don't have to retype things +const enum lang { + emojiStart = 'What emoji would you like to steal?', + emojiRetry = '{error} Pick a valid emoji, emoji id, or image url.', + emojiDescription = 'The emoji to steal.', + nameStart = 'What would you like to name the emoji?', + nameRetry = '{error} Choose a valid name fore the emoji.', + nameDescription = 'The name to give the new emoji.' +} + export default class StealCommand extends BushCommand { public constructor() { super('steal', { - aliases: ['steal', 'copy-emoji'], + aliases: ['steal', 'copy-emoji', 'emoji'], category: 'utilities', description: 'Steal an emoji from another server and add it to your own.', usage: ['steal <emoji/emojiId/url> [name]'], examples: ['steal <:omegaclown:782630946435366942> ironm00n'], - args: [ - { - id: 'emoji', - description: 'The emoji to steal.', - type: util.arg.union('discordEmoji', 'snowflake', 'url'), - readableType: 'discordEmoji|snowflake|url', - prompt: 'What emoji would you like to steal?', - retry: '{error} Pick a valid emoji, emoji id, or image url.', - optional: true, - only: 'slash', - slashType: ApplicationCommandOptionType.String - }, - { - id: 'name', - description: 'The name to give the new emoji.', - prompt: 'What would you like to name the emoji?', - retry: '{error} Choose a valid name fore the emoji.', - optional: true, - only: 'slash', - slashType: ApplicationCommandOptionType.String - } + slashOptions: [ + { name: 'emoji', description: lang.emojiStart, type: ApplicationCommandOptionType.Attachment, required: true }, + { name: 'name', description: lang.nameStart, type: ApplicationCommandOptionType.String, required: false } + ], + helpArgs: [ + { id: 'emoji', description: lang.emojiDescription, readableType: 'emoji|emojiId|url', optional: false }, + { id: 'name', description: lang.nameDescription, readableType: 'string', optional: true } ], slash: true, channel: 'guild', @@ -50,58 +47,79 @@ export default class StealCommand extends BushCommand { const emoji = hasImage ? message.attachments.first()!.url : yield { - id: 'emoji', type: util.arg.union('discordEmoji', 'snowflake', 'url') as ArgumentType | ArgumentTypeCaster, - prompt: { - start: 'What emoji would you like to steal?', - retry: '{error} Pick a valid emoji, emoji id, or image url.' - } + prompt: { start: lang.emojiStart, retry: lang.emojiRetry } }; const name = yield { - id: 'name', - prompt: { - start: 'What would you like to name the emoji?', - retry: '{error} Choose a valid name fore the emoji.', - optional: true - }, - default: - hasImage && message.attachments.first()?.name - ? _.camelCase(message.attachments.first()!.name ?? 'stolen_emoji') - : 'stolen_emoji' + prompt: { start: lang.nameStart, retry: lang.nameRetry, optional: true }, + default: hasImage && message.attachments.first()!.name ? _.snakeCase(message.attachments.first()!.name!) : 'unnamed_emoji' }; return { emoji, name }; } public override async exec( - message: BushMessage | BushSlashMessage, - args?: { emoji?: ArgType<'discordEmoji'> | ArgType<'snowflake'> | ArgType<'url'> | string; name: string } + message: BushMessage, + args: { emoji: OptArgType<'discordEmoji'> | OptArgType<'snowflake'> | OptArgType<'url'> | string; name: string } ) { - if (!args || !args.emoji) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); + assert(message.inGuild()); + + if (!args.emoji) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); const image = - args?.emoji instanceof URL + args.emoji instanceof URL ? args.emoji.href - : typeof args?.emoji === 'object' + : typeof args.emoji === 'object' ? `https://cdn.discordapp.com/emojis/${args.emoji.id}` - : client.consts.regex.snowflake.test(args?.emoji ?? '') + : client.consts.regex.snowflake.test(args.emoji ?? '') ? `https://cdn.discordapp.com/emojis/${args!.emoji}` - : (args?.emoji ?? '').match(/https?:\/\//) - ? args?.emoji + : (args.emoji ?? '').match(/https?:\/\//) + ? args.emoji : undefined; if (image === undefined) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); const emojiName = - args.name ?? args?.emoji instanceof URL - ? args?.name ?? 'stolen_emoji' - : typeof args?.emoji === 'object' - ? args?.name ?? args.emoji.name ?? 'stolen_emoji' + args.name ?? args.emoji instanceof URL + ? args.name ?? 'stolen_emoji' + : typeof args.emoji === 'object' + ? args.name ?? args.emoji.name ?? 'stolen_emoji' : 'stolen_emoji'; - const creationSuccess = await message - .guild!.emojis.create(image, emojiName, { + const creationSuccess = await message.guild.emojis + .create(image, emojiName, { + reason: `Stolen by ${message.author.tag} (${message.author.id})` + }) + .catch((e: Error) => e); + + if (!(creationSuccess instanceof Error)) + return await message.util.reply(`${util.emojis.success} You successfully stole ${creationSuccess}.`); + else { + return await message.util.reply( + `${util.emojis.error} The was an error stealing that emoji \`${creationSuccess.message}\`.` + ); + } + } + + public override async execSlash(message: BushSlashMessage, args: { emoji: Attachment; name?: string }) { + assert(message.inGuild()); + + const name = args.name ?? args.emoji.name ?? 'stolen_emoji'; + + const data = + args.emoji.attachment instanceof Stream + ? await (new Promise((resolve, reject) => { + let data = ''; + assert(args.emoji.attachment instanceof Stream); + args.emoji.attachment.on('data', (chunk) => (data += chunk)); + args.emoji.attachment.on('end', () => resolve(data)); + args.emoji.attachment.on('error', (e) => reject(e)); + }) as Promise<string>) + : args.emoji.attachment; + + const creationSuccess = await message.guild.emojis + .create(data, name, { reason: `Stolen by ${message.author.tag} (${message.author.id})` }) .catch((e: Error) => e); diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 20e7272..cb106dd 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.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 { ApplicationCommandOptionType, ChannelType, EmbedBuilder, Message, PermissionFlagsBits } from 'discord.js'; @@ -67,7 +67,7 @@ export default class ViewRawCommand extends BushCommand { message: BushMessage | BushSlashMessage, args: { message: ArgType<'message'> | ArgType<'messageLink'>; - channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'> | OptionalArgType<'threadChannel'>; + channel: OptArgType<'textChannel'> | OptArgType<'newsChannel'> | OptArgType<'threadChannel'>; json: boolean; js: boolean; } diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index ac6f3c5..5f13c02 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -1,4 +1,4 @@ -import { BushCommand, BushRole, ButtonPaginator, OptionalArgType, type BushMessage, type BushSlashMessage } from '#lib'; +import { BushCommand, BushRole, ButtonPaginator, OptArgType, type BushMessage, type BushSlashMessage } from '#lib'; import assert from 'assert'; import { ApplicationCommandOptionType, Util, type CommandInteraction } from 'discord.js'; @@ -33,7 +33,7 @@ export default class WhoHasRoleCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - [K in `role${NumberRange}`]: OptionalArgType<'role'>; + [K in `role${NumberRange}`]: OptArgType<'role'>; } ) { assert(message.inGuild()); diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 958e451..1797be8 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -284,7 +284,7 @@ interface ExtendedCommandOptions { /** * Use instead of {@link BaseBushCommandOptions.args} when using argument generators or custom slashOptions */ - helpArgs?: BushArgumentOptions[]; + helpArgs?: (Omit<BushArgumentOptions, 'slashType'> & { slashType?: AkairoApplicationCommandOptionData['type'] })[]; /** * Extra information about the command, displayed in the help command. @@ -340,6 +340,23 @@ export interface ArgsInfo { type: string; } +export interface ArgsInfoText { + id: string; + description: string; + optional?: boolean; + only: 'text'; + type: string; +} + +export interface ArgsInfoSlash { + id: string; + description: string; + optional?: boolean; + slashType: AkairoApplicationCommandOptionData['type'] | false; + slashResolve?: SlashResolveType; + only: 'slash'; +} + export class BushCommand extends Command { public declare client: BushClient; @@ -486,7 +503,7 @@ export class BushCommand extends Command { id: arg.id, description: arg.description, optional: arg.optional, - slashType: arg.slashType, + slashType: arg.slashType!, slashResolve: arg.slashResolve, only: arg.only, type: (arg.readableType ?? arg.type) as string @@ -539,4 +556,4 @@ interface PseudoArguments extends BaseBushArgumentType { } export type ArgType<T extends keyof PseudoArguments> = NonNullable<PseudoArguments[T]>; -export type OptionalArgType<T extends keyof PseudoArguments> = PseudoArguments[T]; +export type OptArgType<T extends keyof PseudoArguments> = PseudoArguments[T]; diff --git a/src/lib/extensions/discord.js/other.ts b/src/lib/extensions/discord.js/other.ts index aeba01c..0560ffc 100644 --- a/src/lib/extensions/discord.js/other.ts +++ b/src/lib/extensions/discord.js/other.ts @@ -184,3 +184,5 @@ export type BushGuildCacheMessage<Cached extends CacheType> = CacheTypeReducer< BushMessage | APIMessage, BushMessage | APIMessage >; + +export { ApplicationCommandOptionType as SlashType } from 'discord.js'; |