diff options
60 files changed, 371 insertions, 177 deletions
diff --git a/package.json b/package.json index f8a756f..8d7b80d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,9 @@ "imports": { "#lib": { "default": "./src/lib/index.js" + }, + "#args": { + "default": "./src/arguments/index.js" } }, "scripts": { @@ -100,4 +103,4 @@ "eslint-plugin-deprecation": "^1.3.2" }, "packageManager": "yarn@3.1.1" -} +}
\ No newline at end of file diff --git a/src/arguments/abbreviatedNumber.ts b/src/arguments/abbreviatedNumber.ts index 3a447d7..3027cb2 100644 --- a/src/arguments/abbreviatedNumber.ts +++ b/src/arguments/abbreviatedNumber.ts @@ -1,7 +1,7 @@ import { type BushArgumentTypeCaster } from '#lib'; import numeral from 'numeral'; -export const abbreviatedNumber: BushArgumentTypeCaster = (_, phrase): number | null => { +export const abbreviatedNumber: BushArgumentTypeCaster<number | null> = (_, phrase) => { if (!phrase) return null; const num = numeral(phrase?.toLowerCase()).value(); diff --git a/src/arguments/contentWithDuration.ts b/src/arguments/contentWithDuration.ts index 41cb9bb..3d87061 100644 --- a/src/arguments/contentWithDuration.ts +++ b/src/arguments/contentWithDuration.ts @@ -1,5 +1,5 @@ import { ParsedDuration, type BushArgumentTypeCaster } from '#lib'; -export const contentWithDuration: BushArgumentTypeCaster = async (_, phrase): Promise<ParsedDuration> => { +export const contentWithDuration: BushArgumentTypeCaster<Promise<ParsedDuration>> = async (_, phrase) => { return client.util.parseDuration(phrase); }; diff --git a/src/arguments/discordEmoji.ts b/src/arguments/discordEmoji.ts index a3c531c..d9428e1 100644 --- a/src/arguments/discordEmoji.ts +++ b/src/arguments/discordEmoji.ts @@ -1,7 +1,7 @@ import { type BushArgumentTypeCaster } from '#lib'; import { type Snowflake } from 'discord-api-types'; -export const discordEmoji: BushArgumentTypeCaster = (_, phrase): DiscordEmojiInfo | null => { +export const discordEmoji: BushArgumentTypeCaster<DiscordEmojiInfo | null> = (_, phrase) => { if (!phrase) return null; const validEmoji: RegExpExecArray | null = client.consts.regex.discordEmoji.exec(phrase); if (!validEmoji || !validEmoji.groups) return null; diff --git a/src/arguments/duration.ts b/src/arguments/duration.ts index 9cb1d03..58593ef 100644 --- a/src/arguments/duration.ts +++ b/src/arguments/duration.ts @@ -1,5 +1,5 @@ import { type BushArgumentTypeCaster } from '#lib'; -export const duration: BushArgumentTypeCaster = (_, phrase): number | null => { +export const duration: BushArgumentTypeCaster<number | null> = (_, phrase) => { return client.util.parseDuration(phrase).duration; }; diff --git a/src/arguments/durationSeconds.ts b/src/arguments/durationSeconds.ts index 8cbfa21..74d136f 100644 --- a/src/arguments/durationSeconds.ts +++ b/src/arguments/durationSeconds.ts @@ -1,6 +1,6 @@ import { type BushArgumentTypeCaster } from '#lib'; -export const durationSeconds: BushArgumentTypeCaster = (_, phrase): number | null => { +export const durationSeconds: BushArgumentTypeCaster<number | null> = (_, phrase) => { phrase += 's'; return client.util.parseDuration(phrase).duration; }; diff --git a/src/arguments/globalUser.ts b/src/arguments/globalUser.ts index 081eabf..89e8324 100644 --- a/src/arguments/globalUser.ts +++ b/src/arguments/globalUser.ts @@ -1,7 +1,7 @@ import { BushUser, type BushArgumentTypeCaster } from '#lib'; // resolve non-cached users -export const globalUser: BushArgumentTypeCaster = async (_, phrase): Promise<BushUser | null> => { +export const globalUser: BushArgumentTypeCaster<Promise<BushUser | null>> = async (_, phrase) => { return client.users.cache.has(phrase) ? client.users.cache.get(`${phrase}`) ?? null : await client.users.fetch(`${phrase}`).catch(() => null); diff --git a/src/arguments/index.ts b/src/arguments/index.ts new file mode 100644 index 0000000..eebf0a2 --- /dev/null +++ b/src/arguments/index.ts @@ -0,0 +1,10 @@ +export * from './abbreviatedNumber.js'; +export * from './contentWithDuration.js'; +export * from './discordEmoji.js'; +export * from './duration.js'; +export * from './durationSeconds.js'; +export * from './globalUser.js'; +export * from './messageLink.js'; +export * from './permission.js'; +export * from './roleWithDuration.js'; +export * from './snowflake.js'; diff --git a/src/arguments/messageLink.ts b/src/arguments/messageLink.ts index d270abd..80aacde 100644 --- a/src/arguments/messageLink.ts +++ b/src/arguments/messageLink.ts @@ -1,6 +1,7 @@ +import { Message } from 'discord.js'; import { type BushArgumentTypeCaster } from '../lib'; -export const messageLink: BushArgumentTypeCaster = async (_, phrase) => { +export const messageLink: BushArgumentTypeCaster<Promise<Message | null>> = async (_, phrase) => { const match = client.consts.regex.messageLink.exec(phrase); if (!match || !match.groups) return null; diff --git a/src/arguments/permission.ts b/src/arguments/permission.ts index b5ff4bf..7a43216 100644 --- a/src/arguments/permission.ts +++ b/src/arguments/permission.ts @@ -1,7 +1,7 @@ import { type BushArgumentTypeCaster } from '#lib'; import { Permissions, PermissionString } from 'discord.js'; -export const permission: BushArgumentTypeCaster = (_, phrase): PermissionString | null => { +export const permission: BushArgumentTypeCaster<PermissionString | null> = (_, phrase) => { if (!phrase) return null; phrase = phrase.toUpperCase().replace(/ /g, '_'); if (!(phrase in Permissions.FLAGS)) { diff --git a/src/arguments/roleWithDuration.ts b/src/arguments/roleWithDuration.ts index 999ac1c..b0357dd 100644 --- a/src/arguments/roleWithDuration.ts +++ b/src/arguments/roleWithDuration.ts @@ -1,7 +1,7 @@ import { type BushArgumentTypeCaster } from '#lib'; import { Role } from 'discord.js'; -export const roleWithDuration: BushArgumentTypeCaster = async (message, phrase): Promise<RoleWithDuration | null> => { +export const roleWithDuration: BushArgumentTypeCaster<Promise<RoleWithDuration | null>> = async (message, phrase) => { // eslint-disable-next-line prefer-const let { duration, contentWithoutTime } = client.util.parseDuration(phrase); if (contentWithoutTime === null || contentWithoutTime === undefined) return null; diff --git a/src/arguments/snowflake.ts b/src/arguments/snowflake.ts index 455ed63..15896ae 100644 --- a/src/arguments/snowflake.ts +++ b/src/arguments/snowflake.ts @@ -1,7 +1,7 @@ import { type BushArgumentTypeCaster } from '#lib'; import { type Snowflake } from 'discord.js'; -export const snowflake: BushArgumentTypeCaster = (_, phrase): Snowflake | null => { +export const snowflake: BushArgumentTypeCaster<Snowflake | null> = (_, phrase) => { if (!phrase) return null; if (client.consts.regex.snowflake.test(phrase)) return phrase; return null; diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts index 17bea40..b94094c 100644 --- a/src/commands/admin/channelPermissions.ts +++ b/src/commands/admin/channelPermissions.ts @@ -1,5 +1,5 @@ -import { BushCommand, ButtonPaginator, type BushMessage, type BushSlashMessage } from '#lib'; -import { MessageEmbed, type GuildMember, type PermissionString, type Role } from 'discord.js'; +import { ArgType, BushCommand, ButtonPaginator, type BushMessage, type BushSlashMessage } from '#lib'; +import { MessageEmbed } from 'discord.js'; export default class ChannelPermissionsCommand extends BushCommand { public constructor() { @@ -57,8 +57,8 @@ export default class ChannelPermissionsCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - target: Role | GuildMember; - permission: PermissionString; + target: ArgType<'member'> | ArgType<'role'>; + permission: ArgType<'permission'>; state: 'true' | 'false' | 'neutral'; } ) { diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts index 585e6cc..a946e33 100644 --- a/src/commands/admin/roleAll.ts +++ b/src/commands/admin/roleAll.ts @@ -1,5 +1,5 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { type GuildMember, type Role } from 'discord.js'; +import { AllowedMentions, ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { type GuildMember } from 'discord.js'; export default class RoleAllCommand extends BushCommand { public constructor() { @@ -37,7 +37,7 @@ export default class RoleAllCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { role: Role; bots: boolean }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'>; bots: boolean }) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`); if (!message.member!.permissions.has('ADMINISTRATOR') && !message.member!.user.isOwner()) return await message.util.reply(`${util.emojis.error} You must have admin perms to use this command.`); diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index a6e6a3d..d119774 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -1,5 +1,5 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { GuildTextBasedChannel, User } from 'discord.js'; +import { AllowedMentions, ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { User } from 'discord.js'; export default class BlacklistCommand extends BushCommand { public constructor() { @@ -51,7 +51,7 @@ export default class BlacklistCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { action?: 'blacklist' | 'unblacklist'; target: GuildTextBasedChannel | User | string; global: boolean } + args: { action?: 'blacklist' | 'unblacklist'; target: ArgType<'channel'> | ArgType<'user'> | string; global: boolean } ) { let action: 'blacklist' | 'unblacklist' | 'toggle' = args.action ?? (message?.util?.parsed?.alias as 'blacklist' | 'unblacklist' | undefined) ?? 'toggle'; diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts index b88147d..9377dd0 100644 --- a/src/commands/config/config.ts +++ b/src/commands/config/config.ts @@ -1,4 +1,12 @@ -import { BushCommand, guildSettingsObj, settingsArr, type BushMessage, type BushSlashMessage, type GuildSettings } from '#lib'; +import { + ArgType, + BushCommand, + guildSettingsObj, + settingsArr, + type BushMessage, + type BushSlashMessage, + type GuildSettings +} from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; import { Channel, @@ -172,7 +180,7 @@ export default class SettingsCommand extends BushCommand { subcommandGroup?: GuildSettings; action?: Action; subcommand?: Action; - value: string | Channel | Role; + value: ArgType<'channel'> | ArgType<'role'> | string; } ) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`); diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts index 44c28d3..f6bce3a 100644 --- a/src/commands/config/disable.ts +++ b/src/commands/config/disable.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; export default class DisableCommand extends BushCommand { private static blacklistedCommands = ['eval', 'disable']; @@ -49,7 +49,7 @@ export default class DisableCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { action?: 'enable' | 'disable'; command: BushCommand | string; global: boolean } + args: { action?: 'enable' | 'disable'; command: ArgType<'commandAlias'> | string; global: boolean } ) { let action = (args.action ?? message?.util?.parsed?.alias ?? 'toggle') as 'disable' | 'enable' | 'toggle'; const global = args.global && message.author.isOwner(); diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts index 52cb8f5..c61a6d5 100644 --- a/src/commands/config/log.ts +++ b/src/commands/config/log.ts @@ -1,6 +1,5 @@ -import { BushCommand, guildLogsArr, type BushMessage, type BushSlashMessage, type GuildLogType } from '#lib'; +import { ArgType, BushCommand, guildLogsArr, type BushMessage, type BushSlashMessage, type GuildLogType } from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; -import { type TextChannel } from 'discord.js'; export default class LogCommand extends BushCommand { public constructor() { @@ -62,7 +61,10 @@ export default class LogCommand extends BushCommand { return { log_type, channel }; } - public override async exec(message: BushMessage | BushSlashMessage, args: { log_type: GuildLogType; channel: TextChannel }) { + public override async exec( + message: BushMessage | BushSlashMessage, + args: { log_type: GuildLogType; channel: ArgType<'textChannel'> } + ) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`); const currentLogs = await message.guild.getSetting('logChannels'); const oldChannel = currentLogs[args.log_type] ?? undefined; diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts index 5bb29c8..4341198 100644 --- a/src/commands/dev/__template.ts +++ b/src/commands/dev/__template.ts @@ -36,6 +36,7 @@ export default class TemplateCommand extends BushCommand { userPermissions: [] }); } + public override async exec( message: BushMessage | BushSlashMessage, args: { required_argument: string; optional_argument: string } diff --git a/src/commands/dev/dm.ts b/src/commands/dev/dm.ts index 5031dfd..4133b96 100644 --- a/src/commands/dev/dm.ts +++ b/src/commands/dev/dm.ts @@ -1,4 +1,4 @@ -import { BushCommand, BushUser, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; export default class DMCommand extends BushCommand { public constructor() { @@ -11,7 +11,7 @@ export default class DMCommand extends BushCommand { args: [ { id: 'user', - type: 'string', + type: 'user', description: 'The user to send the dm to.', prompt: 'Who would you like to dm?', retry: '{error} Pick a valid user to send a dm to.', @@ -20,6 +20,7 @@ export default class DMCommand extends BushCommand { { id: 'content', type: 'string', + match: 'rest', description: 'This is the second argument.', prompt: 'What would you like to set your second argument to be?', retry: '{error} Pick a valid argument.', @@ -34,7 +35,7 @@ export default class DMCommand extends BushCommand { userPermissions: [] }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: BushUser; content: string }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'>; content: string }) { try { const u = await client.users.fetch(args.user.id); await u.send(args.content); diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 2393a9b..f97f00d 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { exec } from 'child_process'; import { MessageEmbed as _MessageEmbed } from 'discord.js'; import ts from 'typescript'; @@ -119,7 +119,7 @@ export default class EvalCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - sel_depth: number; + sel_depth: ArgType<'integer'>; code: string; sudo: boolean; silent: boolean; diff --git a/src/commands/dev/javascript.ts b/src/commands/dev/javascript.ts index d832c09..2a6a87b 100644 --- a/src/commands/dev/javascript.ts +++ b/src/commands/dev/javascript.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { MessageEmbed } from 'discord.js'; import { VM } from 'vm2'; @@ -51,7 +51,7 @@ export default class JavascriptCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - sel_depth: number; + sel_depth: ArgType<'integer'>; code: string; } ) { diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index f38419f..35e98aa 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,6 +1,5 @@ -import { BushCommand, Global, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, Global, type BushMessage, type BushSlashMessage } from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; -import { type User } from 'discord.js'; export default class SuperUserCommand extends BushCommand { public constructor() { @@ -56,7 +55,7 @@ export default class SuperUserCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { action, user }: { action: 'add' | 'remove'; user: User } + { action, user }: { action: 'add' | 'remove'; user: ArgType<'user'> } ) { if (!message.author.isOwner()) return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`); diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts index 16352ce..0b6c89c 100644 --- a/src/commands/fun/minesweeper.ts +++ b/src/commands/fun/minesweeper.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { Minesweeper } from '@notenoughupdates/discord.js-minesweeper'; export default class MinesweeperCommand extends BushCommand { @@ -67,7 +67,13 @@ export default class MinesweeperCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { rows: number; columns: number; mines: number; spaces: boolean; reveal_first_cell: boolean } + args: { + rows: ArgType<'integer'>; + columns: ArgType<'integer'>; + mines: ArgType<'integer'>; + spaces: boolean; + reveal_first_cell: boolean; + } ) { const minesweeper = new Minesweeper({ rows: args.rows ?? 9, diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index 36504f8..3436b87 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -1,5 +1,5 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { GuildMember, MessageEmbed, type User } from 'discord.js'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { GuildMember, MessageEmbed } from 'discord.js'; export default class AvatarCommand extends BushCommand { constructor() { @@ -27,7 +27,7 @@ export default class AvatarCommand extends BushCommand { }); } - override async exec(message: BushMessage | BushSlashMessage, args: { user: GuildMember | User }) { + override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'member'> | ArgType<'globalUser'> }) { const params: { size: 2048; format: 'png'; dynamic: true } = { size: 2048, format: 'png', dynamic: true }; const defaultAvatar = `https://cdn.discordapp.com/embed/avatars/${Math.ceil(Math.random() * 6) - 1}.png`; diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts index 4277d56..2f9751b 100644 --- a/src/commands/info/color.ts +++ b/src/commands/info/color.ts @@ -1,5 +1,6 @@ import { AllowedMentions, + ArgType, BushArgumentTypeCaster, BushCommand, type BushGuildMember, @@ -46,7 +47,10 @@ export default class ColorCommand extends BushCommand { return color.substring(4, color.length - 5); } - public override async exec(message: BushMessage | BushSlashMessage, args: { color: string | BushRole | BushGuildMember }) { + public override async exec( + message: BushMessage | BushSlashMessage, + args: { color: string | ArgType<'role'> | ArgType<'member'> } + ) { const _color = message.util.isSlashMessage(message) ? ((await util.arg.cast(util.arg.union(isValidTinyColor as any, 'role', 'member'), message, args.color as string)) as | string diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts index ab09741..67150f6 100644 --- a/src/commands/info/guildInfo.ts +++ b/src/commands/info/guildInfo.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { Constants, Guild, @@ -35,7 +35,7 @@ export default class GuildInfoCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { guild: Guild | Snowflake | GuildPreview }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { guild: ArgType<'guild'> | ArgType<'snowflake'> }) { if (!args?.guild && !message.guild) { return await message.util.reply( `${util.emojis.error} You must either provide an server to provide info about or run this command in a server.` @@ -43,16 +43,17 @@ export default class GuildInfoCommand extends BushCommand { } const otherEmojis = client.consts.mappings.otherEmojis; let isPreview = false; + let _guild: ArgType<'guild'> | ArgType<'snowflake'> | GuildPreview = args.guild; if (['number', 'string'].includes(typeof args?.guild)) { const preview = await client.fetchGuildPreview(`${args.guild}` as Snowflake).catch(() => {}); if (preview) { - args.guild = preview; + _guild = preview; isPreview = true; } else { return await message.util.reply(`${util.emojis.error} That guild is not discoverable or does not exist.`); } } - const guild: Guild | GuildPreview = (args?.guild as Guild | GuildPreview) || (message.guild as Guild); + const guild: Guild | GuildPreview = (_guild as Guild | GuildPreview) || (_guild as Guild); const emojis: string[] = []; const guildAbout: string[] = []; const guildStats: string[] = []; diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 4999446..277017e 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -1,4 +1,4 @@ -import { BushCommand, BushMessage, BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, BushMessage, BushSlashMessage } from '#lib'; import { MessageActionRow, MessageButton, MessageEmbed } from 'discord.js'; import packageDotJSON from '../../../package.json' assert { type: 'json' }; @@ -40,7 +40,7 @@ export default class HelpCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { command: BushCommand | string; showHidden?: boolean } + args: { command: ArgType<'commandAlias'> | string; showHidden?: boolean } ) { const prefix = util.prefix(message); const row = this.addLinks(message); diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index 29cd2ce..3dacede 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -1,5 +1,5 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { MessageEmbed, type User } from 'discord.js'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { MessageEmbed } from 'discord.js'; export default class PronounsCommand extends BushCommand { public constructor() { @@ -25,7 +25,8 @@ export default class PronounsCommand extends BushCommand { slash: true }); } - override async exec(message: BushMessage | BushSlashMessage, args: { user?: User }) { + + override async exec(message: BushMessage | BushSlashMessage, args: { user?: ArgType<'globalUser'> }) { const user = args.user ?? message.author; const author = user.id === message.author.id; diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts index f2ffaa8..1658da9 100644 --- a/src/commands/info/snowflake.ts +++ b/src/commands/info/snowflake.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { MessageEmbed, SnowflakeUtil, @@ -39,7 +39,8 @@ export default class SnowflakeCommand extends BushCommand { slash: true }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { snowflake: Snowflake }) { + + public override async exec(message: BushMessage | BushSlashMessage, args: { snowflake: ArgType<'snowflake'> }) { const snowflake = `${args.snowflake}` as Snowflake; const snowflakeEmbed = new MessageEmbed().setTitle('Unknown :snowflake:').setColor(util.colors.default); diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts index 98ead3d..9c7a2a7 100644 --- a/src/commands/info/userInfo.ts +++ b/src/commands/info/userInfo.ts @@ -1,5 +1,5 @@ -import { BushCommand, BushGuild, BushGuildMember, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; -import { MessageEmbed, type Snowflake } from 'discord.js'; +import { ArgType, BushCommand, BushGuild, BushGuildMember, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; +import { MessageEmbed } from 'discord.js'; // TODO: Add bot information export default class UserInfoCommand extends BushCommand { @@ -28,7 +28,7 @@ export default class UserInfoCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user: BushUser | Snowflake }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { user: ArgType<'user'> | ArgType<'snowflake'> }) { const user = args?.user === undefined || args?.user === null ? message.author diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts index 6bf66d1..d53d358 100644 --- a/src/commands/leveling/leaderboard.ts +++ b/src/commands/leveling/leaderboard.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, Level, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, ButtonPaginator, Level, type BushMessage, type BushSlashMessage } from '#lib'; import { MessageEmbed } from 'discord.js'; export default class LeaderboardCommand extends BushCommand { @@ -27,7 +27,7 @@ export default class LeaderboardCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { page: number }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { page: ArgType<'integer'> }) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`); if (!(await message.guild.hasFeature('leveling'))) return await message.util.reply( diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts index 61e5d0b..ed8466d 100644 --- a/src/commands/leveling/level.ts +++ b/src/commands/leveling/level.ts @@ -1,5 +1,6 @@ import { AllowedMentions, + ArgType, BushCommand, CanvasProgressBar, Level, @@ -41,7 +42,7 @@ export default class LevelCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { user?: BushUser }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { user?: ArgType<'user'> }) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a server.`); if (!(await message.guild.hasFeature('leveling'))) return await message.util.reply( diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts index 69f562d..5118a8b 100644 --- a/src/commands/leveling/setLevel.ts +++ b/src/commands/leveling/setLevel.ts @@ -1,5 +1,4 @@ -import { AllowedMentions, BushCommand, Level, type BushMessage, type BushSlashMessage } from '#lib'; -import { type User } from 'discord.js'; +import { AllowedMentions, ArgType, BushCommand, Level, type BushMessage, type BushSlashMessage } from '#lib'; export default class SetLevelCommand extends BushCommand { public constructor() { @@ -34,7 +33,10 @@ export default class SetLevelCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { user, level }: { user: User; level: number }) { + public override async exec( + message: BushMessage | BushSlashMessage, + { user, level }: { user: ArgType<'user'>; level: ArgType<'integer'> } + ) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a guild.`); if (!user.id) throw new Error('user.id is null'); diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts index 68be528..eeafff3 100644 --- a/src/commands/leveling/setXp.ts +++ b/src/commands/leveling/setXp.ts @@ -1,5 +1,4 @@ -import { AllowedMentions, BushCommand, Level, type BushMessage, type BushSlashMessage } from '#lib'; -import { type User } from 'discord.js'; +import { AllowedMentions, ArgType, BushCommand, Level, type BushMessage, type BushSlashMessage } from '#lib'; export default class SetXpCommand extends BushCommand { public constructor() { @@ -35,7 +34,10 @@ export default class SetXpCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { user, xp }: { user: User; xp: number }) { + public override async exec( + message: BushMessage | BushSlashMessage, + { user, xp }: { user: ArgType<'user'>; xp: ArgType<'abbreviatedNumber'> } + ) { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be run in a guild.`); if (!user.id) throw new Error('user.id is null'); diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 506a7c3..c0375e0 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,5 +1,13 @@ -import { AllowedMentions, BushCommand, Moderation, type BushMessage, type BushSlashMessage } from '#lib'; -import { type Snowflake, type User } from 'discord.js'; +import { + AllowedMentions, + ArgType, + BushCommand, + Moderation, + OptionalArgType, + type BushMessage, + type BushSlashMessage +} from '#lib'; +import { type User } from 'discord.js'; export default class BanCommand extends BushCommand { public constructor() { @@ -61,9 +69,9 @@ export default class BanCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, args: { - user: User | Snowflake; - reason: { duration: number | null; contentWithoutTime: string } | null; - days: number | null; + user: ArgType<'user'> | ArgType<'snowflake'>; + reason: OptionalArgType<'contentWithDuration'>; + days: OptionalArgType<'integer'>; force: boolean; } ) { diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 9463154..b59b9f9 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Moderation, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, Moderation, type BushMessage, type BushSlashMessage } from '#lib'; export default class KickCommand extends BushCommand { public constructor() { @@ -46,7 +46,7 @@ export default class KickCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason, force }: { user: BushUser; reason?: string; force: boolean } + { user, reason, force }: { user: ArgType<'user'>; reason: ArgType<'string'>; force: boolean } ) { const member = await message.guild!.members.fetch(user.id); diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 0f2d33c..be6e6b4 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -1,4 +1,4 @@ -import { BushCommand, ButtonPaginator, ModLog, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; +import { ArgType, BushCommand, ButtonPaginator, ModLog, type BushMessage, type BushSlashMessage } from '#lib'; import { MessageEmbed, User } from 'discord.js'; export default class ModlogCommand extends BushCommand { @@ -35,21 +35,9 @@ export default class ModlogCommand extends BushCommand { }); } - static generateModlogInfo(log: ModLog, showUser: boolean): string { - const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim()); - const modLog = [`**Case ID**: ${util.discord.escapeMarkdown(log.id)}`, `**Type**: ${log.type.toLowerCase()}`]; - if (showUser) modLog.push(`**User**: <@!${log.user}>`); - modLog.push(`**Moderator**: <@!${log.moderator}>`); - if (log.duration) modLog.push(`**Duration**: ${util.humanizeDuration(log.duration)}`); - modLog.push(`**Reason**: ${trim(log.reason ?? 'No Reason Specified.')}`); - modLog.push(`**Date**: ${util.timestamp(log.createdAt)}`); - if (log.evidence) modLog.push(`**Evidence:** ${trim(log.evidence)}`); - return modLog.join(`\n`); - } - public override async exec( message: BushMessage | BushSlashMessage, - { search, hidden }: { search: BushUser | string; hidden: boolean } + { search, hidden }: { search: ArgType<'user'> | string; hidden: boolean } ) { const foundUser = search instanceof User ? search : await util.resolveUserAsync(search); if (foundUser) { @@ -90,4 +78,16 @@ export default class ModlogCommand extends BushCommand { return await ButtonPaginator.send(message, [embed]); } } + + public static generateModlogInfo(log: ModLog, showUser: boolean): string { + const trim = (str: string): string => (str.endsWith('\n') ? str.substring(0, str.length - 1).trim() : str.trim()); + const modLog = [`**Case ID**: ${util.discord.escapeMarkdown(log.id)}`, `**Type**: ${log.type.toLowerCase()}`]; + if (showUser) modLog.push(`**User**: <@!${log.user}>`); + modLog.push(`**Moderator**: <@!${log.moderator}>`); + if (log.duration) modLog.push(`**Duration**: ${util.humanizeDuration(log.duration)}`); + modLog.push(`**Reason**: ${trim(log.reason ?? 'No Reason Specified.')}`); + modLog.push(`**Date**: ${util.timestamp(log.createdAt)}`); + if (log.evidence) modLog.push(`**Evidence:** ${trim(log.evidence)}`); + return modLog.join(`\n`); + } } diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index c7091b3..2c0a1e8 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, Moderation, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, Moderation, type BushMessage, type BushSlashMessage } from '#lib'; export default class MuteCommand extends BushCommand { public constructor() { @@ -47,7 +47,7 @@ export default class MuteCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { user: BushUser; reason?: { duration: number | null; contentWithoutTime: string } | string; force: boolean } + args: { user: ArgType<'user'>; reason?: ArgType<'contentWithDuration'> | string; force: boolean } ) { const reason: { duration: number | null; contentWithoutTime: string | null } = args.reason ? typeof args.reason === 'string' diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts index f039046..7a25a0d 100644 --- a/src/commands/moderation/purge.ts +++ b/src/commands/moderation/purge.ts @@ -1,4 +1,4 @@ -import { BushCommand, BushMessage, BushUser } from '#lib'; +import { ArgType, BushCommand, BushMessage } from '#lib'; import { Collection, type Snowflake } from 'discord.js'; export default class PurgeCommand extends BushCommand { @@ -47,7 +47,7 @@ export default class PurgeCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { amount: number; bot: boolean; user: BushUser }) { + public override async exec(message: BushMessage, args: { amount: number; bot: boolean; user: ArgType<'user'> }) { if (message.channel.type === 'DM') return message.util.reply(`${util.emojis.error} You cannot run this command in dms.`); if (args.amount > 100 || args.amount < 1) return message.util.reply(`${util.emojis.error} `); diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts index 4ada9d5..bede2cf 100644 --- a/src/commands/moderation/removeReactionEmoji.ts +++ b/src/commands/moderation/removeReactionEmoji.ts @@ -1,5 +1,5 @@ -import { BushCommand, type BushMessage } from '#lib'; -import { Message, type Emoji, type Snowflake } from 'discord.js'; +import { ArgType, BushCommand, type BushMessage } from '#lib'; +import { Message, type Emoji } from 'discord.js'; export default class RemoveReactionEmojiCommand extends BushCommand { public constructor() { @@ -36,7 +36,10 @@ export default class RemoveReactionEmojiCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { message: BushMessage | Snowflake; emoji: Emoji | Snowflake }) { + public override async exec( + message: BushMessage, + args: { message: ArgType<'guildMessage'> | string; emoji: ArgType<'emoji'> | ArgType<'snowflake'> } + ) { const resolvedMessage = args.message instanceof Message ? args.message : await message.channel.messages.fetch(args.message); const id = !(['string'] as const).includes(typeof args.emoji); diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 689ef1e..651762b 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushGuildMember, type BushMessage, type BushRole, type BushSlashMessage } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, OptionalArgType, type BushMessage, type BushSlashMessage } from '#lib'; import { type ArgumentOptions, type Flag } from 'discord-akairo'; import { Snowflake } from 'discord.js'; @@ -98,12 +98,24 @@ export default class RoleCommand extends BushCommand { match: 'flag' }; - return { action, member: member, role: (_role as any).role ?? _role, duration: (_role as any).duration, force }; + return { + action, + member: member, + role: (_role as ArgType<'roleWithDuration'>).role ?? _role, + duration: (_role as ArgType<'roleWithDuration'>).duration, + force + }; } public override async exec( message: BushMessage | BushSlashMessage, - args: { action: 'add' | 'remove'; member: BushGuildMember; role: BushRole; duration?: number | null; force?: boolean } + args: { + action: 'add' | 'remove'; + member: ArgType<'member'>; + role: ArgType<'role'>; + duration?: OptionalArgType<'duration'>; + force?: boolean; + } ) { if (!args.role) return await message.util.reply(`${util.emojis.error} You must specify a role.`); if (args.duration === null) args.duration = 0; diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index f4ab822..d38b266 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -1,13 +1,6 @@ -import { - BushCommand, - type BushMessage, - type BushNewsChannel, - type BushSlashMessage, - type BushTextChannel, - type BushThreadChannel -} from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { Argument } from 'discord-akairo'; -import { TextChannel, ThreadChannel, type NewsChannel } from 'discord.js'; +import { TextChannel, ThreadChannel } from 'discord.js'; export default class SlowModeCommand extends BushCommand { public constructor() { @@ -52,8 +45,8 @@ export default class SlowModeCommand extends BushCommand { length, channel }: { - length: number | 'off' | 'none' | 'disable' | null; - channel: TextChannel | ThreadChannel | BushTextChannel | BushNewsChannel | BushThreadChannel | NewsChannel; + length: ArgType<'duration'> | ArgType<'durationSeconds'> | 'off' | 'none' | 'disable' | null; + channel: ArgType<'channel'>; } ) { if (message.channel!.type === 'DM') diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts index 8444801..dbbb9a5 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/unban.ts @@ -1,4 +1,4 @@ -import { AllowedMentions, BushCommand, type BushMessage, type BushSlashMessage, type BushUser } from '#lib'; +import { AllowedMentions, ArgType, BushCommand, OptionalArgType, type BushMessage, type BushSlashMessage } from '#lib'; export default class UnbanCommand extends BushCommand { public constructor() { @@ -34,7 +34,10 @@ export default class UnbanCommand extends BushCommand { userPermissions: ['BAN_MEMBERS'] }); } - public override async exec(message: BushMessage | BushSlashMessage, { user, reason }: { user: BushUser; reason?: string }) { + public override async exec( + message: BushMessage | BushSlashMessage, + { user, reason }: { user: ArgType<'user'>; reason: OptionalArgType<'string'> } + ) { const responseCode = await message.guild!.bushUnban({ user, moderator: message.author, diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/unmute.ts index 0c59b1f..d36a5db 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/unmute.ts @@ -1,11 +1,12 @@ import { AllowedMentions, + ArgType, BushCommand, Moderation, + OptionalArgType, type BushGuildMember, type BushMessage, - type BushSlashMessage, - type BushUser + type BushSlashMessage } from '#lib'; export default class UnmuteCommand extends BushCommand { @@ -55,7 +56,7 @@ export default class UnmuteCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason, force }: { user: BushUser; reason?: string; force: boolean } + { user, reason, force }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force: boolean } ) { const error = util.emojis.error; const member = message.guild!.members.cache.get(user.id) as BushGuildMember; diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 2212548..95e409d 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -1,11 +1,12 @@ import { AllowedMentions, + ArgType, BushCommand, Moderation, + OptionalArgType, type BushGuildMember, type BushMessage, - type BushSlashMessage, - type BushUser + type BushSlashMessage } from '#lib'; export default class WarnCommand extends BushCommand { @@ -54,7 +55,7 @@ export default class WarnCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - { user, reason, force }: { user: BushUser; reason: string; force: boolean } + { user, reason, force }: { user: ArgType<'user'>; reason: OptionalArgType<'string'>; force?: boolean } ) { const member = message.guild!.members.cache.get(user.id) as BushGuildMember; if (!member) return message.util.reply(`${util.emojis.error} I cannot warn users that are not in the server.`); diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts index 2749430..8dd51fe 100644 --- a/src/commands/moulberry-bush/capePerms.ts +++ b/src/commands/moulberry-bush/capePerms.ts @@ -1,4 +1,4 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; import { MessageEmbed } from 'discord.js'; import got from 'got'; @@ -27,7 +27,7 @@ export default class CapePermissionsCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { ign: string }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { ign: ArgType<'string'> }) { interface CapePerms { success: boolean; perms: User[]; diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts index 1734568..fe18d89 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 } from '#lib'; +import { BushCommand, ButtonPaginator, DeleteButton, OptionalArgType, type BushMessage } from '#lib'; import { type MessageEmbedOptions } from 'discord.js'; import got from 'got'; @@ -28,7 +28,7 @@ export default class CapesCommand extends BushCommand { }); } - public override async exec(message: BushMessage, args: { cape: string | null }) { + public override async exec(message: BushMessage, args: { cape: OptionalArgType<'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/moulHammer.ts b/src/commands/moulberry-bush/moulHammer.ts index 2811f80..ffb9afb 100644 --- a/src/commands/moulberry-bush/moulHammer.ts +++ b/src/commands/moulberry-bush/moulHammer.ts @@ -1,5 +1,5 @@ -import { BushCommand, BushSlashMessage, type BushMessage } from '#lib'; -import { MessageEmbed, type User } from 'discord.js'; +import { ArgType, BushCommand, BushSlashMessage, type BushMessage } from '#lib'; +import { MessageEmbed } from 'discord.js'; export default class MoulHammerCommand extends BushCommand { public constructor() { @@ -27,7 +27,7 @@ export default class MoulHammerCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, { user }: { user: User }) { + public override async exec(message: BushMessage | BushSlashMessage, { user }: { user: ArgType<'user'> }) { await message.delete(); const embed = new MessageEmbed() .setTitle('L') diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts index 2a0d06f..a65cfcf 100644 --- a/src/commands/moulberry-bush/report.ts +++ b/src/commands/moulberry-bush/report.ts @@ -1,5 +1,5 @@ -import { AllowedMentions, BushCommand, type BushMessage } from '#lib'; -import { MessageEmbed, type GuildMember } from 'discord.js'; +import { AllowedMentions, ArgType, BushCommand, type BushMessage } from '#lib'; +import { MessageEmbed } from 'discord.js'; import moment from 'moment'; export default class ReportCommand extends BushCommand { @@ -37,7 +37,7 @@ export default class ReportCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { member, evidence }: { member: GuildMember; evidence: string }) { + public override async exec(message: BushMessage, { member, evidence }: { member: ArgType<'member'>; evidence: string }) { if (!message.guild || !(await message.guild.hasFeature('reporting'))) return await message.util.reply( `${util.emojis.error} This command can only be used in servers where reporting is enabled.` diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index 2404c4d..cc59db5 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,5 +1,5 @@ -import { AllowedMentions, BushCommand, type BushMessage } from '#lib'; -import { MessageEmbed, type User } from 'discord.js'; +import { AllowedMentions, BushCommand, OptionalArgType, type BushMessage } from '#lib'; +import { MessageEmbed } from 'discord.js'; const rules = [ { @@ -90,14 +90,17 @@ export default class RuleCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { rule, user }: { rule: undefined | number; user: User }) { + public override async exec( + message: BushMessage, + { rule, user }: { rule: OptionalArgType<'integer'>; user: OptionalArgType<'user'> } + ) { const rulesEmbed = new MessageEmbed() .setColor('#ef3929') .setFooter(`Triggered by ${message.author.tag}`, message.author.avatarURL({ dynamic: true }) ?? undefined) .setTimestamp(); - if (rule !== undefined && (rule > 12 || rule < 1)) { - rule = undefined; + if (rule != null && (rule > 12 || rule < 1)) { + rule = null; } if (rule) { if (rules[rule - 1]?.title && rules[rule - 1]?.description) diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts index 2ab56cc..fe21aec 100644 --- a/src/commands/utilities/activity.ts +++ b/src/commands/utilities/activity.ts @@ -1,5 +1,5 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { DiscordAPIError, Message, VoiceChannel } from 'discord.js'; +import { ArgType, BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; +import { DiscordAPIError, Message } from 'discord.js'; const activityMap = { 'Poker Night': { @@ -131,7 +131,7 @@ export default class YouTubeCommand extends BushCommand { }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { channel: VoiceChannel; activity: string }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { channel: ArgType<'channel'>; activity: string }) { const channel = typeof args.channel === 'string' ? message.guild?.channels.cache.get(args.channel) : args.channel; if (!channel || channel.type !== 'GUILD_VOICE') return await message.util.reply(`${util.emojis.error} Choose a valid voice channel`); diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts index 6a15d71..480201a 100644 --- a/src/commands/utilities/steal.ts +++ b/src/commands/utilities/steal.ts @@ -1,7 +1,7 @@ -import { BushCommand, BushSlashMessage, type BushMessage } from '#lib'; +import { ArgType, BushCommand, BushSlashMessage, type BushMessage } from '#lib'; import { ArgumentOptions, ArgumentType, ArgumentTypeCaster, Flag } from 'discord-akairo'; -import { type Snowflake } from 'discord.js'; import _ from 'lodash'; +import { URL } from 'url'; export default class StealCommand extends BushCommand { public constructor() { @@ -72,7 +72,7 @@ export default class StealCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args?: { emoji?: { name: string; id: Snowflake } | Snowflake | URL | string; name: string } + args?: { emoji?: ArgType<'discordEmoji'> | ArgType<'snowflake'> | ArgType<'url'> | string; name: string } ) { if (!args || !args.emoji) return await message.util.reply(`${util.emojis.error} You must provide an emoji to steal.`); diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts index e0f6b1c..6195edd 100644 --- a/src/commands/utilities/uuid.ts +++ b/src/commands/utilities/uuid.ts @@ -34,7 +34,10 @@ export default class UuidCommand extends BushCommand { }); } - public override async exec(message: BushMessage, { ign, dashed }: { ign: { match: any[]; matches: any[] }; dashed: boolean }) { + public override async exec( + message: BushMessage, + { ign, dashed }: { ign: { match: RegExpMatchArray; matches: any[] }; dashed: boolean } + ) { if (!ign) return await message.util.reply(`${util.emojis.error} Please enter a valid ign.`); const readableIGN = ign.match[0]; try { diff --git a/src/commands/utilities/viewRaw.ts b/src/commands/utilities/viewRaw.ts index 0809a39..eeec4d9 100644 --- a/src/commands/utilities/viewRaw.ts +++ b/src/commands/utilities/viewRaw.ts @@ -1,5 +1,13 @@ -import { BushCommand, type BushMessage, type BushSlashMessage } from '#lib'; -import { Message, MessageEmbed, type DMChannel, type NewsChannel, type Snowflake, type TextChannel } from 'discord.js'; +import { + ArgType, + BushCommand, + BushNewsChannel, + BushTextChannel, + OptionalArgType, + type BushMessage, + type BushSlashMessage +} from '#lib'; +import { Message, MessageEmbed, type Snowflake } from 'discord.js'; export default class ViewRawCommand extends BushCommand { public constructor() { @@ -22,7 +30,7 @@ export default class ViewRawCommand extends BushCommand { { id: 'channel', description: 'The channel that the message is in.', - type: 'channel', + type: util.arg.union('textChannel', 'newsChannel'), prompt: 'What channel is the message in?', retry: '{error} Choose a valid channel.', optional: true, @@ -57,9 +65,14 @@ export default class ViewRawCommand extends BushCommand { public override async exec( message: BushMessage | BushSlashMessage, - args: { message: BushMessage | Snowflake; channel: TextChannel | NewsChannel | DMChannel; json?: boolean; js: boolean } + args: { + message: ArgType<'guildMessage'> | ArgType<'messageLink'>; + channel: OptionalArgType<'textChannel'> | OptionalArgType<'newsChannel'>; + json: boolean; + js: boolean; + } ) { - if (!args.channel) args.channel = (message.channel as TextChannel | NewsChannel | DMChannel)!; + if (!args.channel) args.channel = (message.channel as BushTextChannel | BushNewsChannel)!; const newMessage = args.message instanceof Message ? args.message diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts index 45cf77f..ffb7ce1 100644 --- a/src/commands/utilities/whoHasRole.ts +++ b/src/commands/utilities/whoHasRole.ts @@ -1,5 +1,5 @@ -import { BushCommand, ButtonPaginator, type BushMessage, type BushSlashMessage } from '#lib'; -import { Util, type CommandInteraction, type Role } from 'discord.js'; +import { ArgType, BushCommand, ButtonPaginator, type BushMessage, type BushSlashMessage } from '#lib'; +import { Util, type CommandInteraction } from 'discord.js'; export default class WhoHasRoleCommand extends BushCommand { public constructor() { @@ -27,7 +27,7 @@ export default class WhoHasRoleCommand extends BushCommand { typing: true }); } - public override async exec(message: BushMessage | BushSlashMessage, args: { role: Role }) { + public override async exec(message: BushMessage | BushSlashMessage, args: { role: ArgType<'role'> }) { if (message.util.isSlash) await (message.interaction as CommandInteraction).deferReply(); const roleMembers = args.role.members.map((member) => `${member.user} (${Util.escapeMarkdown(member.user.tag)})`); diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 5ae2ac0..f44c80d 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -513,7 +513,7 @@ export class BushClientUtil extends ClientUtil { if (!content) return { duration: 0, contentWithoutTime: null }; // eslint-disable-next-line prefer-const - let duration = null; + let duration: number | null = null; // Try to reduce false positives by requiring a space before the duration, this makes sure it still matches if it is // in the beginning of the argument let contentWithoutTime = ` ${content}`; @@ -522,8 +522,7 @@ export class BushClientUtil extends ClientUtil { const regex = BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].match; const match = regex.exec(contentWithoutTime); const value = Number(match?.groups?.[unit]); - if (!isNaN(value)) - (duration as unknown as number) += value * BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].value; + if (!isNaN(value)) duration! += value * BushConstants.TimeUnits[unit as keyof typeof BushConstants.TimeUnits].value; if (remove) contentWithoutTime = contentWithoutTime.replace(regex, ''); } diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 6b54e20..0d0a0a8 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -1,34 +1,109 @@ +import { type DiscordEmojiInfo, type RoleWithDuration } from '#args'; import { - BushArgumentTypeCaster, - BushUser, - ParsedDuration, + type BushArgumentTypeCaster, + type BushBaseGuildVoiceChannel, + type BushCategoryChannel, type BushClient, type BushCommandHandler, + type BushEmoji, + type BushGuild, + type BushGuildBasedChannel, + type BushGuildChannel, + type BushGuildEmoji, + type BushGuildMember, + type BushInhibitor, + type BushListener, type BushMessage, - type BushSlashMessage + type BushNewsChannel, + type BushRole, + type BushSlashMessage, + type BushStageChannel, + type BushStoreChannel, + type BushTask, + type BushTextChannel, + type BushThreadChannel, + type BushUser, + type BushVoiceChannel, + type ParsedDuration } from '#lib'; import { - AkairoApplicationCommandAutocompleteOption, - AkairoApplicationCommandChannelOptionData, - AkairoApplicationCommandChoicesData, - AkairoApplicationCommandNonOptionsData, - AkairoApplicationCommandNumericOptionData, - AkairoApplicationCommandOptionData, - AkairoApplicationCommandSubCommandData, - AkairoApplicationCommandSubGroupData, Command, - MissingPermissionSupplier, - SlashOption, - SlashResolveTypes, + type AkairoApplicationCommandAutocompleteOption, + type AkairoApplicationCommandChannelOptionData, + type AkairoApplicationCommandChoicesData, + type AkairoApplicationCommandNonOptionsData, + type AkairoApplicationCommandNumericOptionData, + type AkairoApplicationCommandOptionData, + type AkairoApplicationCommandSubCommandData, + type AkairoApplicationCommandSubGroupData, type ArgumentOptions, - type CommandOptions + type ArgumentType, + type ArgumentTypeCaster, + type BaseArgumentType, + type CommandOptions, + type ContextMenuCommand, + type MissingPermissionSupplier, + type SlashOption, + type SlashResolveTypes } from 'discord-akairo'; -import { ArgumentType, ArgumentTypeCaster, BaseArgumentType } from 'discord-akairo/dist/src/struct/commands/arguments/Argument'; -import { ApplicationCommandOptionChoice, PermissionString, type PermissionResolvable, type Snowflake } from 'discord.js'; -import { DiscordEmojiInfo } from '../../../arguments/discordEmoji'; -import { RoleWithDuration } from '../../../arguments/roleWithDuration'; +import { + type ApplicationCommandOptionChoice, + type Collection, + type Invite, + type PermissionResolvable, + type PermissionString, + type Snowflake +} from 'discord.js'; + +export interface OverriddenBaseArgumentType extends BaseArgumentType { + user: BushUser | null; + users: Collection<string, BushUser> | null; + member: BushGuildMember | null; + members: Collection<string, BushGuildMember> | null; + relevant: BushUser | BushGuildMember | null; + relevants: Collection<string, BushUser> | Collection<string, BushGuildMember> | null; + channel: BushGuildBasedChannel | BushBaseGuildVoiceChannel | null; + channels: Collection<string, BushGuildBasedChannel | BushBaseGuildVoiceChannel> | null; + textChannel: BushTextChannel | null; + textChannels: Collection<string, BushTextChannel> | null; + voiceChannel: BushVoiceChannel | null; + voiceChannels: Collection<string, BushVoiceChannel> | null; + categoryChannel: BushCategoryChannel | null; + categoryChannels: Collection<string, BushCategoryChannel> | null; + newsChannel: BushNewsChannel | null; + newsChannels: Collection<string, BushNewsChannel> | null; + // eslint-disable-next-line deprecation/deprecation + storeChannel: BushStoreChannel | null; + // eslint-disable-next-line deprecation/deprecation + storeChannels: Collection<string, BushStoreChannel> | null; + stageChannel: BushStageChannel | null; + stageChannels: Collection<string, BushStageChannel> | null; + threadChannel: BushThreadChannel | null; + threadChannels: Collection<string, BushThreadChannel> | null; + role: BushRole | null; + roles: Collection<string, BushRole> | null; + emoji: BushEmoji | null; + emojis: Collection<string, BushEmoji> | null; + guild: BushGuild | null; + guilds: Collection<string, BushGuild> | null; + message: BushMessage | null; + guildMessage: BushMessage | null; + relevantMessage: BushMessage | null; + invite: Invite | null; + userMention: BushUser | null; + memberMention: BushGuildMember | null; + channelMention: BushThreadChannel | BushGuildChannel | null; + roleMention: BushRole | null; + emojiMention: BushGuildEmoji | null; + commandAlias: BushCommand | null; + command: BushCommand | null; + inhibitor: BushInhibitor | null; + listener: BushListener | null; + task: BushTask | null; + contextMenuCommand: ContextMenuCommand | null; +} -export interface BaseBushArgumentType extends BaseArgumentType { +export interface BaseBushArgumentType extends OverriddenBaseArgumentType { duration: number | null; contentWithDuration: ParsedDuration; permission: PermissionString | null; @@ -38,6 +113,7 @@ export interface BaseBushArgumentType extends BaseArgumentType { abbreviatedNumber: number | null; globalUser: BushUser | null; messageLink: BushMessage | null; + durationSeconds: number | null; } export type BushArgumentType = keyof BaseBushArgumentType | RegExp; @@ -446,3 +522,6 @@ type SlashOptionKeys = | keyof AkairoApplicationCommandAutocompleteOption | keyof AkairoApplicationCommandNumericOptionData | keyof AkairoApplicationCommandSubCommandData; + +export type ArgType<T extends keyof BaseBushArgumentType> = NonNullable<BaseBushArgumentType[T]>; +export type OptionalArgType<T extends keyof BaseBushArgumentType> = BaseBushArgumentType[T]; diff --git a/src/lib/extensions/discord-akairo/BushSlashMessage.ts b/src/lib/extensions/discord-akairo/BushSlashMessage.ts index 448963b..3cd167d 100644 --- a/src/lib/extensions/discord-akairo/BushSlashMessage.ts +++ b/src/lib/extensions/discord-akairo/BushSlashMessage.ts @@ -1,4 +1,11 @@ -import { type BushClient, type BushCommandUtil, type BushGuild, type BushGuildMember, type BushUser } from '#lib'; +import { + type BushClient, + type BushCommandUtil, + type BushGuild, + type BushGuildMember, + type BushTextBasedChannel, + type BushUser +} from '#lib'; import { AkairoMessage } from 'discord-akairo'; import { type CommandInteraction } from 'discord.js'; @@ -13,5 +20,12 @@ export class BushSlashMessage extends AkairoMessage { } export interface BushSlashMessage extends AkairoMessage { + /** + * The channel that the interaction was sent in. + */ + get channel(): BushTextBasedChannel | null; +} + +export interface BushSlashMessage extends AkairoMessage { get guild(): BushGuild | null; } diff --git a/tsconfig.json b/tsconfig.json index df315ca..d47c02b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,13 @@ "target": "ESNext", "moduleResolution": "Node", "outDir": "dist", - "lib": ["esnext", "esnext.array", "esnext.asyncIterable", "esnext.intl", "esnext.symbol"], + "lib": [ + "esnext", + "esnext.array", + "esnext.asyncIterable", + "esnext.intl", + "esnext.symbol" + ], "sourceMap": true, "incremental": true, "experimentalDecorators": true, @@ -22,9 +28,22 @@ "preserveValueImports": true, "removeComments": true, "paths": { - "#lib": ["./src/lib/index.js"] + "#lib": [ + "./src/lib/index.js" + ], + "#args": [ + "./src/arguments/index.ts" + ], } }, - "include": ["src/**/*.ts", "src/**/*d.ts", "lib/**/*.ts", "ecosystem.config.cjs"], - "exclude": ["dist", "node_modules"] -} + "include": [ + "src/**/*.ts", + "src/**/*d.ts", + "lib/**/*.ts", + "ecosystem.config.cjs" + ], + "exclude": [ + "dist", + "node_modules" + ] +}
\ No newline at end of file |