diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/arguments/roleWithDuation.ts | 13 | ||||
-rw-r--r-- | src/commands/config/blacklist.ts | 4 | ||||
-rw-r--r-- | src/commands/config/settings.ts | 142 | ||||
-rw-r--r-- | src/commands/dev/superUser.ts | 11 | ||||
-rw-r--r-- | src/commands/info/avatar.ts | 5 | ||||
-rw-r--r-- | src/commands/leveling/leaderboard.ts | 0 | ||||
-rw-r--r-- | src/commands/leveling/level.ts (renamed from src/commands/moulberry-bush/level.ts) | 105 | ||||
-rw-r--r-- | src/commands/moderation/ban.ts | 3 | ||||
-rw-r--r-- | src/commands/moderation/modlog.ts | 3 | ||||
-rw-r--r-- | src/commands/moderation/mute.ts | 3 | ||||
-rw-r--r-- | src/commands/moderation/role.ts | 90 | ||||
-rw-r--r-- | src/commands/moderation/slowmode.ts | 5 | ||||
-rw-r--r-- | src/commands/moulberry-bush/rule.ts | 2 | ||||
-rw-r--r-- | src/commands/utilities/decode.ts | 3 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClient.ts | 4 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushClientUtil.ts | 4 | ||||
-rw-r--r-- | src/lib/extensions/discord-akairo/BushCommand.ts | 6 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuild.ts | 6 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuildMember.ts | 2 |
19 files changed, 264 insertions, 147 deletions
diff --git a/src/arguments/roleWithDuation.ts b/src/arguments/roleWithDuation.ts new file mode 100644 index 0000000..423e7df --- /dev/null +++ b/src/arguments/roleWithDuation.ts @@ -0,0 +1,13 @@ +import { BushArgumentTypeCaster } from '@lib'; + +export const roleWithDurationTypeCaster: BushArgumentTypeCaster = async ( + message, + phrase +): Promise<{ duration: number; role: string | null } | null> => { + const { duration, contentWithoutTime } = client.util.parseDuration(phrase); + if (contentWithoutTime === null || contentWithoutTime === undefined) return null; + const role = await util.arg.cast('role', client.commandHandler.resolver, message, contentWithoutTime); + console.debug(['role'], [role], [contentWithoutTime]); + if (!role) return null; + return { duration, role }; +}; diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts index 57c3015..ff34567 100644 --- a/src/commands/config/blacklist.ts +++ b/src/commands/config/blacklist.ts @@ -68,8 +68,8 @@ export default class BlacklistCommand extends BushCommand { const global = args.global && message.author.isOwner(); const target = typeof args.target === 'string' - ? (await Argument.cast('channel', client.commandHandler.resolver, message as BushMessage, args.target)) ?? - (await Argument.cast('user', client.commandHandler.resolver, message as BushMessage, args.target)) + ? (await util.arg.cast('channel', client.commandHandler.resolver, message as BushMessage, args.target)) ?? + (await util.arg.cast('user', client.commandHandler.resolver, message as BushMessage, args.target)) : args.target; if (!target) return await message.util.reply(`${util.emojis.error} Choose a valid channel or user.`); const targetID = target.id; diff --git a/src/commands/config/settings.ts b/src/commands/config/settings.ts index a8070e2..8a5f290 100644 --- a/src/commands/config/settings.ts +++ b/src/commands/config/settings.ts @@ -1,4 +1,5 @@ -import { BushCommand, BushMessage, BushSlashMessage, guildSettingsObj, settingsArr } from '@lib'; +import { BushCommand, BushMessage, BushSlashMessage, GuildSettings, guildSettingsObj, settingsArr } from '@lib'; +import { ArgumentOptions, Flag } from 'discord-akairo'; import { Message, MessageActionRow, @@ -8,6 +9,7 @@ import { MessageOptions, MessageSelectMenu } from 'discord.js'; +import _ from 'lodash'; export default class SettingsCommand extends BushCommand { public constructor() { @@ -16,13 +18,15 @@ export default class SettingsCommand extends BushCommand { category: 'config', description: { content: 'Configure server options.', - usage: 'settings', + usage: `settings (${settingsArr.map((s) => `\`${s}\``).join(', ')}) (${['view', 'set', 'add', 'remove'].map( + (s) => `\`${s}\`` + )})`, examples: ['settings'] }, slash: true, slashOptions: settingsArr.map((setting) => { return { - name: util.camelToSnakeCase(setting), + name: _.snakeCase(setting), description: `Manage the server's ${guildSettingsObj[setting].name.toLowerCase()}`, type: 'SUB_COMMAND_GROUP', options: guildSettingsObj[setting].type.includes('-array') @@ -95,14 +99,122 @@ export default class SettingsCommand extends BushCommand { }); } - // *args(): any {} + // I make very readable code :) + *args(message: BushMessage): IterableIterator<ArgumentOptions | Flag> { + const setting = yield { + id: 'setting', + type: settingsArr, + prompt: { + start: `What setting would you like to see or change? You can choose one of the following: ${settingsArr + .map((s) => `\`${s}\``) + .join(', ')}`, + retry: `{error} Choose one of the following settings: ${settingsArr.map((s) => `\`${s}\``).join(', ')}`, + optional: message.util.parsed!.alias === 'settings' + } + }; + + const action = yield { + id: 'action', + type: guildSettingsObj[setting as unknown as GuildSettings].type.includes('-array') + ? ['view', 'add', 'remove'] + : ['view', 'set'], + prompt: { + start: `Would you like to ${util.oxford( + (guildSettingsObj[setting as unknown as GuildSettings].type.includes('-array') + ? ['view', 'add', 'remove'] + : ['view', 'set'] + ).map((a) => `\`${a}\``), + 'or' + )} the \`${setting}\` setting?`, + retry: `{error} Choose one of the following actions to perform on the \`${setting}\` setting: ${util.oxford( + (guildSettingsObj[setting as unknown as GuildSettings].type.includes('-array') + ? ['view', 'add', 'remove'] + : ['view', 'set'] + ).map((a) => `\`${a}\``), + 'or' + )}`, + optional: message.util.parsed!.alias === 'settings' + } + }; - public override async exec(message: BushMessage | BushSlashMessage, args: unknown): Promise<unknown> { - client.console.debugRaw(message.interaction); - client.console.debugRaw(args); + const value = + action === 'view' + ? undefined + : yield { + id: 'value', + type: 'string', + match: 'restContent', + prompt: { + start: `What would you like to ${action} ${ + (action as unknown as 'add' | 'remove' | 'set') === 'add' + ? `to the ${setting} setting` + : (action as unknown as 'remove' | 'set') === 'remove' + ? `from the ${setting} setting` + : `the ${setting} setting to` + }?`, + retry: `{error} You must choose a value to ${action} ${ + (action as unknown as 'add' | 'remove' | 'set') === 'add' + ? `to the ${setting} setting` + : (action as unknown as 'remove' | 'set') === 'remove' + ? `from the ${setting} setting` + : `the ${setting} setting to` + }.`, + optional: message.util.parsed!.alias === 'settings' + } + }; + + return { setting, action, value }; + } + + public override async exec( + message: BushMessage | BushSlashMessage, + args: { [x in GuildSettings | ('view' | 'set' | 'add' | 'remove') | ('setting' | 'action') | 'value']: string | undefined } + ): Promise<unknown> { if (!message.guild) return await message.util.reply(`${util.emojis.error} This command can only be used in servers.`); - const messageOptions = await this.generateMessageOptions(message); - const msg = (await message.util.reply(messageOptions)) as Message; + if (!message.member?.permissions.has('MANAGE_GUILD')) + return await message.util.reply( + `${util.emojis.error} You must have the **MANAGE_GUILD** permissions to run this command.` + ); + const setting = _.camelCase(args[settingsArr.find((s) => args[s]) ?? 'setting']) as GuildSettings | undefined; + const action = (args[ + (['view', 'set', 'add', 'remove'] as ('view' | 'set' | 'add' | 'remove')[]).find((a) => args[a]) ?? 'action' + ] ?? 'view') as 'view' | 'set' | 'add' | 'remove'; + const value = args.value; + + let msg; + + if (!setting || action === 'view') { + const messageOptions = await this.generateMessageOptions(message, setting ?? undefined); + msg = (await message.util.reply(messageOptions)) as Message; + } else { + if (!value) + return await message.util.reply( + `${util.emojis.error} You must choose a value to ${action} ${ + (action as unknown as 'add' | 'remove' | 'set') === 'add' + ? `to the ${setting} setting` + : (action as unknown as 'remove' | 'set') === 'remove' + ? `from the ${setting} setting` + : `the ${setting} setting to` + }` + ); + switch (action) { + case 'add': + case 'remove': { + const existing = (await message.guild.getSetting(setting)) as string[]; + const updated = util.addOrRemoveFromArray('add', existing, value); + await message.guild.setSetting(setting, updated); + const messageOptions = await this.generateMessageOptions(message); + msg = (await message.util.reply(messageOptions)) as Message; + break; + } + case 'set': { + await message.guild.setSetting(setting, value); + const messageOptions = await this.generateMessageOptions(message); + msg = (await message.util.reply(messageOptions)) as Message; + break; + } + } + } const collector = msg.createMessageComponentCollector({ channel: message.channel ?? undefined, guild: message.guild, @@ -121,6 +233,11 @@ export default class SettingsCommand extends BushCommand { await this.generateMessageOptions(message, interaction.values[0] as keyof typeof guildSettingsObj) ); } + case 'command_settingsBack': { + if (!interaction.isButton()) return; + + return interaction.update(await this.generateMessageOptions(message)); + } } } else { return await interaction?.deferUpdate().catch(() => undefined); @@ -130,7 +247,7 @@ export default class SettingsCommand extends BushCommand { public async generateMessageOptions( message: BushMessage | BushSlashMessage, - feature?: keyof typeof guildSettingsObj + feature?: undefined | keyof typeof guildSettingsObj ): Promise<MessageOptions> { if (!message.guild) throw new Error('message.guild is null'); const settingsEmbed = new MessageEmbed().setTitle(`${message.guild!.name}'s Settings`).setColor(util.colors.default); @@ -158,6 +275,7 @@ export default class SettingsCommand extends BushCommand { type: 'string' | 'channel' | 'channel-array' | 'role' | 'role-array' ): Promise<string> => { const feat = await message.guild!.getSetting(feature); + console.debug(feat); switch (type.replace('-array', '') as 'string' | 'channel' | 'role') { case 'string': { return Array.isArray(feat) @@ -172,6 +290,7 @@ export default class SettingsCommand extends BushCommand { } } }; + const components = new MessageActionRow().addComponents( new MessageButton().setStyle('PRIMARY').setCustomId('command_settingsBack').setLabel('Back') ); @@ -184,7 +303,8 @@ export default class SettingsCommand extends BushCommand { ); settingsEmbed.addField( guildSettingsObj[feature].name, - await generateCurrentValue(feature as 'string' | 'channel' | 'channel-array' | 'role' | 'role-array') + (await generateCurrentValue(feature as 'string' | 'channel' | 'channel-array' | 'role' | 'role-array')) || + '[No Value Set]' ); return { embeds: [settingsEmbed], components: [components] }; } diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index 957e2b7..1b2fd7c 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,4 +1,5 @@ import { BushCommand, BushMessage, BushSlashMessage, Global } from '@lib'; +import { ArgumentOptions, Flag } from 'discord-akairo'; import { User } from 'discord.js'; export default class SuperUserCommand extends BushCommand { @@ -15,14 +16,14 @@ export default class SuperUserCommand extends BushCommand { ownerOnly: true }); } - *args(): unknown { + *args(): IterableIterator<ArgumentOptions | Flag> { const action = yield { id: 'action', type: ['add', 'remove'], prompt: { start: 'Would you like to `add` or `remove` a user from the superuser list?', retry: '{error} Choose if you would like to `add` or `remove` a user.', - required: true + optional: false } }; const user = yield { @@ -30,9 +31,9 @@ export default class SuperUserCommand extends BushCommand { type: 'user', match: 'restContent', prompt: { - start: `Who would you like to ${action || 'add/remove'} from the superuser list?`, - retry: `Choose a valid user to ${action || 'add/remove'} from the superuser list.`, - required: true + start: `Who would you like to ${action ?? 'add/remove'} from the superuser list?`, + retry: `Choose a valid user to ${action ?? 'add/remove'} from the superuser list.`, + optional: false } }; return { action, user }; diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts index 33393b8..7654d2f 100644 --- a/src/commands/info/avatar.ts +++ b/src/commands/info/avatar.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, MessageEmbed, User } from 'discord.js'; +import { MessageEmbed, User } from 'discord.js'; import { BushCommand, BushMessage, BushSlashMessage } from '../../lib'; export default class AvatarCommand extends BushCommand { @@ -36,9 +36,6 @@ export default class AvatarCommand extends BushCommand { } override async exec(message: BushMessage | BushSlashMessage, args: { user: User }): Promise<void> { - client.console.debugRaw(args); - client.console.debugRaw(message.interaction); - client.console.debugRaw((message.interaction as CommandInteraction).options.getUser('user')); const user = args.user ?? message.author; const embed = new MessageEmbed() diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/commands/leveling/leaderboard.ts diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/leveling/level.ts index 02d66be..a579df0 100644 --- a/src/commands/moulberry-bush/level.ts +++ b/src/commands/leveling/level.ts @@ -1,17 +1,15 @@ -import { BushCommand, BushGuild, BushMessage, BushSlashMessage, BushUser, Level } from '@lib'; -/* +import { BushCommand, BushGuild, BushMessage, BushSlashMessage, BushUser, CanvasProgressBar, Level } from '@lib'; import canvas from 'canvas'; import { MessageAttachment } from 'discord.js'; -import { join } from 'path'; import got from 'got/dist/source'; -import { CanvasProgressBar } from '@lib'; -*/ +import { join } from 'path'; +import SimplifyNumber from 'simplify-number'; export default class LevelCommand extends BushCommand { public constructor() { super('level', { - aliases: ['level', 'rank'], - category: "Moulberry's Bush", + aliases: ['level', 'rank', 'lvl'], + category: 'leveling', description: { content: 'Shows the level of a user', usage: 'level [user]', @@ -41,71 +39,62 @@ export default class LevelCommand extends BushCommand { }); } - /* private simplifyXP(xp: number): string { - - } - - private async getImage(user: User): Promise<Buffer> { + private async getImage(user: BushUser, guild: BushGuild): Promise<Buffer> { // I added comments because this code is impossible to read const [userLevelRow] = await Level.findOrBuild({ where: { - id: user.id + user: user.id, + guild: guild.id }, defaults: { - id: user.id + user: user.id, + guild: guild.id } }); - const userLevel = userLevelRow.level + const rank = (await Level.findAll({ where: { guild: guild.id } })).sort((a, b) => b.xp - a.xp); + const userLevel = userLevelRow.level; const currentLevelXP = Level.convertLevelToXp(userLevel); const currentLevelXPProgress = userLevelRow.xp - currentLevelXP; - const xpForNextLevel = - Level.convertLevelToXp(userLevelRow.level + 1) - currentLevelXP; + const xpForNextLevel = Level.convertLevelToXp(userLevelRow.level + 1) - currentLevelXP; + const white = '#FFFFFF', + gray = '#23272A', + newBlurple = '#5865F2'; // Load roboto font because yes - canvas.registerFont( - join(__dirname, '..', '..', '..', 'Roboto-Regular.ttf'), - { - family: 'Roboto' - } - ); + canvas.registerFont(join(__dirname, '..', '..', '..', '..', 'lib', 'assets', 'Roboto-Regular.ttf'), { + family: 'Roboto' + }); // Create image canvas const image = canvas.createCanvas(800, 200), ctx = image.getContext('2d'); // Fill background - ctx.fillStyle = '#00c7eb'; + ctx.fillStyle = gray; ctx.fillRect(0, 0, image.width, image.height); // Draw avatar - const avatarBuffer = await got - .get(user.displayAvatarURL({ format: 'png', size: 128 })) - .buffer(); + const avatarBuffer = await got.get(user.displayAvatarURL({ format: 'png', size: 128 })).buffer(); const avatarImage = new canvas.Image(); avatarImage.src = avatarBuffer; - avatarImage.height = 128 - avatarImage.width = 128 - const imageTopCoord = (image.height / 2) - (avatarImage.height / 2) + avatarImage.height = 128; + avatarImage.width = 128; + const imageTopCoord = image.height / 2 - avatarImage.height / 2; ctx.drawImage(avatarImage, imageTopCoord, imageTopCoord); // Write tag of user ctx.font = '30px Roboto'; - ctx.fillStyle = 'black'; + ctx.fillStyle = white; const measuredTag = ctx.measureText(user.tag); ctx.fillText(user.tag, avatarImage.width + 70, 60); // Draw line under tag - ctx.fillStyle = 'yellow'; - ctx.fillRect( - avatarImage.width + 70, - 65 + measuredTag.actualBoundingBoxDescent, - measuredTag.width, - 3 - ); + ctx.fillStyle = newBlurple; + ctx.fillRect(avatarImage.width + 70, 65 + measuredTag.actualBoundingBoxDescent, measuredTag.width, 3); // Draw leveling bar const fullProgressBar = new CanvasProgressBar( ctx, { x: avatarImage.width + 70, - y: avatarImage.height - 10, + y: avatarImage.height - 0, height: 30, width: 550 }, - '#6e6e6e', + white, 1 ); fullProgressBar.draw(); @@ -113,38 +102,30 @@ export default class LevelCommand extends BushCommand { ctx, { x: avatarImage.width + 70, - y: avatarImage.height - 10, + y: avatarImage.height - 0, height: 30, width: 550 }, - 'yellow', + newBlurple, currentLevelXPProgress / xpForNextLevel ); progressBar.draw(); // Draw level data text - ctx.fillStyle = 'black' - ctx.fillText(`Level: ${userLevel} XP: $`, avatarImage.width + 70, avatarImage.height - 20) + ctx.fillStyle = white; + ctx.fillText( + `Level: ${userLevel} XP: ${SimplifyNumber(currentLevelXPProgress)}/${SimplifyNumber( + xpForNextLevel + )} Rank: ${SimplifyNumber(rank.indexOf(rank.find((x) => x.user === user.id)!) + 1)}`, + avatarImage.width + 70, + avatarImage.height - 20 + ); // Return image in buffer form return image.toBuffer(); - } */ - - private async getResponse(user: BushUser, guild: BushGuild): Promise<string> { - const userLevelRow = await Level.findOne({ where: { user: user.id, guild: guild.id } }); - if (userLevelRow) { - return `${user ? `${user.tag}'s` : 'Your'} level is ${userLevelRow.level} (${userLevelRow.xp} XP)`; - } else { - return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; - } } - public override async exec(message: BushMessage | BushSlashMessage, { user }: { user?: BushUser }): Promise<void> { - // await message.reply( - // new MessageAttachment( - // await this.getImage(user || message.author), - // 'lel.png' - // ) - // ); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - await message.reply(await this.getResponse(user || message.author, message.guild!)); + public override async exec(message: BushMessage | BushSlashMessage, args: { user?: BushUser }): Promise<unknown> { + return await message.reply({ + files: [new MessageAttachment(await this.getImage(args.user ?? message.author, message.guild!), 'level.png')] + }); } } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 7f1a67c..c33b39a 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,5 +1,4 @@ import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushSlashMessage } from '@lib'; -import { Argument } from 'discord-akairo'; import { User } from 'discord.js'; export default class BanCommand extends BushCommand { @@ -110,7 +109,7 @@ export default class BanCommand extends BushCommand { if (reason) { time = typeof reason === 'string' - ? await Argument.cast('duration', client.commandHandler.resolver, message as BushMessage, reason) + ? await util.arg.cast('duration', client.commandHandler.resolver, message as BushMessage, reason) : reason.duration; } const parsedReason = reason?.contentWithoutTime ?? ''; diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 04264b8..ef0a56e 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -42,8 +42,7 @@ export default class ModlogCommand extends BushCommand { `**Moderator**: <@!${log.moderator}> (${log.moderator})` ]; if (log.duration) modLog.push(`**Duration**: ${util.humanizeDuration(log.duration)}`); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - modLog.push(`**Reason**: ${log.reason || 'No Reason Specified.'}`); + modLog.push(`**Reason**: ${log.reason ?? 'No Reason Specified.'}`); if (log.evidence) modLog.push(`**Evidence:** ${log.evidence}`); return modLog.join(`\n`); } diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index 7b8689a..915302e 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -1,5 +1,4 @@ import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, BushUser } from '@lib'; -import { Argument } from 'discord-akairo'; export default class MuteCommand extends BushCommand { public constructor() { @@ -77,7 +76,7 @@ export default class MuteCommand extends BushCommand { if (reason) { time = typeof reason === 'string' - ? await Argument.cast('duration', client.commandHandler.resolver, message as BushMessage, reason) + ? await util.arg.cast('duration', client.commandHandler.resolver, message as BushMessage, reason) : reason.duration; } const parsedReason = reason?.contentWithoutTime ?? ''; diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 4575a11..ddaefaa 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,51 +1,16 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushRole, BushSlashMessage } from '@lib'; +import { ArgumentOptions, Flag } from 'discord-akairo'; export default class RoleCommand extends BushCommand { public constructor() { super('role', { - aliases: ['role'], + aliases: ['role', 'rr', 'ar', 'ra'], category: 'moderation', description: { content: "Manages users' roles.", usage: 'role <add|remove> <user> <role> [duration]', examples: ['role add spammer nogiveaways 7days'] }, - args: [ - { - id: 'action', - customType: [['add'], ['remove']], - prompt: { - start: 'Would you like to `add` or `remove` a role?', - retry: '{error} Choose whether you would you like to `add` or `remove` a role.' - } - }, - { - id: 'user', - type: 'member', - prompt: { - start: `What user do you want to add/remove the role to/from?`, - retry: `{error} Choose a valid user to add/remove the role to/from.` - } - }, - { - id: 'role', - type: 'role', - prompt: { - start: `What role do you want to add/remove to/from the user?`, - retry: `{error} Choose a valid role to add/remove.` - } - }, - { - id: 'duration', - type: 'duration', - prompt: { - start: 'How long would you like to role to last?', - retry: '{error} Choose a valid duration.', - optional: true - } - } - ], slash: true, slashOptions: [ { @@ -90,9 +55,56 @@ export default class RoleCommand extends BushCommand { }); } + *args(message: BushMessage): IterableIterator<ArgumentOptions | Flag> { + const action = ['rr'].includes(message.util.parsed?.alias ?? '') + ? 'remove' + : ['ar', 'ra'].includes(message.util.parsed?.alias ?? '') + ? 'add' + : yield { + id: 'action', + type: [['add'], ['remove']], + prompt: { + start: 'Would you like to `add` or `remove` a role?', + retry: (...arg) => { + console.debug(...arg); + return '{error} Choose whether you would you like to `add` or `remove` a role.'; + } + } + }; + console.debug(action); + const user = yield { + id: 'user', + type: 'member', + prompt: { + start: `What user do you want to ${action} the role ${action === 'add' ? 'to' : 'from'}?`, + retry: (...arg) => { + console.debug(...arg); + return `{error} Choose a valid user to ${action} the role ${action === 'add' ? 'to' : 'from'}.`; + } + } + }; + console.debug(user); + const _role = yield { + id: 'role', + type: `${action === 'add' ? 'roleWithDuration' : 'role'}`, + match: 'rest', + prompt: { + start: `What role do you want to ${action} ${action === 'add' ? 'to' : 'from'} the user${ + action === 'add' ? ', and for how long' : '' + }?`, + retry: (...arg) => { + console.debug(...arg); + return `{error} Choose a valid role to ${action}.`; + } + } + }; + console.debug(_role); + return { action, user, role: (_role as any).role ?? _role, duration: (_role as any).duration }; + } + public override async exec( message: BushMessage | BushSlashMessage, - { action, user, role, duration }: { action: 'add' | 'remove'; user: BushGuildMember; role: BushRole; duration: number } + { action, user, role, duration }: { action: 'add' | 'remove'; user: BushGuildMember; role: BushRole; duration?: number } ): Promise<unknown> { if (!message.member!.permissions.has('MANAGE_ROLES')) { const mappings = client.consts.mappings; @@ -131,6 +143,8 @@ export default class RoleCommand extends BushCommand { const responseMessage = () => { switch (responseCode) { case 'user hierarchy': + client.console.debug(role.position); + client.console.debug(user.roles.highest.position); return `${util.emojis.error} <@&${role.id}> is higher or equal to your highest role.`; case 'role managed': return `${util.emojis.error} <@&${role.id}> is managed by an integration and cannot be managed.`; diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts index 1d47616..04fe3e4 100644 --- a/src/commands/moderation/slowmode.ts +++ b/src/commands/moderation/slowmode.ts @@ -66,12 +66,11 @@ export default class SlowModeCommand extends BushCommand { if (length) { length = typeof length === 'string' && !['off', 'none', 'disable'].includes(length) - ? await Argument.cast('duration', client.commandHandler.resolver, message as BushMessage, length) + ? await util.arg.cast('duration', client.commandHandler.resolver, message as BushMessage, length) : length; } - // @ts-expect-error: stop being dumb smh - const length2: number = ['off', 'none', 'disable'].includes(length) ? 0 : length; + const length2: number = ['off', 'none', 'disable'].includes(length as string) ? 0 : (length as number); const setSlowmode = await (channel as ThreadChannel | TextChannel) .setRateLimitPerUser(length2 / 1000, `Changed by ${message.author.tag} (${message.author.id}).`) diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index bf44dad..0c1e435 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -131,7 +131,7 @@ export default class RuleCommand extends BushCommand { // If the original message was a reply -> imitate it message.reference?.messageId && !message.util.isSlash ? await message.channel.messages.fetch(message.reference.messageId).then(async (message) => { - await message.util!.reply({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() }); + await message.reply({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() }); }) : await message.util.send({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() }) ); diff --git a/src/commands/utilities/decode.ts b/src/commands/utilities/decode.ts index a5a4c21..e48c644 100644 --- a/src/commands/utilities/decode.ts +++ b/src/commands/utilities/decode.ts @@ -95,8 +95,7 @@ export default class DecodeCommand extends BushCommand { message: BushMessage | AkairoMessage, { from, to, data }: { from: BufferEncoding; to: BufferEncoding; data: string } ): Promise<unknown> { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const encodeOrDecode = util.capitalizeFirstLetter(message?.util?.parsed?.alias || 'decoded'); + const encodeOrDecode = util.capitalizeFirstLetter(message?.util?.parsed?.alias ?? 'decoded'); const decodedEmbed = new MessageEmbed() .setTitle(`${encodeOrDecode} Information`) .addField('📥 Input', await util.inspectCleanRedactCodeblock(data)); diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts index ca5f325..2eaf3d3 100644 --- a/src/lib/extensions/discord-akairo/BushClient.ts +++ b/src/lib/extensions/discord-akairo/BushClient.ts @@ -26,6 +26,7 @@ import { contentWithDurationTypeCaster } from '../../../arguments/contentWithDur import { discordEmojiTypeCaster } from '../../../arguments/discordEmoji'; import { durationTypeCaster } from '../../../arguments/duration'; import { permissionTypeCaster } from '../../../arguments/permission'; +import { roleWithDurationTypeCaster } from '../../../arguments/roleWithDuation'; import { snowflakeTypeCaster } from '../../../arguments/snowflake'; import { UpdateCacheTask } from '../../../tasks/updateCache'; import { ActivePunishment } from '../../models/ActivePunishment'; @@ -264,7 +265,8 @@ export class BushClient<Ready extends boolean = boolean> extends AkairoClient<Re contentWithDuration: contentWithDurationTypeCaster, permission: permissionTypeCaster, snowflake: snowflakeTypeCaster, - discordEmoji: discordEmojiTypeCaster + discordEmoji: discordEmojiTypeCaster, + roleWithDuration: roleWithDurationTypeCaster }); // loads all the handlers const loaders = { diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts index 9ed890a..ef51b63 100644 --- a/src/lib/extensions/discord-akairo/BushClientUtil.ts +++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts @@ -1354,10 +1354,6 @@ export class BushClientUtil extends ClientUtil { return new Promise((resolve) => setTimeout(resolve, s * 1000)); } - camelToSnakeCase(str: string) { - return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); - } - //~ modified from https://stackoverflow.com/questions/31054910/get-functions-methods-of-a-class //~ answer by Bruno Grieder //~ public getMethods(obj: any): string { diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts index 7ecb679..3a2c619 100644 --- a/src/lib/extensions/discord-akairo/BushCommand.ts +++ b/src/lib/extensions/discord-akairo/BushCommand.ts @@ -63,7 +63,8 @@ export type BaseBushArgumentType = | 'contentWithDuration' | 'permission' | 'snowflake' - | 'discordEmoji'; + | 'discordEmoji' + | 'roleWithDuration'; export type BushArgumentType = BaseBushArgumentType | RegExp; @@ -180,8 +181,7 @@ export class BushCommand extends Command { } super(id, options); this.options = options; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - this.hidden = options.hidden || false; + this.hidden = options.hidden ?? false; this.restrictedChannels = options.restrictedChannels!; this.restrictedGuilds = options.restrictedGuilds!; this.completelyHide = options.completelyHide!; diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index 4fc27a7..2c3b4bd 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -73,8 +73,7 @@ export class BushGuild extends Guild { if (!bans.has(user)) notBanned = true; const unbanSuccess = await this.bans - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - .remove(user, `${moderator.tag} | ${options.reason || 'No reason provided.'}`) + .remove(user, `${moderator.tag} | ${options.reason ?? 'No reason provided.'}`) .catch((e) => { if (e?.code === 'UNKNOWN_BAN') { notBanned = true; @@ -108,8 +107,7 @@ export class BushGuild extends Guild { const userObject = client.users.cache.get(user); - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - userObject?.send(`You have been unbanned from **${this}** for **${options.reason || 'No reason provided'}**.`); + userObject?.send(`You have been unbanned from **${this}** for **${options.reason ?? 'No reason provided'}**.`); if (notBanned) return 'user not banned'; return 'success'; diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts index e596c82..6ce473a 100644 --- a/src/lib/extensions/discord.js/BushGuildMember.ts +++ b/src/lib/extensions/discord.js/BushGuildMember.ts @@ -182,7 +182,7 @@ export class BushGuildMember extends GuildMember { } #checkIfShouldAddRole(role: BushRole | Role): true | 'user hierarchy' | 'role managed' | 'client hierarchy' { - if (this.roles.highest.position <= role.position) { + if (this.roles.highest.position <= role.position && this.guild.ownerId !== this.id) { return 'user hierarchy'; } else if (role.managed) { return 'role managed'; |