aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/_fake-command/ironmoon.ts4
-rw-r--r--src/commands/admin/channelPermissions.ts7
-rw-r--r--src/commands/admin/roleAll.ts2
-rw-r--r--src/commands/config/blacklist.ts4
-rw-r--r--src/commands/config/config.ts61
-rw-r--r--src/commands/config/customAutomodPhrases.ts4
-rw-r--r--src/commands/config/disable.ts11
-rw-r--r--src/commands/config/features.ts4
-rw-r--r--src/commands/config/levelRoles.ts126
-rw-r--r--src/commands/config/log.ts4
-rw-r--r--src/commands/dev/__template.ts4
-rw-r--r--src/commands/dev/eval.ts4
-rw-r--r--src/commands/dev/reload.ts4
-rw-r--r--src/commands/dev/say.ts13
-rw-r--r--src/commands/dev/servers.ts45
-rw-r--r--src/commands/dev/sh.ts4
-rw-r--r--src/commands/dev/superUser.ts3
-rw-r--r--src/commands/dev/test.ts9
-rw-r--r--src/commands/fun/coinflip.ts5
-rw-r--r--src/commands/fun/dice.ts3
-rw-r--r--src/commands/fun/eightBall.ts4
-rw-r--r--src/commands/fun/minesweeper.ts68
-rw-r--r--src/commands/info/avatar.ts3
-rw-r--r--src/commands/info/botInfo.ts4
-rw-r--r--src/commands/info/color.ts3
-rw-r--r--src/commands/info/guildInfo.ts4
-rw-r--r--src/commands/info/help.ts4
-rw-r--r--src/commands/info/icon.ts3
-rw-r--r--src/commands/info/links.ts3
-rw-r--r--src/commands/info/ping.ts4
-rw-r--r--src/commands/info/pronouns.ts17
-rw-r--r--src/commands/info/snowflake.ts3
-rw-r--r--src/commands/info/userInfo.ts4
-rw-r--r--src/commands/leveling/leaderboard.ts7
-rw-r--r--src/commands/leveling/level.ts66
-rw-r--r--src/commands/leveling/setLevel.ts4
-rw-r--r--src/commands/leveling/setXp.ts4
-rw-r--r--src/commands/moderation/_lockdown.ts8
-rw-r--r--src/commands/moderation/ban.ts2
-rw-r--r--src/commands/moderation/evidence.ts4
-rw-r--r--src/commands/moderation/hideCase.ts6
-rw-r--r--src/commands/moderation/kick.ts8
-rw-r--r--src/commands/moderation/modlog.ts12
-rw-r--r--src/commands/moderation/mute.ts8
-rw-r--r--src/commands/moderation/purge.ts2
-rw-r--r--src/commands/moderation/removeReactionEmoji.ts6
-rw-r--r--src/commands/moderation/role.ts4
-rw-r--r--src/commands/moderation/slowmode.ts4
-rw-r--r--src/commands/moderation/unban.ts7
-rw-r--r--src/commands/moderation/unmute.ts6
-rw-r--r--src/commands/moderation/warn.ts6
-rw-r--r--src/commands/moulberry-bush/capePerms.ts21
-rw-r--r--src/commands/moulberry-bush/capes.ts49
-rw-r--r--src/commands/moulberry-bush/giveawayPing.ts4
-rw-r--r--src/commands/moulberry-bush/moulHammer.ts6
-rw-r--r--src/commands/moulberry-bush/report.ts8
-rw-r--r--src/commands/moulberry-bush/rule.ts9
-rw-r--r--src/commands/moulberry-bush/serverStatus.ts3
-rw-r--r--src/commands/utilities/activity.ts4
-rw-r--r--src/commands/utilities/calculator.ts4
-rw-r--r--src/commands/utilities/decode.ts33
-rw-r--r--src/commands/utilities/hash.ts3
-rw-r--r--src/commands/utilities/price.ts11
-rw-r--r--src/commands/utilities/steal.ts4
-rw-r--r--src/commands/utilities/suicide.ts22
-rw-r--r--src/commands/utilities/uuid.ts6
-rw-r--r--src/commands/utilities/viewraw.ts38
-rw-r--r--src/commands/utilities/whoHasRole.ts7
-rw-r--r--src/commands/utilities/wolframAlpha.ts40
-rw-r--r--src/config/example-options.ts4
-rw-r--r--src/context-menu-commands/message/viewRaw.ts2
-rw-r--r--src/lib/badlinks.ts1
-rw-r--r--src/lib/badwords.ts2
-rw-r--r--src/lib/common/ButtonPaginator.ts186
-rw-r--r--src/lib/common/DeleteButton.ts61
-rw-r--r--src/lib/common/Format.ts72
-rw-r--r--src/lib/common/autoMod.ts52
-rw-r--r--src/lib/common/moderation.ts19
-rw-r--r--src/lib/common/typings/BushInspectOptions.d.ts91
-rw-r--r--src/lib/common/typings/CodeBlockLang.d.ts310
-rw-r--r--src/lib/common/util/Arg.ts120
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts856
-rw-r--r--src/lib/extensions/discord-akairo/BushCommand.ts4
-rw-r--r--src/lib/extensions/discord.js/BushGuild.ts2
-rw-r--r--src/lib/extensions/discord.js/BushGuildMember.ts2
-rw-r--r--src/lib/models/Guild.ts2
-rw-r--r--src/lib/utils/Config.ts72
-rw-r--r--src/listeners/client/interactionCreate.ts2
-rw-r--r--src/listeners/message/automodCreate.ts2
-rw-r--r--src/listeners/message/automodUpdate.ts2
90 files changed, 1473 insertions, 1272 deletions
diff --git a/src/commands/_fake-command/ironmoon.ts b/src/commands/_fake-command/ironmoon.ts
index ddc6ced..d7e737f 100644
--- a/src/commands/_fake-command/ironmoon.ts
+++ b/src/commands/_fake-command/ironmoon.ts
@@ -5,7 +5,9 @@ export default class IronmoonCommand extends BushCommand {
super('ironmoon', {
category: 'fake-commands',
description: { content: '', examples: '', usage: '' },
- pseudo: true
+ pseudo: true,
+ clientPermissions: [],
+ userPermissions: []
});
}
public override condition(message: BushMessage): boolean {
diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts
index f313a8f..f8c97a9 100644
--- a/src/commands/admin/channelPermissions.ts
+++ b/src/commands/admin/channelPermissions.ts
@@ -1,5 +1,6 @@
import { GuildMember, MessageEmbed, Role } from 'discord.js';
import { BushCommand, BushMessage } from '../../lib';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class ChannelPermissionsCommand extends BushCommand {
public constructor() {
@@ -42,9 +43,7 @@ export default class ChannelPermissionsCommand extends BushCommand {
}
}
],
- ratelimit: 4,
- cooldown: 4000,
- clientPermissions: ['MANAGE_CHANNELS', 'SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_CHANNELS']),
userPermissions: ['ADMINISTRATOR'],
channel: 'guild'
});
@@ -85,7 +84,7 @@ export default class ChannelPermissionsCommand extends BushCommand {
paginate.push(new MessageEmbed().setDescription(failure.substring(i, Math.min(failure.length, i + 4000))));
}
const normalMessage = `Finished changing perms! Failed channels:`;
- return await client.util.buttonPaginate(message, paginate, normalMessage);
+ return await ButtonPaginator.send(message, paginate, normalMessage);
} else {
return await message.util.reply({
content: `Finished changing perms! Failed channels:`,
diff --git a/src/commands/admin/roleAll.ts b/src/commands/admin/roleAll.ts
index d965239..ec1f2b5 100644
--- a/src/commands/admin/roleAll.ts
+++ b/src/commands/admin/roleAll.ts
@@ -28,7 +28,7 @@ export default class RoleAllCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['MANAGE_ROLES', 'SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']),
userPermissions: ['ADMINISTRATOR'],
typing: true
});
diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts
index 5dea36a..9f3737d 100644
--- a/src/commands/config/blacklist.ts
+++ b/src/commands/config/blacklist.ts
@@ -54,8 +54,8 @@ export default class BlacklistCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['MANAGE_GUILD']
});
}
diff --git a/src/commands/config/config.ts b/src/commands/config/config.ts
index 2075531..72ba566 100644
--- a/src/commands/config/config.ts
+++ b/src/commands/config/config.ts
@@ -12,6 +12,7 @@ import {
MessageOptions,
MessageSelectMenu,
Role,
+ Snowflake,
User
} from 'discord.js';
import _ from 'lodash';
@@ -51,11 +52,7 @@ export default class SettingsCommand extends BushCommand {
description: `What would you like to add to the server's ${guildSettingsObj[
setting
].name.toLowerCase()}?'`,
- type: guildSettingsObj[setting].type.replace('-array', '').toUpperCase() as
- | 'ROLE'
- | 'STRING'
- | 'CHANNEL'
- | 'USER',
+ type: guildSettingsObj[setting].type.replace('-array', '').toUpperCase() as SlashArgType,
required: true
}
]
@@ -70,11 +67,7 @@ export default class SettingsCommand extends BushCommand {
description: `What would you like to remove from the server's ${guildSettingsObj[
setting
].name.toLowerCase()}?'`,
- type: guildSettingsObj[setting].type.replace('-array', '').toUpperCase() as
- | 'ROLE'
- | 'STRING'
- | 'CHANNEL'
- | 'USER',
+ type: guildSettingsObj[setting].type.replace('-array', '').toUpperCase() as SlashArgType,
required: true
}
]
@@ -96,7 +89,7 @@ export default class SettingsCommand extends BushCommand {
description: `What would you like to set the server's ${guildSettingsObj[
setting
].name.toLowerCase()} to?'`,
- type: guildSettingsObj[setting].type.toUpperCase() as 'ROLE' | 'STRING' | 'CHANNEL' | 'USER',
+ type: guildSettingsObj[setting].type.toUpperCase() as SlashArgType,
required: true
}
]
@@ -105,13 +98,13 @@ export default class SettingsCommand extends BushCommand {
};
}),
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['MANAGE_GUILD']
});
}
// I make very readable code :)
- override *args(message: BushMessage): IterableIterator<ArgumentOptions | Flag> {
+ override *args(message: BushMessage): Generator<ArgumentOptions | Flag> {
const optional = message.util.parsed!.alias === 'settings';
const setting = yield {
id: 'setting',
@@ -126,7 +119,7 @@ export default class SettingsCommand extends BushCommand {
};
const actionType = setting
- ? guildSettingsObj[setting as unknown as GuildSettings]?.type.includes('-array')
+ ? guildSettingsObj[setting as GuildSettings]?.type.includes('-array')
? ['view', 'add', 'remove']
: ['view', 'set']
: undefined;
@@ -151,13 +144,13 @@ export default class SettingsCommand extends BushCommand {
const valueType =
setting && action && action !== 'view'
- ? (guildSettingsObj[setting as unknown as GuildSettings].type.replace('-array', '') as 'string' | 'channel' | 'role')
+ ? (guildSettingsObj[setting as GuildSettings].type.replace('-array', '') as 'string' | 'channel' | 'role')
: undefined;
const grammar =
setting && action && action !== 'view'
- ? (action as unknown as 'add' | 'remove' | 'set') === 'add'
+ ? (action as 'add' | 'remove' | 'set') === 'add'
? `to the ${setting} setting`
- : (action as unknown as 'remove' | 'set') === 'remove'
+ : (action as 'remove' | 'set') === 'remove'
? `from the ${setting} setting`
: `the ${setting} setting to`
: undefined;
@@ -184,8 +177,8 @@ export default class SettingsCommand extends BushCommand {
args: {
setting?: GuildSettings;
subcommandGroup?: GuildSettings;
- action?: 'view' | 'add' | 'remove' | 'set';
- subcommand?: 'view' | 'add' | 'remove' | 'set';
+ action?: Action;
+ subcommand?: Action;
value: string | Channel | Role;
}
): Promise<unknown> {
@@ -214,9 +207,9 @@ export default class SettingsCommand extends BushCommand {
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'
+ action === 'add'
? `to the ${setting} setting`
- : (action as unknown as 'remove' | 'set') === 'remove'
+ : action === 'remove'
? `from the ${setting} setting`
: `the ${setting} setting to`
}`
@@ -295,12 +288,10 @@ export default class SettingsCommand extends BushCommand {
return { embeds: [settingsEmbed], components: [selMenu] };
} else {
settingsEmbed.setTitle(guildSettingsObj[setting].name);
- const generateCurrentValue = async (
- type: 'string' | 'channel' | 'channel-array' | 'role' | 'role-array' | 'user' | 'user-array' | 'custom'
- ): Promise<string> => {
+ const generateCurrentValue = async (type: SettingTypes): Promise<string> => {
const feat = await message.guild!.getSetting(setting);
- switch (type.replace('-array', '') as 'string' | 'channel' | 'role' | 'user' | 'custom') {
+ switch (type.replace('-array', '') as BaseSettingTypes) {
case 'string': {
return Array.isArray(feat)
? feat.length
@@ -315,21 +306,21 @@ export default class SettingsCommand extends BushCommand {
? feat.length
? feat.map((feat) => `<#${feat}>`).join('\n')
: '[Empty Array]'
- : `<#${feat as string}>`;
+ : `<#${feat as Snowflake}>`;
}
case 'role': {
return Array.isArray(feat)
? feat.length
? feat.map((feat) => `<@&${feat}>`).join('\n')
: '[Empty Array]'
- : `<@&${feat as string}>`;
+ : `<@&${feat as Snowflake}>`;
}
case 'user': {
return Array.isArray(feat)
? feat.length
? feat.map((feat) => `<@${feat}>`).join('\n')
: '[Empty Array]'
- : `<@${feat as string}>`;
+ : `<@${feat as Snowflake}>`;
}
case 'custom': {
return util.inspectAndRedact(feat);
@@ -355,13 +346,13 @@ export default class SettingsCommand extends BushCommand {
guildSettingsObj[setting].type.includes('-array') ? 'add/remove' : 'set'
} <value>" to set this setting.`
);
- settingsEmbed.addField(
- 'value',
- (await generateCurrentValue(
- guildSettingsObj[setting].type as 'string' | 'channel' | 'channel-array' | 'role' | 'role-array'
- )) || '[No Value Set]'
- );
+ settingsEmbed.addField('value', (await generateCurrentValue(guildSettingsObj[setting].type)) || '[No Value Set]');
return { embeds: [settingsEmbed], components: [components] };
}
}
}
+
+type SlashArgType = 'ROLE' | 'STRING' | 'CHANNEL' | 'USER';
+type BaseSettingTypes = 'string' | 'channel' | 'role' | 'user' | 'custom';
+type SettingTypes = 'string' | 'channel' | 'channel-array' | 'role' | 'role-array' | 'user' | 'user-array' | 'custom';
+type Action = 'view' | 'add' | 'remove' | 'set';
diff --git a/src/commands/config/customAutomodPhrases.ts b/src/commands/config/customAutomodPhrases.ts
index 2cbe874..51e219a 100644
--- a/src/commands/config/customAutomodPhrases.ts
+++ b/src/commands/config/customAutomodPhrases.ts
@@ -49,8 +49,8 @@
// ownerOnly: true,
// channel: 'guild',
// hidden: true,
-// clientPermissions: ['SEND_MESSAGES'],
-// userPermissions: ['SEND_MESSAGES']
+// clientPermissions: (m) => util.clientSendAndPermCheck(m),
+// userPermissions: []
// });
// }
diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts
index db4909a..bca1207 100644
--- a/src/commands/config/disable.ts
+++ b/src/commands/config/disable.ts
@@ -52,8 +52,8 @@ export default class DisableCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['MANAGE_GUILD']
});
}
@@ -63,13 +63,12 @@ export default class DisableCommand extends BushCommand {
message: BushMessage | BushSlashMessage,
args: { action: 'enable' | 'disable'; command: BushCommand | string; global: boolean }
): Promise<unknown> {
- let action: 'disable' | 'enable' | 'toggle' =
- args.action ?? (message?.util?.parsed?.alias as 'disable' | 'enable') ?? 'toggle';
+ let action = (args.action ?? message?.util?.parsed?.alias ?? 'toggle') as 'disable' | 'enable' | 'toggle';
const global = args.global && message.author.isOwner();
const commandID = (args.command as BushCommand).id;
if (global) {
- if ((action as 'disable' | 'enable' | 'toggle') === 'toggle') {
+ if (action === 'toggle') {
const disabledCommands = (
(await Global.findByPk(client.config.environment)) ??
(await Global.create({ environment: client.config.environment }))
@@ -99,7 +98,7 @@ export default class DisableCommand extends BushCommand {
// guild disable
} else {
const disabledCommands = await message.guild!.getSetting('disabledCommands');
- if ((action as 'disable' | 'enable' | 'toggle') === 'toggle') {
+ if (action === 'toggle') {
action = disabledCommands.includes(commandID) ? 'disable' : 'enable';
}
const newValue = util.addOrRemoveFromArray(action === 'disable' ? 'remove' : 'add', disabledCommands, commandID);
diff --git a/src/commands/config/features.ts b/src/commands/config/features.ts
index 076d469..3c3075c 100644
--- a/src/commands/config/features.ts
+++ b/src/commands/config/features.ts
@@ -13,8 +13,8 @@ export default class FeaturesCommand extends BushCommand {
},
slash: true,
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: ['MANAGE_GUILD']
});
}
diff --git a/src/commands/config/levelRoles.ts b/src/commands/config/levelRoles.ts
index 36dc50c..ee5f255 100644
--- a/src/commands/config/levelRoles.ts
+++ b/src/commands/config/levelRoles.ts
@@ -1,65 +1,65 @@
-import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+// import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
-export default class LevelRolesCommand extends BushCommand {
- public constructor() {
- super('levelRole', {
- aliases: ['level-role', 'level-roles', 'lr'],
- category: 'config',
- description: {
- content: 'Configure roles to be assigned to users upon reaching certain levels.',
- usage: ['level-role add <level> <role>', 'level-role remove <level>'],
- examples: ['level-role 1 2']
- },
- args: [
- {
- id: 'action',
- customType: ['add', 'remove']
- },
- {
- id: 'role',
- type: 'role',
- prompt: {
- start: 'What would you like to set your first argument to be?',
- retry: '{error} Pick a valid argument.',
- optional: false
- }
- },
- {
- id: 'level',
- type: 'integer',
- prompt: {
- start: 'What would you like to set your second argument to be?',
- retry: '{error} Pick a valid argument.',
- optional: false
- }
- }
- ],
- slash: true,
- slashOptions: [
- {
- name: 'role',
- description: 'What would you like to set your first argument to be?',
- type: 'STRING',
- required: true
- },
- {
- name: 'level',
- description: 'What would you like to set your second argument to be?',
- type: 'STRING',
- required: true
- }
- ],
- channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD', 'MANAGE_ROLES']
- });
- }
+// export default class LevelRolesCommand extends BushCommand {
+// public constructor() {
+// super('levelRole', {
+// aliases: ['level-role', 'level-roles', 'lr'],
+// category: 'config',
+// description: {
+// content: 'Configure roles to be assigned to users upon reaching certain levels.',
+// usage: ['level-role add <level> <role>', 'level-role remove <level>'],
+// examples: ['level-role 1 2']
+// },
+// args: [
+// {
+// id: 'action',
+// customType: ['add', 'remove']
+// },
+// {
+// id: 'role',
+// type: 'role',
+// prompt: {
+// start: 'What would you like to set your first argument to be?',
+// retry: '{error} Pick a valid argument.',
+// optional: false
+// }
+// },
+// {
+// id: 'level',
+// type: 'integer',
+// prompt: {
+// start: 'What would you like to set your second argument to be?',
+// retry: '{error} Pick a valid argument.',
+// optional: false
+// }
+// }
+// ],
+// slash: true,
+// slashOptions: [
+// {
+// name: 'role',
+// description: 'What would you like to set your first argument to be?',
+// type: 'STRING',
+// required: true
+// },
+// {
+// name: 'level',
+// description: 'What would you like to set your second argument to be?',
+// type: 'STRING',
+// required: true
+// }
+// ],
+// channel: 'guild',
+// clientPermissions: (m) => util.clientSendAndPermCheck(m),
+// userPermissions: ['MANAGE_GUILD', 'MANAGE_ROLES']
+// });
+// }
- public override async exec(
- message: BushMessage | BushSlashMessage,
- args: { required_argument: string; optional_argument: string }
- ): Promise<unknown> {
- return await message.util.reply(`${util.emojis.error} Do not use the template command.`);
- args;
- }
-}
+// public override async exec(
+// message: BushMessage | BushSlashMessage,
+// args: { required_argument: string; optional_argument: string }
+// ): Promise<unknown> {
+// return await message.util.reply(`${util.emojis.error} Do not use the template command.`);
+// args;
+// }
+// }
diff --git a/src/commands/config/log.ts b/src/commands/config/log.ts
index 03e3582..363620a 100644
--- a/src/commands/config/log.ts
+++ b/src/commands/config/log.ts
@@ -29,8 +29,8 @@ export default class LogCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['MANAGE_GUILD']
});
}
diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts
index 49549af..b9d9114 100644
--- a/src/commands/dev/__template.ts
+++ b/src/commands/dev/__template.ts
@@ -49,8 +49,8 @@ export default class TemplateCommand extends BushCommand {
ownerOnly: true,
channel: 'guild',
hidden: true,
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts
index 2f61822..c4a6e8f 100644
--- a/src/commands/dev/eval.ts
+++ b/src/commands/dev/eval.ts
@@ -45,7 +45,9 @@ export default class EvalCommand extends BushCommand {
{ name: 'show_proto', description: 'Show prototype.', type: 'BOOLEAN', required: false },
{ name: 'show_methods', description: 'Show class functions.', type: 'BOOLEAN', required: false }
],
- ownerOnly: true
+ ownerOnly: true,
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts
index 987cd01..43b8ab6 100644
--- a/src/commands/dev/reload.ts
+++ b/src/commands/dev/reload.ts
@@ -19,6 +19,7 @@ export default class ReloadCommand extends BushCommand {
// ],
ownerOnly: true,
typing: true,
+ slash: true,
// slashOptions: [
// {
// name: 'fast',
@@ -27,7 +28,8 @@ export default class ReloadCommand extends BushCommand {
// required: false
// }
// ],
- slash: true
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/dev/say.ts b/src/commands/dev/say.ts
index 1c797ea..a7e9943 100644
--- a/src/commands/dev/say.ts
+++ b/src/commands/dev/say.ts
@@ -13,7 +13,7 @@ export default class SayCommand extends BushCommand {
},
args: [
{
- id: 'say',
+ id: 'content',
type: 'string',
match: 'rest',
prompt: { start: 'What would you like the bot to say?', retry: '{error} Choose something valid to say.' }
@@ -21,20 +21,21 @@ export default class SayCommand extends BushCommand {
],
slashOptions: [{ name: 'content', description: 'What would you like the bot to say?', type: 'STRING' }],
ownerOnly: true,
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
slash: true
});
}
- public override async exec(message: BushMessage, { say }: { say: string }): Promise<unknown> {
+ public override async exec(message: BushMessage, args: { content: string }): Promise<unknown> {
if (!message.author.isOwner())
return await message.util.reply(`${util.emojis.error} Only my developers can run this command.`);
await message.delete().catch(() => {});
- await message.util.send({ content: say, allowedMentions: AllowedMentions.none() });
+ await message.util.send({ content: args.content, allowedMentions: AllowedMentions.none() });
}
- public override async execSlash(message: AkairoMessage, { content }: { content: string }): Promise<unknown> {
+ public override async execSlash(message: AkairoMessage, args: { content: string }): Promise<unknown> {
if (!client.config.owners.includes(message.author.id)) {
return await message.interaction.reply({
content: `${util.emojis.error} Only my developers can run this command.`,
@@ -42,6 +43,6 @@ export default class SayCommand extends BushCommand {
});
}
await message.interaction.reply({ content: 'Attempting to send message.', ephemeral: true });
- return message.channel!.send({ content, allowedMentions: AllowedMentions.none() });
+ return message.channel!.send({ content: args.content, allowedMentions: AllowedMentions.none() });
}
}
diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts
index c86e889..e366a64 100644
--- a/src/commands/dev/servers.ts
+++ b/src/commands/dev/servers.ts
@@ -1,5 +1,6 @@
-import { Guild, MessageEmbed } from 'discord.js';
+import { Guild, MessageEmbedOptions } from 'discord.js';
import { BushCommand, BushMessage, BushSlashMessage } from '../../lib';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class ServersCommand extends BushCommand {
public constructor() {
@@ -11,38 +12,30 @@ export default class ServersCommand extends BushCommand {
usage: 'servers',
examples: ['servers']
},
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
ownerOnly: true
});
}
public override async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
- const maxLength = 10;
const guilds = [...client.guilds.cache.sort((a, b) => (a.memberCount < b.memberCount ? 1 : -1)).values()];
- const chunkedGuilds: Guild[][] = [];
- const embeds: MessageEmbed[] = [];
-
- for (let i = 0, j = guilds.length; i < j; i += maxLength) {
- chunkedGuilds.push(guilds.slice(i, i + maxLength));
- }
-
- chunkedGuilds.forEach((c: Guild[]) => {
- const embed = new MessageEmbed();
- c.forEach((g: Guild) => {
- const owner = client.users.cache.get(g.ownerId)?.tag;
- embed
- .addField(
- `**${g.name}**`,
- `**ID:** ${g.id}\n**Owner:** ${owner ? owner : g.ownerId}\n**Members:** ${g.memberCount.toLocaleString()}`,
- false
- )
- .setTitle(`Server List [\`${client.guilds.cache.size.toLocaleString()}\`]`)
- .setColor(util.colors.default);
- });
- embeds.push(embed);
+ const chunkedGuilds: Guild[][] = util.chunk(guilds, 10);
+ const embeds: MessageEmbedOptions[] = chunkedGuilds.map((chunk) => {
+ return {
+ title: `Server List [\`${guilds.length.toLocaleString()}\`]`,
+ color: util.colors.default,
+ fields: chunk.map((guild) => ({
+ name: util.format.bold(guild.name),
+ value: [
+ `**ID:** ${guild.id}`,
+ `**Owner:** ${client.users.cache.has(guild.ownerId) ? client.users.cache.get(guild.ownerId)!.tag : guild.ownerId}`,
+ `**Members:** ${guild.memberCount.toLocaleString()}`
+ ].join('\n')
+ }))
+ } as MessageEmbedOptions;
});
- return await util.buttonPaginate(message, embeds);
+ return await ButtonPaginator.send(message, embeds);
}
}
diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts
index 067a0e6..3fca2b2 100644
--- a/src/commands/dev/sh.ts
+++ b/src/commands/dev/sh.ts
@@ -33,7 +33,9 @@ export default class ShCommand extends BushCommand {
}
}
],
- ownerOnly: true
+ ownerOnly: true,
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts
index 4dc4584..fcdec53 100644
--- a/src/commands/dev/superUser.ts
+++ b/src/commands/dev/superUser.ts
@@ -12,7 +12,8 @@ export default class SuperUserCommand extends BushCommand {
usage: 'superuser <add/remove> <user>',
examples: ['superuser add IRONM00N']
},
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
ownerOnly: true
});
}
diff --git a/src/commands/dev/test.ts b/src/commands/dev/test.ts
index c77d7ec..a1e5052 100644
--- a/src/commands/dev/test.ts
+++ b/src/commands/dev/test.ts
@@ -7,6 +7,7 @@ import {
MessageButton,
MessageEmbed
} from 'discord.js';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class TestCommand extends BushCommand {
public constructor() {
@@ -18,8 +19,6 @@ export default class TestCommand extends BushCommand {
usage: 'test [feature]',
examples: ['test lots of buttons', 'test buttons']
},
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'feature',
@@ -32,7 +31,9 @@ export default class TestCommand extends BushCommand {
}
}
],
- superUserOnly: true
+ superUserOnly: true,
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
@@ -95,7 +96,7 @@ export default class TestCommand extends BushCommand {
for (let i = 1; i <= 5; i++) {
embeds.push(new MessageEmbed().setDescription(i.toString()));
}
- return await util.buttonPaginate(message, embeds);
+ return await ButtonPaginator.send(message, embeds);
} else if (['lots of embeds'].includes(args?.feature?.toLowerCase())) {
const description = 'This is a description.';
const _avatar = message.author.avatarURL({ dynamic: true }) ?? undefined;
diff --git a/src/commands/fun/coinflip.ts b/src/commands/fun/coinflip.ts
index e0892b7..42e7167 100644
--- a/src/commands/fun/coinflip.ts
+++ b/src/commands/fun/coinflip.ts
@@ -10,14 +10,15 @@ export default class CoinFlipCommand extends BushCommand {
usage: 'coinflip',
examples: ['coinflip']
},
- clientPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
public override async exec(message: BushMessage | BushSlashMessage): Promise<void> {
const random = Math.random();
let result: string;
- const fall = message.author.id === '322862723090219008' ? 0.1 : 0.001;
+ const fall = message.author.id === '322862723090219008' ? 0.1 : 0.001; //dw about it
if (random < fall) result = 'The coin fell off the table :(';
else if (random <= 0.5 + fall / 2) result = 'Heads';
else result = 'Tails';
diff --git a/src/commands/fun/dice.ts b/src/commands/fun/dice.ts
index 241f1d2..9f18657 100644
--- a/src/commands/fun/dice.ts
+++ b/src/commands/fun/dice.ts
@@ -10,7 +10,8 @@ export default class EightBallCommand extends BushCommand {
usage: 'dice',
examples: ['dice']
},
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
slash: true
});
}
diff --git a/src/commands/fun/eightBall.ts b/src/commands/fun/eightBall.ts
index efaaff5..b3c56da 100644
--- a/src/commands/fun/eightBall.ts
+++ b/src/commands/fun/eightBall.ts
@@ -30,8 +30,8 @@ export default class EightBallCommand extends BushCommand {
required: true
}
],
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/fun/minesweeper.ts b/src/commands/fun/minesweeper.ts
index 57909a2..b45dcda 100644
--- a/src/commands/fun/minesweeper.ts
+++ b/src/commands/fun/minesweeper.ts
@@ -42,43 +42,15 @@ export default class MinesweeperCommand extends BushCommand {
},
default: 10
},
- {
- id: 'spaces',
- match: 'flag',
- flag: '--spaces'
- },
- {
- id: 'reveal_first_cell',
- match: 'flag',
- flag: '--revealFirstCell'
- }
+ { id: 'spaces', match: 'flag', flag: '--spaces' },
+ { id: 'reveal_first_cell', match: 'flag', flag: '--revealFirstCell' }
],
slash: true,
slashOptions: [
- {
- name: 'rows',
- description: 'How many rows would you like?',
- type: 'INTEGER',
- required: false
- },
- {
- name: 'columns',
- description: 'How many rows would you like?',
- type: 'INTEGER',
- required: false
- },
- {
- name: 'mines',
- description: 'How many rows would you like?',
- type: 'INTEGER',
- required: false
- },
- {
- name: 'spaces',
- description: 'Would you like there to be spaces?',
- type: 'BOOLEAN',
- required: false
- },
+ { name: 'rows', description: 'How many rows would you like?', type: 'INTEGER', required: false },
+ { name: 'columns', description: 'How many rows would you like?', type: 'INTEGER', required: false },
+ { name: 'mines', description: 'How many rows would you like?', type: 'INTEGER', required: false },
+ { name: 'spaces', description: 'Would you like there to be spaces?', type: 'BOOLEAN', required: false },
{
name: 'reveal_first_cell',
description: 'Would you like to automatically reveal the first cell?',
@@ -86,35 +58,23 @@ export default class MinesweeperCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
public override async exec(
message: BushMessage | BushSlashMessage,
- {
- rows,
- columns,
- mines,
- spaces,
- reveal_first_cell
- }: {
- rows: number;
- columns: number;
- mines: number;
- spaces: boolean;
- reveal_first_cell: boolean;
- }
+ args: { rows: number; columns: number; mines: number; spaces: boolean; reveal_first_cell: boolean }
): Promise<unknown> {
const minesweeper = new Minesweeper({
- rows: rows ?? 9,
- columns: columns ?? 9,
- mines: mines ?? 10,
+ rows: args.rows ?? 9,
+ columns: args.columns ?? 9,
+ mines: args.mines ?? 10,
emote: 'boom',
- revealFirstCell: reveal_first_cell ?? false,
+ revealFirstCell: args.reveal_first_cell ?? false,
zeroFirstCell: true,
- spaces: spaces ?? false,
+ spaces: args.spaces ?? false,
returnType: 'emoji'
});
const matrix = minesweeper.start();
diff --git a/src/commands/info/avatar.ts b/src/commands/info/avatar.ts
index 1e6496a..7625b61 100644
--- a/src/commands/info/avatar.ts
+++ b/src/commands/info/avatar.ts
@@ -22,7 +22,8 @@ export default class AvatarCommand extends BushCommand {
}
}
],
- clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
slash: true,
slashOptions: [
{
diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts
index 257dc90..ffb785c 100644
--- a/src/commands/info/botInfo.ts
+++ b/src/commands/info/botInfo.ts
@@ -14,8 +14,8 @@ export default class BotInfoCommand extends BushCommand {
examples: ['botinfo']
},
slash: true,
- clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts
index d641fd7..958aadd 100644
--- a/src/commands/info/color.ts
+++ b/src/commands/info/color.ts
@@ -31,7 +31,8 @@ export default class ColorCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/info/guildInfo.ts b/src/commands/info/guildInfo.ts
index f1db783..4a79918 100644
--- a/src/commands/info/guildInfo.ts
+++ b/src/commands/info/guildInfo.ts
@@ -31,8 +31,8 @@ export default class GuildInfoCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts
index ce171c9..5e80199 100644
--- a/src/commands/info/help.ts
+++ b/src/commands/info/help.ts
@@ -37,8 +37,8 @@ export default class HelpCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/info/icon.ts b/src/commands/info/icon.ts
index 677fdaf..ce27cc0 100644
--- a/src/commands/info/icon.ts
+++ b/src/commands/info/icon.ts
@@ -11,7 +11,8 @@ export default class IconCommand extends BushCommand {
usage: 'icon',
examples: 'icon'
},
- clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
channel: 'guild',
slash: true
});
diff --git a/src/commands/info/links.ts b/src/commands/info/links.ts
index b3a762a..569b959 100644
--- a/src/commands/info/links.ts
+++ b/src/commands/info/links.ts
@@ -14,7 +14,8 @@ export default class LinksCommand extends BushCommand {
},
ratelimit: 4,
cooldown: 4000,
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
slash: true
});
}
diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts
index 5f2220c..82db2ff 100644
--- a/src/commands/info/ping.ts
+++ b/src/commands/info/ping.ts
@@ -12,8 +12,8 @@ export default class PingCommand extends BushCommand {
examples: ['ping']
},
slash: true,
- clientPermissions: ['SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts
index 77612da..cd66530 100644
--- a/src/commands/info/pronouns.ts
+++ b/src/commands/info/pronouns.ts
@@ -1,5 +1,4 @@
import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
-import { Snowflake } from 'discord-api-types';
import { MessageEmbed, User } from 'discord.js';
export default class PronounsCommand extends BushCommand {
@@ -15,7 +14,7 @@ export default class PronounsCommand extends BushCommand {
args: [
{
id: 'user',
- customType: util.arg.union('user', 'snowflake'),
+ type: 'globalUser',
prompt: {
start: 'Who would you like to view the pronouns of?',
retry: '{error} Choose a valid user to view the pronouns of.',
@@ -23,7 +22,8 @@ export default class PronounsCommand extends BushCommand {
}
}
],
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
slashOptions: [
{
name: 'user',
@@ -35,18 +35,15 @@ export default class PronounsCommand extends BushCommand {
slash: true
});
}
- override async exec(message: BushMessage | BushSlashMessage, args: { user?: User | Snowflake }): Promise<unknown> {
- const user = (await util.resolveNonCachedUser(args.user)) ?? message.author;
-
- if (!user) return message.util.reply(`${util.emojis.error} Invalid user.`);
-
+ override async exec(message: BushMessage | BushSlashMessage, args: { user?: User }): Promise<unknown> {
+ const user = args.user ?? message.author;
const author = user.id === message.author.id;
const pronouns = await util.getPronounsOf(user);
if (!pronouns) {
return await message.util.reply(
- `${author ? 'You do' : `${user.tag} does`} not appear to have any pronouns set. Please ${
- author ? '' : 'tell them to'
+ `${author ? 'You do' : `${user.tag} does`} not appear to have any pronouns set. Please${
+ author ? '' : ' tell them to'
} go to https://pronoundb.org/ and set ${author ? 'your' : 'their'} pronouns.`
);
} else {
diff --git a/src/commands/info/snowflake.ts b/src/commands/info/snowflake.ts
index df11bce..81b3f9d 100644
--- a/src/commands/info/snowflake.ts
+++ b/src/commands/info/snowflake.ts
@@ -40,7 +40,8 @@ export default class SnowflakeCommand extends BushCommand {
}
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
slash: true,
slashOptions: [
{
diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts
index b55bbcf..353d844 100644
--- a/src/commands/info/userInfo.ts
+++ b/src/commands/info/userInfo.ts
@@ -33,8 +33,8 @@ export default class UserInfoCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/leveling/leaderboard.ts b/src/commands/leveling/leaderboard.ts
index 432fc60..e3344a0 100644
--- a/src/commands/leveling/leaderboard.ts
+++ b/src/commands/leveling/leaderboard.ts
@@ -1,5 +1,6 @@
import { BushCommand, BushMessage, BushSlashMessage, Level } from '@lib';
import { MessageEmbed } from 'discord.js';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class LeaderboardCommand extends BushCommand {
public constructor() {
@@ -32,8 +33,8 @@ export default class LeaderboardCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
@@ -62,6 +63,6 @@ export default class LeaderboardCommand extends BushCommand {
const embeds = chunked.map((c) =>
new MessageEmbed().setTitle(`${message.guild!.name}'s Leaderboard`).setDescription(c.join('\n'))
);
- return await util.buttonPaginate(message, embeds, undefined, true, args?.page ?? undefined);
+ return await ButtonPaginator.send(message, embeds, undefined, true, args?.page ?? undefined);
}
}
diff --git a/src/commands/leveling/level.ts b/src/commands/leveling/level.ts
index 2073e1a..4d7adb3 100644
--- a/src/commands/leveling/level.ts
+++ b/src/commands/leveling/level.ts
@@ -45,11 +45,42 @@ export default class LevelCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
+ public override async exec(message: BushMessage | BushSlashMessage, args: { user?: BushUser }): Promise<unknown> {
+ 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(
+ `${util.emojis.error} This command can only be run in servers with the leveling feature enabled.${
+ message.member?.permissions.has('MANAGE_GUILD')
+ ? ` You can toggle features using the \`${
+ message.util.isSlash
+ ? '/'
+ : client.config.isDevelopment
+ ? 'dev '
+ : message.util.parsed?.prefix ?? client.config.prefix
+ }features\` command.`
+ : ''
+ }`
+ );
+ const user = args.user ?? message.author;
+ try {
+ return await message.util.reply({
+ files: [new MessageAttachment(await this.getImage(user, message.guild!), 'level.png')]
+ });
+ } catch (e) {
+ if (e instanceof Error && e.message === 'User does not have a level') {
+ return await message.util.reply({
+ content: `${util.emojis.error} ${user} does not have a level.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ } else throw e;
+ }
+ }
+
private async getImage(user: BushUser, guild: BushGuild): Promise<Buffer> {
const guildRows = await Level.findAll({ where: { guild: guild.id } });
const rank = guildRows.sort((a, b) => b.xp - a.xp);
@@ -111,35 +142,4 @@ export default class LevelCommand extends BushCommand {
// Return image in buffer form
return levelCard.toBuffer();
}
-
- public override async exec(message: BushMessage | BushSlashMessage, args: { user?: BushUser }): Promise<unknown> {
- 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(
- `${util.emojis.error} This command can only be run in servers with the leveling feature enabled.${
- message.member?.permissions.has('MANAGE_GUILD')
- ? ` You can toggle features using the \`${
- message.util.isSlash
- ? '/'
- : client.config.isDevelopment
- ? 'dev '
- : message.util.parsed?.prefix ?? client.config.prefix
- }features\` command.`
- : ''
- }`
- );
- const user = args.user ?? message.author;
- try {
- return await message.util.reply({
- files: [new MessageAttachment(await this.getImage(user, message.guild!), 'level.png')]
- });
- } catch (e) {
- if (e instanceof Error && e.message === 'User does not have a level') {
- return await message.util.reply({
- content: `${util.emojis.error} ${user} does not have a level.`,
- allowedMentions: AllowedMentions.none()
- });
- } else throw e;
- }
- }
}
diff --git a/src/commands/leveling/setLevel.ts b/src/commands/leveling/setLevel.ts
index b98b488..9a7337a 100644
--- a/src/commands/leveling/setLevel.ts
+++ b/src/commands/leveling/setLevel.ts
@@ -45,8 +45,8 @@ export default class SetLevelCommand extends BushCommand {
],
slash: true,
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'ADMINISTRATOR']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['ADMINISTRATOR']
});
}
diff --git a/src/commands/leveling/setXp.ts b/src/commands/leveling/setXp.ts
index 3e00ea2..a73ae58 100644
--- a/src/commands/leveling/setXp.ts
+++ b/src/commands/leveling/setXp.ts
@@ -48,8 +48,8 @@ export default class SetXpCommand extends BushCommand {
],
slash: true,
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'ADMINISTRATOR']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: ['ADMINISTRATOR']
});
}
diff --git a/src/commands/moderation/_lockdown.ts b/src/commands/moderation/_lockdown.ts
index 5df9f18..0086ff6 100644
--- a/src/commands/moderation/_lockdown.ts
+++ b/src/commands/moderation/_lockdown.ts
@@ -27,15 +27,15 @@ export default class LockdownCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
hidden: true
});
}
- public override async exec(message: BushMessage | BushSlashMessage, { all }: { all: boolean }): Promise<unknown> {
+ public override async exec(message: BushMessage | BushSlashMessage, args: { all: boolean }): Promise<unknown> {
return await message.util.reply('Unfortunately my developer is too lazy to implement this command.');
- if (!all) {
+ if (!args.all) {
if (!['GUILD_TEXT', 'GUILD_NEWS'].includes(message.channel!.type))
return message.util.reply(`${util.emojis.error} You can only lock down text and announcement channels.`);
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts
index 4c2b3d3..b3d97d2 100644
--- a/src/commands/moderation/ban.ts
+++ b/src/commands/moderation/ban.ts
@@ -1,6 +1,6 @@
import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage } from '@lib';
import { Snowflake, User } from 'discord.js';
-import { Moderation } from '../../lib/common/moderation';
+import { Moderation } from '../../lib/common/Moderation';
export default class BanCommand extends BushCommand {
public constructor() {
diff --git a/src/commands/moderation/evidence.ts b/src/commands/moderation/evidence.ts
index 250df24..a681bc1 100644
--- a/src/commands/moderation/evidence.ts
+++ b/src/commands/moderation/evidence.ts
@@ -27,8 +27,8 @@ export default class EvidenceCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES'])
});
}
diff --git a/src/commands/moderation/hideCase.ts b/src/commands/moderation/hideCase.ts
index 2ed788a..38cfe31 100644
--- a/src/commands/moderation/hideCase.ts
+++ b/src/commands/moderation/hideCase.ts
@@ -20,10 +20,8 @@ export default class HideCaseCommand extends BushCommand {
}
}
],
- userPermissions: (message) => {
- return message.member?.permissions.has('MANAGE_MESSAGES') ? null : ['MANAGE_MESSAGES'];
- },
- clientPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']),
slash: true,
slashOptions: [
{
diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts
index 715483a..9bd5658 100644
--- a/src/commands/moderation/kick.ts
+++ b/src/commands/moderation/kick.ts
@@ -1,5 +1,5 @@
-import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushSlashMessage, BushUser } from '@lib';
-import { Moderation } from '../../lib/common/moderation';
+import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, BushUser } from '@lib';
+import { Moderation } from '../../lib/common/Moderation';
export default class KickCommand extends BushCommand {
public constructor() {
@@ -51,7 +51,7 @@ export default class KickCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['SEND_MESSAGES', 'KICK_MEMBERS'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['KICK_MEMBERS']),
userPermissions: ['KICK_MEMBERS']
});
}
@@ -60,7 +60,7 @@ export default class KickCommand extends BushCommand {
message: BushMessage | BushSlashMessage,
{ user, reason, force }: { user: BushUser; reason?: string; force: boolean }
): Promise<unknown> {
- const member = message.guild!.members.cache.get(user.id) as BushGuildMember;
+ const member = await message.guild!.members.fetch(user.id);
if (!member)
return await message.util.reply(
diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts
index d5c6f91..eb37681 100644
--- a/src/commands/moderation/modlog.ts
+++ b/src/commands/moderation/modlog.ts
@@ -1,5 +1,6 @@
import { BushCommand, BushMessage, BushSlashMessage, BushUser, ModLog } from '@lib';
import { MessageEmbed, User } from 'discord.js';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class ModlogCommand extends BushCommand {
public constructor() {
@@ -27,7 +28,8 @@ export default class ModlogCommand extends BushCommand {
default: false
}
],
- userPermissions: [],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES']),
slash: true,
slashOptions: [
{
@@ -62,10 +64,6 @@ export default class ModlogCommand extends BushCommand {
message: BushMessage | BushSlashMessage,
{ search, hidden }: { search: BushUser | string; hidden: boolean }
): Promise<unknown> {
- if (!message.member?.permissions.has('MANAGE_MESSAGES'))
- return await message.util.reply(
- `${util.emojis.error} You must have the **Manage Message** permission to use this command.`
- );
const foundUser = search instanceof User ? search : await util.resolveUserAsync(search);
if (foundUser) {
const logs = await ModLog.findAll({
@@ -90,7 +88,7 @@ export default class ModlogCommand extends BushCommand {
color: util.colors.default
})
);
- return await util.buttonPaginate(message, embedPages, undefined, true);
+ return await ButtonPaginator.send(message, embedPages, undefined, true);
} else if (search) {
const entry = await ModLog.findByPk(search as string);
if (!entry || entry.pseudo || (entry.hidden && !hidden))
@@ -102,7 +100,7 @@ export default class ModlogCommand extends BushCommand {
description: this.#generateModlogInfo(entry, true),
color: util.colors.default
};
- return await util.buttonPaginate(message, [embed]);
+ return await ButtonPaginator.send(message, [embed]);
}
}
}
diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts
index 942c0b0..03ecf2a 100644
--- a/src/commands/moderation/mute.ts
+++ b/src/commands/moderation/mute.ts
@@ -1,5 +1,5 @@
import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, BushUser } from '@lib';
-import { Moderation } from '../../lib/common/moderation';
+import { Moderation } from '../../lib/common/Moderation';
export default class MuteCommand extends BushCommand {
public constructor() {
@@ -52,8 +52,8 @@ export default class MuteCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES', 'MANAGE_ROLES'],
- userPermissions: ['MANAGE_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES'])
});
}
@@ -66,7 +66,7 @@ export default class MuteCommand extends BushCommand {
}: { user: BushUser; reason?: { duration: number | null; contentWithoutTime: string }; force: boolean }
): Promise<unknown> {
if (reason?.duration === null) reason.duration = 0;
- const member = message.guild!.members.cache.get(user.id);
+ const member = await message.guild!.members.fetch(user.id).catch(() => null);
if (!member)
return await message.util.reply(
`${util.emojis.error} The user you selected is not in the server or is not a valid user.`
diff --git a/src/commands/moderation/purge.ts b/src/commands/moderation/purge.ts
index 062dad0..a77e46a 100644
--- a/src/commands/moderation/purge.ts
+++ b/src/commands/moderation/purge.ts
@@ -37,7 +37,7 @@ export default class PurgeCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES', 'EMBED_LINKS'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES', 'EMBED_LINKS'], true),
userPermissions: ['MANAGE_MESSAGES'],
channel: 'guild'
});
diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts
index dc05883..63e20bd 100644
--- a/src/commands/moderation/removeReactionEmoji.ts
+++ b/src/commands/moderation/removeReactionEmoji.ts
@@ -11,8 +11,6 @@ export default class RemoveReactionEmojiCommand extends BushCommand {
usage: 'remove-reaction-emoji <message> <emoji>',
examples: ['remove-reaction-emoji 791413052347252786 <:omegaclown:782630946435366942>']
},
- clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['MANAGE_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS'], // Can't undo the removal of 1000s of reactions
args: [
{
id: 'messageToRemoveFrom',
@@ -32,7 +30,9 @@ export default class RemoveReactionEmojiCommand extends BushCommand {
}
}
],
- channel: 'guild'
+ channel: 'guild',
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES', 'EMBED_LINKS'], true),
+ userPermissions: ['MANAGE_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS'] // Can't undo the removal of 1000s of reactions
});
}
diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts
index c39864b..fe34d75 100644
--- a/src/commands/moderation/role.ts
+++ b/src/commands/moderation/role.ts
@@ -50,8 +50,8 @@ export default class RoleCommand extends BushCommand {
],
channel: 'guild',
typing: true,
- clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES', 'EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/moderation/slowmode.ts b/src/commands/moderation/slowmode.ts
index 94e40ca..d90f122 100644
--- a/src/commands/moderation/slowmode.ts
+++ b/src/commands/moderation/slowmode.ts
@@ -42,8 +42,8 @@ export default class SlowModeCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['MANAGE_CHANNELS', 'SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_CHANNELS', 'EMBED_LINKS'], true),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES'])
});
}
diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/unban.ts
index a319ff9..61e27cf 100644
--- a/src/commands/moderation/unban.ts
+++ b/src/commands/moderation/unban.ts
@@ -1,5 +1,4 @@
import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, BushUser } from '@lib';
-import { User } from 'discord.js';
export default class UnbanCommand extends BushCommand {
public constructor() {
@@ -14,7 +13,7 @@ export default class UnbanCommand extends BushCommand {
args: [
{
id: 'user',
- type: 'user',
+ type: 'globalUser',
prompt: {
start: 'What user would you like to unban?',
retry: '{error} Choose a valid user to unban.'
@@ -55,10 +54,6 @@ export default class UnbanCommand extends BushCommand {
message: BushMessage | BushSlashMessage,
{ user, reason }: { user: BushUser; reason?: string }
): Promise<unknown> {
- if (!(user instanceof User)) {
- user = util.resolveUser(user, client.users.cache) as BushUser;
- }
-
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 3d592b7..e430b83 100644
--- a/src/commands/moderation/unmute.ts
+++ b/src/commands/moderation/unmute.ts
@@ -1,5 +1,5 @@
import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushSlashMessage, BushUser } from '@lib';
-import { Moderation } from '../../lib/common/moderation';
+import { Moderation } from '../../lib/common/Moderation';
export default class UnmuteCommand extends BushCommand {
public constructor() {
@@ -52,8 +52,8 @@ export default class UnmuteCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES', 'MANAGE_ROLES'],
- userPermissions: ['MANAGE_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_ROLES']),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES'])
});
}
diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts
index 3df4b3b..6ae8442 100644
--- a/src/commands/moderation/warn.ts
+++ b/src/commands/moderation/warn.ts
@@ -1,5 +1,5 @@
import { BushCommand, BushGuildMember, BushMessage, BushSlashMessage, BushUser } from '@lib';
-import { Moderation } from '../../lib/common/moderation';
+import { Moderation } from '../../lib/common/Moderation';
export default class WarnCommand extends BushCommand {
public constructor() {
@@ -51,8 +51,8 @@ export default class WarnCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['MANAGE_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: (m) => util.userGuildPermCheck(m, ['MANAGE_MESSAGES'])
});
}
diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts
index 978d8e5..e26a5a4 100644
--- a/src/commands/moulberry-bush/capePerms.ts
+++ b/src/commands/moulberry-bush/capePerms.ts
@@ -23,7 +23,8 @@ export default class CapePermissionsCommand extends BushCommand {
}
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
channel: 'guild',
slash: true,
slashOptions: [
@@ -38,7 +39,7 @@ export default class CapePermissionsCommand extends BushCommand {
}
public override async exec(message: BushMessage | BushSlashMessage, args: { ign: string }): Promise<unknown> {
- interface Capeperms {
+ interface CapePerms {
success: boolean;
perms: User[];
}
@@ -48,7 +49,7 @@ export default class CapePermissionsCommand extends BushCommand {
perms: string[];
}
- let capeperms: Capeperms | null, uuid: string;
+ let capePerms: CapePerms | null, uuid: string;
try {
uuid = await util.findUUID(args.ign);
} catch (e) {
@@ -56,18 +57,18 @@ export default class CapePermissionsCommand extends BushCommand {
}
try {
- capeperms = await got.get('http://moulberry.codes/permscapes.json').json();
+ capePerms = await got.get('http://moulberry.codes/permscapes.json').json();
} catch (error) {
- capeperms = null;
+ capePerms = null;
}
- if (capeperms == null) {
+ if (capePerms == null) {
return await message.util.reply(`${util.emojis.error} There was an error finding cape perms for \`${args.ign}\`.`);
} else {
- if (capeperms?.perms) {
+ if (capePerms?.perms) {
let index = null;
- for (let i = 0; i < capeperms.perms.length; i++) {
- if (capeperms.perms[i]._id == uuid) {
+ for (let i = 0; i < capePerms.perms.length; i++) {
+ if (capePerms.perms[i]._id == uuid) {
index = i;
break;
}
@@ -75,7 +76,7 @@ export default class CapePermissionsCommand extends BushCommand {
}
if (index == null)
return await message.util.reply(`${util.emojis.error} \`${args.ign}\` does not appear to have any capes.`);
- const userPerm: string[] = capeperms.perms[index].perms;
+ const userPerm: string[] = capePerms.perms[index].perms;
const embed = new MessageEmbed()
.setTitle(`${args.ign}'s Capes`)
.setDescription(userPerm.join('\n'))
diff --git a/src/commands/moulberry-bush/capes.ts b/src/commands/moulberry-bush/capes.ts
index 14a972e..1a09eb0 100644
--- a/src/commands/moulberry-bush/capes.ts
+++ b/src/commands/moulberry-bush/capes.ts
@@ -1,6 +1,8 @@
-import { MessageEmbed } from 'discord.js';
+import { MessageEmbedOptions } from 'discord.js';
import got from 'got';
import { BushCommand, BushMessage } from '../../lib';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
+import { DeleteButton } from '../../lib/common/DeleteButton';
export interface GithubFile {
path: string;
@@ -58,8 +60,8 @@ export default class CapesCommand extends BushCommand {
required: false
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
@@ -102,30 +104,33 @@ export default class CapesCommand extends BushCommand {
}
return 0;
});
+
if (args.cape) {
- const capeObj = sortedCapes.find((s_cape) => s_cape.name === args.cape);
- if (capeObj) {
- const embed = new MessageEmbed({
- title: `${capeObj.name} cape`,
- color: util.colors.default
- }).setTimestamp();
- embed.setImage(capeObj.url);
- await util.sendWithDeleteButton(message, { embeds: [embed] });
+ const cape = sortedCapes.find((s_cape) => s_cape.name === args.cape);
+ if (cape) {
+ const embed = this.makeEmbed(cape);
+ await DeleteButton.send(message, { embeds: [embed] });
} else {
await message.util.reply(`${util.emojis.error} Cannot find a cape called \`${args.cape}\`.`);
}
} else {
- const embeds = [];
- for (const capeObj of sortedCapes) {
- const embed = new MessageEmbed({
- title: `${capeObj.name} cape`,
- color: util.colors.default
- }).setTimestamp();
- embed.setImage(capeObj.url);
- if (capeObj.purchasable) embed.setDescription(':money_with_wings: **purchasable** :money_with_wings:');
- embeds.push(embed);
- }
- await util.buttonPaginate(message, embeds, null);
+ const embeds: MessageEmbedOptions[] = sortedCapes.map(this.makeEmbed);
+ await ButtonPaginator.send(message, embeds, null);
}
}
+
+ private makeEmbed(cape: {
+ name: string;
+ url: string;
+ index: number;
+ purchasable?: boolean | undefined;
+ }): MessageEmbedOptions {
+ return {
+ title: `${cape.name} cape`,
+ color: util.colors.default,
+ timestamp: Date.now(),
+ image: { url: cape.url },
+ description: cape.purchasable ? ':money_with_wings: **purchasable** :money_with_wings:' : undefined
+ };
+ }
}
diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts
index 18f4acf..d6005b0 100644
--- a/src/commands/moulberry-bush/giveawayPing.ts
+++ b/src/commands/moulberry-bush/giveawayPing.ts
@@ -10,8 +10,8 @@ export default class GiveawayPingCommand extends BushCommand {
usage: 'giveaway-ping',
examples: ['giveaway-ping']
},
- clientPermissions: ['MANAGE_MESSAGES'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD', 'MANAGE_MESSAGES', 'BAN_MEMBERS', 'KICK_MEMBERS', 'VIEW_CHANNEL'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_MESSAGES'], true),
+ userPermissions: ['MANAGE_GUILD', 'MANAGE_MESSAGES', 'BAN_MEMBERS', 'KICK_MEMBERS', 'VIEW_CHANNEL'],
channel: 'guild',
ignoreCooldown: [],
ignorePermissions: [],
diff --git a/src/commands/moulberry-bush/moulHammer.ts b/src/commands/moulberry-bush/moulHammer.ts
index d5930b5..129eb84 100644
--- a/src/commands/moulberry-bush/moulHammer.ts
+++ b/src/commands/moulberry-bush/moulHammer.ts
@@ -11,8 +11,6 @@ export default class MoulHammerCommand extends BushCommand {
usage: 'moulHammer <user>',
examples: ['moulHammer @IRONM00N']
},
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
args: [
{
id: 'user',
@@ -23,7 +21,9 @@ export default class MoulHammerCommand extends BushCommand {
}
}
],
- restrictedGuilds: ['516977525906341928']
+ restrictedGuilds: ['516977525906341928'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts
index a5c4cb2..13976bb 100644
--- a/src/commands/moulberry-bush/report.ts
+++ b/src/commands/moulberry-bush/report.ts
@@ -33,8 +33,6 @@ export default class ReportCommand extends BushCommand {
}
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- channel: 'guild',
slash: true,
slashOptions: [
{
@@ -49,7 +47,10 @@ export default class ReportCommand extends BushCommand {
type: 'STRING',
required: false
}
- ]
+ ],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
+ channel: 'guild'
});
}
@@ -102,7 +103,6 @@ export default class ReportCommand extends BushCommand {
true
);
- //reusing code pog
if (message.attachments.size > 0) {
const fileName = message.attachments.first()!.name!.toLowerCase();
if (fileName.endsWith('.png') || fileName.endsWith('.jpg') || fileName.endsWith('.gif') || fileName.endsWith('.webp')) {
diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts
index 0c1e435..59c9e43 100644
--- a/src/commands/moulberry-bush/rule.ts
+++ b/src/commands/moulberry-bush/rule.ts
@@ -81,9 +81,6 @@ export default class RuleCommand extends BushCommand {
}
}
],
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
- channel: 'guild',
- restrictedGuilds: ['516977525906341928'],
slash: true,
slashOptions: [
{
@@ -99,7 +96,11 @@ export default class RuleCommand extends BushCommand {
required: false
}
],
- slashGuilds: ['516977525906341928']
+ slashGuilds: ['516977525906341928'],
+ channel: 'guild',
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
+ restrictedGuilds: ['516977525906341928']
});
}
diff --git a/src/commands/moulberry-bush/serverStatus.ts b/src/commands/moulberry-bush/serverStatus.ts
index 8ff9803..f4a80d4 100644
--- a/src/commands/moulberry-bush/serverStatus.ts
+++ b/src/commands/moulberry-bush/serverStatus.ts
@@ -14,7 +14,8 @@ export default class ServerStatusCommand extends BushCommand {
},
ratelimit: 4,
cooldown: 4000,
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
slash: true
});
}
diff --git a/src/commands/utilities/activity.ts b/src/commands/utilities/activity.ts
index de7e79f..c3839e5 100644
--- a/src/commands/utilities/activity.ts
+++ b/src/commands/utilities/activity.ts
@@ -100,8 +100,8 @@ export default class YouTubeCommand extends BushCommand {
choices: Object.keys(activityMap).map((key) => ({ name: key, value: activityMap[key as keyof typeof activityMap] }))
}
],
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/utilities/calculator.ts b/src/commands/utilities/calculator.ts
index a2c91e4..743e9b2 100644
--- a/src/commands/utilities/calculator.ts
+++ b/src/commands/utilities/calculator.ts
@@ -33,8 +33,8 @@ export default class CalculatorCommand extends BushCommand {
required: true
}
],
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
public override async exec(message: BushMessage | BushSlashMessage, args: { expression: string }): Promise<unknown> {
diff --git a/src/commands/utilities/decode.ts b/src/commands/utilities/decode.ts
index e48c644..c497183 100644
--- a/src/commands/utilities/decode.ts
+++ b/src/commands/utilities/decode.ts
@@ -3,7 +3,7 @@ import { AkairoMessage } from 'discord-akairo';
import { MessageEmbed } from 'discord.js';
const encodingTypesArray = ['ascii', 'utf8', 'utf-8', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'latin1', 'binary', 'hex'];
-const encodingTypesString = '`ascii`, `utf8`, `utf-8`, `utf16le`, `ucs2`, `ucs-2`, `base64`, `latin1`, `binary`, `hex`';
+const encodingTypesString = encodingTypesArray.map((e) => `\`${e}\``).join(', ');
export default class DecodeCommand extends BushCommand {
public constructor() {
@@ -42,43 +42,20 @@ export default class DecodeCommand extends BushCommand {
}
}
],
- clientPermissions: ['SEND_MESSAGES'],
slash: true,
slashOptions: [
{
name: 'from',
description: 'The type of data you are inputting.',
type: 'STRING',
- choices: [
- { name: 'ascii', value: 'ascii' },
- { name: 'utf8', value: 'utf8' },
- { name: 'utf-8', value: 'utf-8' },
- { name: 'utf16le', value: 'utf16le' },
- { name: 'ucs2', value: 'ucs2' },
- { name: 'ucs-2', value: 'ucs-2' },
- { name: 'base64', value: 'base64' },
- { name: 'latin1', value: 'latin1' },
- { name: 'binary', value: 'binary' },
- { name: 'hex', value: 'hex' }
- ],
+ choices: encodingTypesArray.map((e) => ({ name: e, value: e })),
required: true
},
{
name: 'to',
description: 'The type of data you want the output to be.',
type: 'STRING',
- choices: [
- { name: 'ascii', value: 'ascii' },
- { name: 'utf8', value: 'utf8' },
- { name: 'utf-8', value: 'utf-8' },
- { name: 'utf16le', value: 'utf16le' },
- { name: 'ucs2', value: 'ucs2' },
- { name: 'ucs-2', value: 'ucs-2' },
- { name: 'base64', value: 'base64' },
- { name: 'latin1', value: 'latin1' },
- { name: 'binary', value: 'binary' },
- { name: 'hex', value: 'hex' }
- ],
+ choices: encodingTypesArray.map((e) => ({ name: e, value: e })),
required: true
},
{
@@ -87,7 +64,9 @@ export default class DecodeCommand extends BushCommand {
type: 'STRING',
required: true
}
- ]
+ ],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/utilities/hash.ts b/src/commands/utilities/hash.ts
index 16ace93..df604c8 100644
--- a/src/commands/utilities/hash.ts
+++ b/src/commands/utilities/hash.ts
@@ -22,7 +22,8 @@ export default class HashCommand extends BushCommand {
}
}
],
- clientPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts
index 497935a..a012501 100644
--- a/src/commands/utilities/price.ts
+++ b/src/commands/utilities/price.ts
@@ -51,15 +51,11 @@ export default class PriceCommand extends BushCommand {
super('price', {
aliases: ['price'],
category: 'utilities',
- clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
description: {
usage: 'price <item id>',
examples: ['price ASPECT_OF_THE_END'],
content: 'Finds the price information of an item.'
},
- ratelimit: 4,
- cooldown: 4000,
- typing: true,
args: [
{
id: 'item',
@@ -91,7 +87,12 @@ export default class PriceCommand extends BushCommand {
type: 'BOOLEAN',
required: false
}
- ]
+ ],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: [],
+ ratelimit: 4,
+ cooldown: 4000,
+ typing: true
});
}
diff --git a/src/commands/utilities/steal.ts b/src/commands/utilities/steal.ts
index 7d61016..767534e 100644
--- a/src/commands/utilities/steal.ts
+++ b/src/commands/utilities/steal.ts
@@ -27,8 +27,8 @@ export default class StealCommand extends BushCommand {
],
slash: false,
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS'],
- userPermissions: ['SEND_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['MANAGE_EMOJIS_AND_STICKERS']),
+ userPermissions: ['MANAGE_EMOJIS_AND_STICKERS']
});
}
public override async exec(
diff --git a/src/commands/utilities/suicide.ts b/src/commands/utilities/suicide.ts
index 58119ef..9289b1c 100644
--- a/src/commands/utilities/suicide.ts
+++ b/src/commands/utilities/suicide.ts
@@ -12,8 +12,8 @@ export default class TemplateCommand extends BushCommand {
examples: ['suicide']
},
slash: true,
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
bypassChannelBlacklist: true
});
}
@@ -29,16 +29,20 @@ export default class TemplateCommand extends BushCommand {
)
.addField(
'**National Suicide Prevention Hotline (U.S.):**',
- `**Call:** 1-800-273-8255, available 24/7 for emotional support
-**Text: HOME** to 741741
-https://suicidepreventionlifeline.org/chat/
-
-**Outside the U.S**: Find a supportive resource on [this Wikipedia list of worldwide crisis hotlines](https://en.wikipedia.org/wiki/List_of_suicide_crisis_lines)`
+ [
+ '**Call:** 1-800-273-8255, available 24/7 for emotional support',
+ '**Text: HOME** to 741741',
+ 'https://suicidepreventionlifeline.org/chat/',
+ '',
+ '**Outside the U.S**: Find a supportive resource on [this Wikipedia list of worldwide crisis hotlines](https://en.wikipedia.org/wiki/List_of_suicide_crisis_lines)'
+ ].join('\n')
)
.addField(
'**More Support**',
- `For Substance Abuse Support, Eating Disorder Support & Child Abuse and Domestic Violence:
-[Click to go to Discord's Health & Safety Page](https://discord.com/safety/360044103771-Mental-health-on-Discord#h_01EGRGT08QSZ5BNCH2E9HN0NYV)`
+ [
+ 'For Substance Abuse Support, Eating Disorder Support & Child Abuse and Domestic Violence:',
+ "[Click to go to Discord's Health & Safety Page](https://discord.com/safety/360044103771-Mental-health-on-Discord#h_01EGRGT08QSZ5BNCH2E9HN0NYV)"
+ ].join('\n')
);
return (
diff --git a/src/commands/utilities/uuid.ts b/src/commands/utilities/uuid.ts
index 50280a0..9484729 100644
--- a/src/commands/utilities/uuid.ts
+++ b/src/commands/utilities/uuid.ts
@@ -14,7 +14,6 @@ export default class UuidCommand extends BushCommand {
{
id: 'ign',
customType: /\w{1,16}/im,
-
prompt: {
start: 'What ign would you like to find the uuid of?',
retry: '{error} Choose a valid ign.',
@@ -22,9 +21,8 @@ export default class UuidCommand extends BushCommand {
}
}
],
- cooldown: 4000,
- ratelimit: 1,
- clientPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
diff --git a/src/commands/utilities/viewraw.ts b/src/commands/utilities/viewraw.ts
index bd185d4..b7017c6 100644
--- a/src/commands/utilities/viewraw.ts
+++ b/src/commands/utilities/viewraw.ts
@@ -3,11 +3,9 @@ import { BushCommand, BushMessage, BushSlashMessage } from '../../lib';
export default class ViewRawCommand extends BushCommand {
public constructor() {
- super('viewraw', {
- aliases: ['viewraw'],
+ super('view-raw', {
+ aliases: ['view-raw', 'vr'],
category: 'utilities',
- clientPermissions: ['EMBED_LINKS'],
- channel: 'guild',
description: {
usage: 'viewraw <message id> <channel>',
examples: ['viewraw 322862723090219008'],
@@ -42,7 +40,37 @@ export default class ViewRawCommand extends BushCommand {
match: 'flag',
flag: '--js'
}
- ]
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'message',
+ description: 'What message would you like to view?',
+ type: 'STRING',
+ required: true
+ },
+ {
+ name: 'channel',
+ description: 'What channel is the message in?',
+ type: 'CHANNEL',
+ required: false
+ },
+ {
+ name: 'json',
+ description: 'Would you like to view the raw JSON message data?',
+ type: 'BOOLEAN',
+ required: false
+ },
+ {
+ name: 'js',
+ description: 'Would you like to view the raw message data?',
+ type: 'BOOLEAN',
+ required: false
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: (m) => util.clientSendAndPermCheck(m, ['EMBED_LINKS'], true),
+ userPermissions: []
});
}
diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts
index 1b72d65..bce88d6 100644
--- a/src/commands/utilities/whoHasRole.ts
+++ b/src/commands/utilities/whoHasRole.ts
@@ -1,5 +1,6 @@
import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
import { CommandInteraction, Role, Util } from 'discord.js';
+import { ButtonPaginator } from '../../lib/common/ButtonPaginator';
export default class WhoHasRoleCommand extends BushCommand {
public constructor() {
@@ -32,8 +33,8 @@ export default class WhoHasRoleCommand extends BushCommand {
}
],
channel: 'guild',
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES'],
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: [],
typing: true
});
}
@@ -51,6 +52,6 @@ export default class WhoHasRoleCommand extends BushCommand {
color
}));
- return await util.buttonPaginate(message, embedPages, null, true);
+ return await ButtonPaginator.send(message, embedPages, null, true);
}
}
diff --git a/src/commands/utilities/wolframAlpha.ts b/src/commands/utilities/wolframAlpha.ts
index 4a357b8..049dd60 100644
--- a/src/commands/utilities/wolframAlpha.ts
+++ b/src/commands/utilities/wolframAlpha.ts
@@ -1,5 +1,5 @@
import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage } from '@lib';
-import { CommandInteraction, MessageEmbed } from 'discord.js';
+import { CommandInteraction, MessageEmbed, MessageOptions } from 'discord.js';
import WolframAlphaAPI from 'wolfram-alpha-api';
export default class WolframAlphaCommand extends BushCommand {
@@ -13,6 +13,7 @@ export default class WolframAlphaCommand extends BushCommand {
examples: ['wolfram-alpha what is the population of france']
},
args: [
+ { id: 'image', match: 'flag', flag: '--image' },
{
id: 'expression',
type: 'string',
@@ -31,30 +32,49 @@ export default class WolframAlphaCommand extends BushCommand {
description: 'What would you like to look up?',
type: 'STRING',
required: true
+ },
+ {
+ name: 'image',
+ description: 'Would you like to use the Simple API instead of the Short Answers API?',
+ type: 'BOOLEAN',
+ required: false
}
],
- clientPermissions: ['SEND_MESSAGES'],
- userPermissions: ['SEND_MESSAGES']
+ clientPermissions: (m) => util.clientSendAndPermCheck(m),
+ userPermissions: []
});
}
- public override async exec(message: BushMessage | BushSlashMessage, args: { expression: string }): Promise<unknown> {
+ public override async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { expression: string; image: boolean }
+ ): Promise<unknown> {
if (message.util.isSlash) await (message.interaction as CommandInteraction).deferReply();
+ args.image && void message.util.reply({ content: `${util.emojis.loading} Loading...`, embeds: [] });
const waApi = WolframAlphaAPI(client.config.credentials.wolframAlphaAppId);
const decodedEmbed = new MessageEmbed().addField('📥 Input', await util.inspectCleanRedactCodeblock(args.expression));
+ const sendOptions: MessageOptions = { content: null, allowedMentions: AllowedMentions.none() };
try {
- const calculated = await waApi.getShort(args.expression);
- decodedEmbed
- .setTitle(`${util.emojis.successFull} Successfully Queried Expression`)
- .setColor(util.colors.success)
- .addField('📤 Output', await util.inspectCleanRedactCodeblock(calculated.toString()));
+ const calculated = await (args.image
+ ? waApi.getSimple({ i: args.expression, timeout: 1, background: '2C2F33', foreground: 'white' })
+ : waApi.getShort(args.expression));
+ decodedEmbed.setTitle(`${util.emojis.successFull} Successfully Queried Expression`).setColor(util.colors.success);
+
+ if (args.image) {
+ decodedEmbed.setImage(await util.uploadImageToImgur(calculated.split(',')[1]));
+ decodedEmbed.addField('📤 Output', '​');
+ } else {
+ decodedEmbed.addField('📤 Output', await util.inspectCleanRedactCodeblock(calculated.toString()));
+ }
} catch (error) {
decodedEmbed
.setTitle(`${util.emojis.errorFull} Unable to Query Expression`)
.setColor(util.colors.error)
.addField(`📤 Error`, await util.inspectCleanRedactCodeblock(`${error.name}: ${error.message}`, 'js'));
}
- return await message.util.reply({ embeds: [decodedEmbed], allowedMentions: AllowedMentions.none() });
+ sendOptions.embeds = [decodedEmbed];
+
+ return await message.util.reply(sendOptions);
}
}
diff --git a/src/config/example-options.ts b/src/config/example-options.ts
index c7838ab..01519b2 100644
--- a/src/config/example-options.ts
+++ b/src/config/example-options.ts
@@ -6,7 +6,9 @@ export default new Config({
betaToken: '[TOKEN]',
devToken: '[TOKEN]',
hypixelApiKey: '[API_KEY]',
- wolframAlphaAppId: '[APP_ID]'
+ wolframAlphaAppId: '[APP_ID]',
+ imgurClientId: '[CLIENT_ID]',
+ imgurClientSecret: '[CLIENT_SECRET]'
},
environment: 'development',
owners: [
diff --git a/src/context-menu-commands/message/viewRaw.ts b/src/context-menu-commands/message/viewRaw.ts
index c04ec3c..77fd0b9 100644
--- a/src/context-menu-commands/message/viewRaw.ts
+++ b/src/context-menu-commands/message/viewRaw.ts
@@ -1,6 +1,6 @@
import { ContextMenuCommand } from 'discord-akairo';
import { ContextMenuInteraction } from 'discord.js';
-import ViewRawCommand from '../../commands/utilities/viewraw';
+import ViewRawCommand from '../../commands/utilities/viewRaw';
import { BushMessage } from '../../lib';
export default class ViewRawContextMenuCommand extends ContextMenuCommand {
diff --git a/src/lib/badlinks.ts b/src/lib/badlinks.ts
index 933fce3..059d3d9 100644
--- a/src/lib/badlinks.ts
+++ b/src/lib/badlinks.ts
@@ -792,6 +792,7 @@ export default [
"discordcreators.net",
"discordd.buzz",
"discordd.gg",
+ "discordd.gift",
"discorddaapp.com",
"discorddev.com",
"discorddiscord.com",
diff --git a/src/lib/badwords.ts b/src/lib/badwords.ts
index 975df24..e5033d7 100644
--- a/src/lib/badwords.ts
+++ b/src/lib/badwords.ts
@@ -1,4 +1,4 @@
-import { BadWords, Severity } from "./common/autoMod";
+import { BadWords, Severity } from "./common/AutoMod";
export default {
/* -------------------------------------------------------------------------- */
diff --git a/src/lib/common/ButtonPaginator.ts b/src/lib/common/ButtonPaginator.ts
new file mode 100644
index 0000000..c74f6ad
--- /dev/null
+++ b/src/lib/common/ButtonPaginator.ts
@@ -0,0 +1,186 @@
+import {
+ Constants,
+ MessageActionRow,
+ MessageButton,
+ MessageComponentInteraction,
+ MessageEmbed,
+ MessageEmbedOptions
+} from 'discord.js';
+import { BushMessage, BushSlashMessage } from '..';
+import { DeleteButton } from './DeleteButton';
+
+export class ButtonPaginator {
+ protected message: BushMessage | BushSlashMessage;
+ protected embeds: MessageEmbed[] | MessageEmbedOptions[];
+ protected text: string | null;
+ protected deleteOnExit: boolean;
+ protected curPage: number;
+ protected sentMessage: BushMessage | undefined;
+
+ /**
+ * Sends multiple embeds with controls to switch between them
+ * @param message - The message to respond to
+ * @param embeds - The embeds to switch between
+ * @param text - The text send with the embeds (optional)
+ * @param deleteOnExit - Whether to delete the message when the exit button is clicked (defaults to true)
+ * @param startOn - The page to start from (**not** the index)
+ */
+ public static async send(
+ message: BushMessage | BushSlashMessage,
+ embeds: MessageEmbed[] | MessageEmbedOptions[],
+ text: string | null = null,
+ deleteOnExit = true,
+ startOn = 1
+ ): Promise<void> {
+ // no need to paginate if there is only one page
+ if (embeds.length === 1) return DeleteButton.send(message, { embeds: embeds });
+
+ return await new ButtonPaginator(message, embeds, text, deleteOnExit, startOn).send();
+ }
+
+ protected get numPages(): number {
+ return this.embeds.length;
+ }
+
+ protected constructor(
+ message: BushMessage | BushSlashMessage,
+ embeds: MessageEmbed[] | MessageEmbedOptions[],
+ text: string | null,
+ deleteOnExit: boolean,
+ startOn: number
+ ) {
+ this.message = message;
+ this.embeds = embeds;
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ this.text = text || null;
+ this.deleteOnExit = deleteOnExit;
+ this.curPage = startOn - 1;
+
+ // add footers
+ for (let i = 0; i < embeds.length; i++) {
+ if (embeds[i] instanceof MessageEmbed) {
+ (embeds[i] as MessageEmbed).setFooter(`Page ${(i + 1).toLocaleString()}/${embeds.length.toLocaleString()}`);
+ } else {
+ (embeds[i] as MessageEmbedOptions).footer = {
+ text: `Page ${(i + 1).toLocaleString()}/${embeds.length.toLocaleString()}`
+ };
+ }
+ }
+ }
+
+ protected async send() {
+ this.sentMessage = (await this.message.util.reply({
+ content: this.text,
+ embeds: [this.embeds[this.curPage]],
+ components: [this.getPaginationRow()]
+ })) as BushMessage;
+
+ const collector = this.sentMessage.createMessageComponentCollector({
+ filter: (i) => i.customId.startsWith('paginate_') && i.message.id === this.sentMessage!.id,
+ time: 300000
+ });
+
+ collector.on('collect', (i) => void this.collect(i));
+ collector.on('end', () => void this.end());
+ }
+
+ protected async collect(interaction: MessageComponentInteraction) {
+ if (interaction.user.id !== this.message.author.id || !client.config.owners.includes(interaction.user.id))
+ return await interaction?.deferUpdate().catch(() => undefined);
+
+ switch (interaction.customId) {
+ case 'paginate_beginning':
+ this.curPage = 0;
+ return this.edit(interaction);
+ case 'paginate_back':
+ this.curPage--;
+ return await this.edit(interaction);
+ case 'paginate_stop':
+ if (this.deleteOnExit) {
+ await interaction.deferUpdate().catch(() => undefined);
+ return await this.sentMessage!.delete().catch(() => undefined);
+ } else {
+ return await interaction
+ ?.update({
+ content: `${this.text ? `${this.text}\n` : ''}Command closed by user.`,
+ embeds: [],
+ components: []
+ })
+ .catch(() => undefined);
+ }
+ case 'paginate_next':
+ this.curPage++;
+ return await this.edit(interaction);
+ case 'paginate_end':
+ this.curPage = this.embeds.length - 1;
+ return await this.edit(interaction);
+ }
+ }
+
+ protected async end() {
+ try {
+ return this.sentMessage!.edit({
+ content: this.text,
+ embeds: [this.embeds[this.curPage]],
+ components: [this.getPaginationRow(true)]
+ });
+ } catch (e) {
+ return undefined;
+ }
+ }
+
+ protected async edit(interaction: MessageComponentInteraction) {
+ try {
+ return interaction?.update({
+ content: this.text,
+ embeds: [this.embeds[this.curPage]],
+ components: [this.getPaginationRow()]
+ });
+ } catch (e) {
+ return undefined;
+ }
+ }
+
+ protected getPaginationRow(disableAll = false): MessageActionRow {
+ return new MessageActionRow().addComponents(
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate_beginning',
+ emoji: PaginateEmojis.BEGGING,
+ disabled: disableAll || this.curPage === 0
+ }),
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate_back',
+ emoji: PaginateEmojis.BACK,
+ disabled: disableAll || this.curPage === 0
+ }),
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate_stop',
+ emoji: PaginateEmojis.STOP,
+ disabled: disableAll
+ }),
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate_next',
+ emoji: PaginateEmojis.FORWARD,
+ disabled: disableAll || this.curPage === this.numPages - 1
+ }),
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate_end',
+ emoji: PaginateEmojis.END,
+ disabled: disableAll || this.curPage === this.numPages - 1
+ })
+ );
+ }
+}
+
+export const enum PaginateEmojis {
+ BEGGING = '853667381335162910',
+ BACK = '853667410203770881',
+ STOP = '853667471110570034',
+ FORWARD = '853667492680564747',
+ END = '853667514915225640'
+}
diff --git a/src/lib/common/DeleteButton.ts b/src/lib/common/DeleteButton.ts
new file mode 100644
index 0000000..7d2e41b
--- /dev/null
+++ b/src/lib/common/DeleteButton.ts
@@ -0,0 +1,61 @@
+import { Constants, MessageActionRow, MessageButton, MessageComponentInteraction, MessageOptions } from 'discord.js';
+import { BushMessage, BushSlashMessage } from '..';
+import { PaginateEmojis } from './ButtonPaginator';
+
+export class DeleteButton {
+ protected messageOptions: MessageOptions;
+ protected message: BushMessage | BushSlashMessage;
+
+ /**
+ * Sends a message with a button for the user to delete it.
+ * @param message - The message to respond to
+ * @param options - The send message options
+ */
+ static async send(message: BushMessage | BushSlashMessage, options: Omit<MessageOptions, 'components'>) {
+ return new DeleteButton(message, options).send();
+ }
+
+ protected constructor(message: BushMessage | BushSlashMessage, options: MessageOptions) {
+ this.message = message;
+ this.messageOptions = options;
+ }
+
+ protected async send() {
+ this.updateComponents();
+
+ const msg = (await this.message.util.reply(this.messageOptions)) as BushMessage;
+
+ const collector = msg.createMessageComponentCollector({
+ filter: (interaction) => interaction.customId == 'paginate__stop' && interaction.message.id == msg.id,
+ time: 300000
+ });
+
+ collector.on('collect', async (interaction: MessageComponentInteraction) => {
+ await interaction.deferUpdate().catch(() => undefined);
+ if (interaction.user.id == this.message.author.id || client.config.owners.includes(interaction.user.id)) {
+ if (msg.deletable && !msg.deleted) await msg.delete();
+ }
+ });
+
+ collector.on('end', async () => {
+ this.updateComponents(true, true);
+ await msg.edit(this.messageOptions).catch(() => undefined);
+ });
+ }
+
+ protected updateComponents(edit = false, disable = false): void {
+ this.messageOptions.components = [
+ new MessageActionRow().addComponents(
+ new MessageButton({
+ style: Constants.MessageButtonStyles.PRIMARY,
+ customId: 'paginate__stop',
+ emoji: PaginateEmojis.STOP,
+ disabled: disable
+ })
+ )
+ ];
+ if (edit) {
+ this.messageOptions.reply = undefined;
+ }
+ }
+}
diff --git a/src/lib/common/Format.ts b/src/lib/common/Format.ts
new file mode 100644
index 0000000..ba1ee9f
--- /dev/null
+++ b/src/lib/common/Format.ts
@@ -0,0 +1,72 @@
+import { Formatters, Util } from 'discord.js';
+import { CodeBlockLang } from './typings/CodeBlockLang';
+
+/**
+ * Formats and escapes content for formatting
+ */
+export class Format {
+ /**
+ * Wraps the content inside a codeblock with no language.
+ * @param content The content to wrap.
+ */
+ public static codeBlock(content: string): string;
+ /**
+ * Wraps the content inside a codeblock with the specified language.
+ * @param language The language for the codeblock.
+ * @param content The content to wrap.
+ */
+ public static codeBlock(language: CodeBlockLang, content: string): string;
+ public static codeBlock(languageOrContent: string, content?: string): string {
+ return typeof content === 'undefined'
+ ? Formatters.codeBlock(Util.escapeCodeBlock(languageOrContent))
+ : Formatters.codeBlock(languageOrContent, Util.escapeCodeBlock(languageOrContent));
+ }
+
+ /**
+ * Wraps the content inside \`backticks\`, which formats it as inline code.
+ * @param content The content to wrap.
+ */
+ public static inlineCode(content: string): string {
+ return Formatters.inlineCode(Util.escapeInlineCode(content));
+ }
+
+ /**
+ * Formats the content into italic text.
+ * @param content The content to wrap.
+ */
+ public static italic(content: string): string {
+ return Formatters.italic(Util.escapeItalic(content));
+ }
+
+ /**
+ * Formats the content into bold text.
+ * @param content The content to wrap.
+ */
+ public static bold(content: string): string {
+ return Formatters.bold(Util.escapeBold(content));
+ }
+
+ /**
+ * Formats the content into underscored text.
+ * @param content The content to wrap.
+ */
+ public static underscore(content: string): string {
+ return Formatters.underscore(Util.escapeUnderline(content));
+ }
+
+ /**
+ * Formats the content into strike-through text.
+ * @param content The content to wrap.
+ */
+ public static strikethrough(content: string): string {
+ return Formatters.strikethrough(Util.escapeStrikethrough(content));
+ }
+
+ /**
+ * Wraps the content inside spoiler (hidden text).
+ * @param content The content to wrap.
+ */
+ public static spoiler(content: string): string {
+ return Formatters.spoiler(Util.escapeSpoiler(content));
+ }
+}
diff --git a/src/lib/common/autoMod.ts b/src/lib/common/autoMod.ts
index 0bdbebf..312beb3 100644
--- a/src/lib/common/autoMod.ts
+++ b/src/lib/common/autoMod.ts
@@ -1,17 +1,17 @@
-import { Formatters, MessageActionRow, MessageButton, MessageEmbed, TextChannel } from 'discord.js';
-import badLinksArray from '../../lib/badlinks';
-import badLinksSecretArray from '../../lib/badlinks-secret'; // I cannot make this public so just make a new file that export defaults an empty array
-import badWords from '../../lib/badwords';
+import { GuildMember, MessageActionRow, MessageButton, MessageEmbed, TextChannel } from 'discord.js';
+import badLinksArray from '../badlinks';
+import badLinksSecretArray from '../badlinks-secret'; // I cannot make this public so just make a new file that export defaults an empty array
+import badWords from '../badwords';
import { BushButtonInteraction } from '../extensions/discord.js/BushButtonInteraction';
-import { BushGuildMember } from '../extensions/discord.js/BushGuildMember';
import { BushMessage } from '../extensions/discord.js/BushMessage';
-import { Moderation } from './moderation';
+import { Moderation } from './Moderation';
export class AutoMod {
private message: BushMessage;
public constructor(message: BushMessage) {
this.message = message;
+ if (message.author.id === client.user?.id) return;
void this.handle();
}
@@ -21,17 +21,10 @@ export class AutoMod {
const customAutomodPhrases = (await this.message.guild.getSetting('autoModPhases')) ?? {};
const badLinks: BadWords = {};
- const badLinksSecret: BadWords = {};
- badLinksArray.forEach((link) => {
- badLinks[link] = {
- severity: Severity.PERM_MUTE,
- ignoreSpaces: true,
- ignoreCapitalization: true,
- reason: 'malicious link'
- };
- });
- badLinksSecretArray.forEach((link) => {
+ const uniqueLinks = [...new Set([...badLinksArray, ...badLinksSecretArray])];
+
+ uniqueLinks.forEach((link) => {
badLinks[link] = {
severity: Severity.PERM_MUTE,
ignoreSpaces: true,
@@ -43,9 +36,7 @@ export class AutoMod {
const result = {
...this.checkWords(customAutomodPhrases),
...this.checkWords((await this.message.guild.hasFeature('excludeDefaultAutomod')) ? {} : badWords),
- ...this.checkWords(
- (await this.message.guild.hasFeature('excludeAutomodScamLinks')) ? {} : { ...badLinks, ...badLinksSecret }
- )
+ ...this.checkWords((await this.message.guild.hasFeature('excludeAutomodScamLinks')) ? {} : badLinks)
};
if (Object.keys(result).length === 0) return;
@@ -59,9 +50,7 @@ export class AutoMod {
embeds: [
{
title: 'AutoMod Error',
- description: `Unable to find severity information for ${Formatters.inlineCode(
- util.discord.escapeInlineCode(highestOffence.word)
- )}`,
+ description: `Unable to find severity information for ${util.format.inlineCode(highestOffence.word)}`,
color: util.colors.error
}
]
@@ -128,7 +117,7 @@ export class AutoMod {
break;
}
default: {
- throw new Error('Invalid severity');
+ throw new Error(`Invalid severity: ${highestOffence.severity}`);
}
}
@@ -163,8 +152,8 @@ export class AutoMod {
.setDescription(
`**User:** ${this.message.author} (${this.message.author.tag})\n**Sent From**: <#${
this.message.channel.id
- }> [Jump to context](${this.message.url})\n**Blacklisted Words:** ${util
- .surroundArray(Object.keys(offences), '`')
+ }> [Jump to context](${this.message.url})\n**Blacklisted Words:** ${Object.keys(offences)
+ .map((key) => `\`${key}\``)
.join(', ')}`
)
.addField('Message Content', `${await util.codeblock(this.message.content, 1024)}`)
@@ -194,12 +183,13 @@ export class AutoMod {
const [action, userId, reason] = interaction.customId.replace('automod;', '').split(';');
switch (action) {
case 'ban': {
- const check = await Moderation.permissionCheck(
- interaction.member as BushGuildMember,
- interaction.guild!.members.cache.get(userId)!,
- 'ban',
- true
- );
+ const victim = await interaction.guild!.members.fetch(userId);
+ const moderator =
+ interaction.member instanceof GuildMember
+ ? interaction.member
+ : await interaction.guild!.members.fetch(interaction.user.id);
+
+ const check = victim ? await Moderation.permissionCheck(moderator, victim, 'ban', true) : true;
if (check !== true)
return interaction.reply({
diff --git a/src/lib/common/moderation.ts b/src/lib/common/moderation.ts
index c8779fc..29d66fa 100644
--- a/src/lib/common/moderation.ts
+++ b/src/lib/common/moderation.ts
@@ -123,7 +123,7 @@ export class Moderation {
const expires = options.duration ? new Date(+new Date() + options.duration ?? 0) : undefined;
const user = (await util.resolveNonCachedUser(options.user))!.id;
const guild = client.guilds.resolveId(options.guild)!;
- const type = this.#findTypeEnum(options.type)!;
+ const type = this.findTypeEnum(options.type)!;
const entry = ActivePunishment.build(
options.extraInfo
@@ -144,7 +144,7 @@ export class Moderation {
}): Promise<boolean> {
const user = await util.resolveNonCachedUser(options.user);
const guild = client.guilds.resolveId(options.guild);
- const type = this.#findTypeEnum(options.type);
+ const type = this.findTypeEnum(options.type);
if (!user || !guild) return false;
@@ -160,18 +160,19 @@ export class Moderation {
success = false;
});
if (entries) {
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
- entries.forEach(async (entry) => {
- await entry.destroy().catch(async (e) => {
+ const promises = entries.map(async (entry) =>
+ entry.destroy().catch(async (e) => {
await util.handleError('removePunishmentEntry', e);
- });
- success = false;
- });
+ success = false;
+ })
+ );
+
+ await Promise.all(promises);
}
return success;
}
- static #findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') {
+ private static findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') {
const typeMap = {
['mute']: ActivePunishmentType.MUTE,
['ban']: ActivePunishmentType.BAN,
diff --git a/src/lib/common/typings/BushInspectOptions.d.ts b/src/lib/common/typings/BushInspectOptions.d.ts
new file mode 100644
index 0000000..c2a2360
--- /dev/null
+++ b/src/lib/common/typings/BushInspectOptions.d.ts
@@ -0,0 +1,91 @@
+import { InspectOptions } from 'util';
+
+/**
+ * {@link https://nodejs.org/api/util.html#util_util_inspect_object_options}
+ */
+export interface BushInspectOptions extends InspectOptions {
+ /**
+ * If `true`, object's non-enumerable symbols and properties are included in the
+ * formatted result. [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) and [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) entries are also included as well as
+ * user defined prototype properties (excluding method properties).
+ *
+ * **Default**: `false`.
+ */
+ showHidden?: boolean | undefined;
+ /**
+ * Specifies the number of times to recurse while formatting `object`. This is useful
+ * for inspecting large objects. To recurse up to the maximum call stack size pass
+ * `Infinity` or `null`.
+ *
+ * **Default**: `2`.
+ */
+ depth?: number | null | undefined;
+ /**
+ * If `true`, the output is styled with ANSI color codes. Colors are customizable. See [Customizing util.inspect colors](https://nodejs.org/api/util.html#util_customizing_util_inspect_colors).
+ *
+ * **Default**: `false`.
+ */
+ colors?: boolean | undefined;
+ /**
+ * If `false`, `[util.inspect.custom](depth, opts)` functions are not invoked.
+ *
+ * **Default**: `true`.
+ */
+ customInspect?: boolean | undefined;
+ /**
+ * If `true`, `Proxy` inspection includes the [`target` and `handler`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology) objects.
+ *
+ * **Default**: `false`.
+ */
+ showProxy?: boolean | undefined;
+ /**
+ * Specifies the maximum number of `Array`, [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) and
+ * [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) elements to include when formatting. Set to `null` or `Infinity` to
+ * show all elements. Set to `0` or negative to show no elements.
+ *
+ * **Default**: `100`.
+ */
+ maxArrayLength?: number | null | undefined;
+ /**
+ * Specifies the maximum number of characters to include when formatting. Set to
+ * `null` or `Infinity` to show all elements. Set to `0` or negative to show no
+ * characters.
+ *
+ * **Default**: `10000`.
+ */
+ maxStringLength?: number | null | undefined;
+ /**
+ * The length at which input values are split across multiple lines. Set to
+ * `Infinity` to format the input as a single line (in combination with compact set
+ * to `true` or any number >= `1`).
+ *
+ * **Default**: `80`.
+ */
+ breakLength?: number | undefined;
+ /**
+ * Setting this to `false` causes each object key to be displayed on a new line. It
+ * will break on new lines in text that is longer than `breakLength`. If set to a
+ * number, the most `n` inner elements are united on a single line as long as all
+ * properties fit into `breakLength`. Short array elements are also grouped together.
+ *
+ * **Default**: `3`
+ */
+ compact?: boolean | number | undefined;
+ /**
+ * If set to `true` or a function, all properties of an object, and `Set` and `Map`
+ * entries are sorted in the resulting string. If set to `true` the [default sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) is used.
+ * If set to a function, it is used as a [compare function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters).
+ *
+ * **Default**: `false`.
+ */
+ sorted?: boolean | ((a: string, b: string) => number) | undefined;
+ /**
+ * If set to `true`, getters are inspected. If set to `'get'`, only getters without a
+ * corresponding setter are inspected. If set to `'set'`, only getters with a
+ * corresponding setter are inspected. This might cause side effects depending on
+ * the getter function.
+ *
+ * **Default**: `false`.
+ */
+ getters?: 'get' | 'set' | boolean | undefined;
+}
diff --git a/src/lib/common/typings/CodeBlockLang.d.ts b/src/lib/common/typings/CodeBlockLang.d.ts
new file mode 100644
index 0000000..5a1aeba
--- /dev/null
+++ b/src/lib/common/typings/CodeBlockLang.d.ts
@@ -0,0 +1,310 @@
+export type CodeBlockLang =
+ | '1c'
+ | 'abnf'
+ | 'accesslog'
+ | 'actionscript'
+ | 'ada'
+ | 'arduino'
+ | 'ino'
+ | 'armasm'
+ | 'arm'
+ | 'avrasm'
+ | 'actionscript'
+ | 'as'
+ | 'angelscript'
+ | 'asc'
+ | 'apache'
+ | 'apacheconf'
+ | 'applescript'
+ | 'osascript'
+ | 'arcade'
+ | 'asciidoc'
+ | 'adoc'
+ | 'aspectj'
+ | 'autohotkey'
+ | 'autoit'
+ | 'awk'
+ | 'mawk'
+ | 'nawk'
+ | 'gawk'
+ | 'bash'
+ | 'sh'
+ | 'zsh'
+ | 'basic'
+ | 'bnf'
+ | 'brainfuck'
+ | 'bf'
+ | 'csharp'
+ | 'cs'
+ | 'c'
+ | 'h'
+ | 'cpp'
+ | 'hpp'
+ | 'cc'
+ | 'hh'
+ | 'c++'
+ | 'h++'
+ | 'cxx'
+ | 'hxx'
+ | 'cal'
+ | 'cos'
+ | 'cls'
+ | 'cmake'
+ | 'cmake.in'
+ | 'coq'
+ | 'csp'
+ | 'css'
+ | 'capnproto'
+ | 'capnp'
+ | 'clojure'
+ | 'clj'
+ | 'coffeescript'
+ | 'coffee'
+ | 'cson'
+ | 'iced'
+ | 'crmsh'
+ | 'crm'
+ | 'pcmk'
+ | 'crystal'
+ | 'cr'
+ | 'd'
+ | 'dns'
+ | 'zone'
+ | 'bind'
+ | 'dos'
+ | 'bat'
+ | 'cmd'
+ | 'dart'
+ | 'dpr'
+ | 'dfm'
+ | 'pas'
+ | 'pascal'
+ | 'diff'
+ | 'patch'
+ | 'django'
+ | 'jinja'
+ | 'dockerfile'
+ | 'docker'
+ | 'dsconfig'
+ | 'dts'
+ | 'dust'
+ | 'dst'
+ | 'ebnf'
+ | 'elixir'
+ | 'elm'
+ | 'erlang'
+ | 'erl'
+ | 'excel'
+ | 'xls'
+ | 'xlsx'
+ | 'fsharp'
+ | 'fs'
+ | 'fix'
+ | 'fortran'
+ | 'f90'
+ | 'f95'
+ | 'gcode'
+ | 'nc'
+ | 'gams'
+ | 'gms'
+ | 'gauss'
+ | 'gss'
+ | 'gherkin'
+ | 'go'
+ | 'golang'
+ | 'golo'
+ | 'gololang'
+ | 'gradle'
+ | 'groovy'
+ | 'xml'
+ | 'html'
+ | 'xhtml'
+ | 'rss'
+ | 'atom'
+ | 'xjb'
+ | 'xsd'
+ | 'xsl'
+ | 'plist'
+ | 'svg'
+ | 'http'
+ | 'https'
+ | 'haml'
+ | 'handlebars'
+ | 'hbs'
+ | 'html.hbs'
+ | 'html.handlebars'
+ | 'haskell'
+ | 'hs'
+ | 'haxe'
+ | 'hx'
+ | 'hlsl'
+ | 'hy'
+ | 'hylang'
+ | 'ini'
+ | 'toml'
+ | 'inform7'
+ | 'i7'
+ | 'irpf90'
+ | 'json'
+ | 'java'
+ | 'jsp'
+ | 'javascript'
+ | 'js'
+ | 'jsx'
+ | 'julia'
+ | 'julia-repl'
+ | 'kotlin'
+ | 'kt'
+ | 'tex'
+ | 'leaf'
+ | 'lasso'
+ | 'ls'
+ | 'lassoscript'
+ | 'less'
+ | 'ldif'
+ | 'lisp'
+ | 'livecodeserver'
+ | 'livescript'
+ | 'ls'
+ | 'lua'
+ | 'makefile'
+ | 'mk'
+ | 'mak'
+ | 'make'
+ | 'markdown'
+ | 'md'
+ | 'mkdown'
+ | 'mkd'
+ | 'mathematica'
+ | 'mma'
+ | 'wl'
+ | 'matlab'
+ | 'maxima'
+ | 'mel'
+ | 'mercury'
+ | 'mizar'
+ | 'mojolicious'
+ | 'monkey'
+ | 'moonscript'
+ | 'moon'
+ | 'n1ql'
+ | 'nsis'
+ | 'nginx'
+ | 'nginxconf'
+ | 'nim'
+ | 'nimrod'
+ | 'nix'
+ | 'ocaml'
+ | 'ml'
+ | 'objectivec'
+ | 'mm'
+ | 'objc'
+ | 'obj-c'
+ | 'obj-c++'
+ | 'objective-c++'
+ | 'glsl'
+ | 'openscad'
+ | 'scad'
+ | 'ruleslanguage'
+ | 'oxygene'
+ | 'pf'
+ | 'pf.conf'
+ | 'php'
+ | 'parser3'
+ | 'perl'
+ | 'pl'
+ | 'pm'
+ | 'plaintext'
+ | 'txt'
+ | 'text'
+ | 'pony'
+ | 'pgsql'
+ | 'postgres'
+ | 'postgresql'
+ | 'powershell'
+ | 'ps'
+ | 'ps1'
+ | 'processing'
+ | 'prolog'
+ | 'properties'
+ | 'protobuf'
+ | 'puppet'
+ | 'pp'
+ | 'python'
+ | 'py'
+ | 'gyp'
+ | 'profile'
+ | 'python-repl'
+ | 'pycon'
+ | 'k'
+ | 'kdb'
+ | 'qml'
+ | 'r'
+ | 'reasonml'
+ | 're'
+ | 'rib'
+ | 'rsl'
+ | 'graph'
+ | 'instances'
+ | 'ruby'
+ | 'rb'
+ | 'gemspec'
+ | 'podspec'
+ | 'thor'
+ | 'irb'
+ | 'rust'
+ | 'rs'
+ | 'sas'
+ | 'scss'
+ | 'sql'
+ | 'p21'
+ | 'step'
+ | 'stp'
+ | 'scala'
+ | 'scheme'
+ | 'scilab'
+ | 'sci'
+ | 'shell'
+ | 'console'
+ | 'smali'
+ | 'smalltalk'
+ | 'st'
+ | 'sml'
+ | 'ml'
+ | 'stan'
+ | 'stanfuncs'
+ | 'stata'
+ | 'stylus'
+ | 'styl'
+ | 'subunit'
+ | 'swift'
+ | 'tcl'
+ | 'tk'
+ | 'tap'
+ | 'thrift'
+ | 'tp'
+ | 'twig'
+ | 'craftcms'
+ | 'typescript'
+ | 'ts'
+ | 'vbnet'
+ | 'vb'
+ | 'vbscript'
+ | 'vbs'
+ | 'vhdl'
+ | 'vala'
+ | 'verilog'
+ | 'v'
+ | 'vim'
+ | 'axapta'
+ | 'x++'
+ | 'x86asm'
+ | 'xl'
+ | 'tao'
+ | 'xquery'
+ | 'xpath'
+ | 'xq'
+ | 'yml'
+ | 'yaml'
+ | 'zephir'
+ | 'zep';
diff --git a/src/lib/common/util/Arg.ts b/src/lib/common/util/Arg.ts
new file mode 100644
index 0000000..84d5aeb
--- /dev/null
+++ b/src/lib/common/util/Arg.ts
@@ -0,0 +1,120 @@
+import { Argument, ArgumentTypeCaster, Flag, ParsedValuePredicate, TypeResolver } from 'discord-akairo';
+import { Message } from 'discord.js';
+import { BushArgumentType } from '../..';
+
+export class Arg {
+ /**
+ * Casts a phrase to this argument's type.
+ * @param type - The type to cast to.
+ * @param resolver - The type resolver.
+ * @param message - Message that called the command.
+ * @param phrase - Phrase to process.
+ */
+ public static cast(type: BushArgumentType, resolver: TypeResolver, message: Message, phrase: string): Promise<any> {
+ return Argument.cast(type, resolver, message, phrase);
+ }
+
+ /**
+ * Creates a type that is the left-to-right composition of the given types.
+ * If any of the types fails, the entire composition fails.
+ * @param types - Types to use.
+ */
+ public static compose(...types: BushArgumentType[]): ArgumentTypeCaster {
+ return Argument.compose(...types);
+ }
+
+ /**
+ * Creates a type that is the left-to-right composition of the given types.
+ * If any of the types fails, the composition still continues with the failure passed on.
+ * @param types - Types to use.
+ */
+ public static composeWithFailure(...types: BushArgumentType[]): ArgumentTypeCaster {
+ return Argument.composeWithFailure(...types);
+ }
+
+ /**
+ * Checks if something is null, undefined, or a fail flag.
+ * @param value - Value to check.
+ */
+ public static isFailure(value: any): value is null | undefined | (Flag & { value: any }) {
+ return Argument.isFailure(value);
+ }
+
+ /**
+ * Creates a type from multiple types (product type).
+ * Only inputs where each type resolves with a non-void value are valid.
+ * @param types - Types to use.
+ */
+ public static product(...types: BushArgumentType[]): ArgumentTypeCaster {
+ return Argument.product(...types);
+ }
+
+ /**
+ * Creates a type where the parsed value must be within a range.
+ * @param type - The type to use.
+ * @param min - Minimum value.
+ * @param max - Maximum value.
+ * @param inclusive - Whether or not to be inclusive on the upper bound.
+ */
+ public static range(type: BushArgumentType, min: number, max: number, inclusive?: boolean): ArgumentTypeCaster {
+ return Argument.range(type, min, max, inclusive);
+ }
+
+ /**
+ * Creates a type that parses as normal but also tags it with some data.
+ * Result is in an object `{ tag, value }` and wrapped in `Flag.fail` when failed.
+ * @param type - The type to use.
+ * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string.
+ */
+ public static tagged(type: BushArgumentType, tag?: any): ArgumentTypeCaster {
+ return Argument.tagged(type, tag);
+ }
+
+ /**
+ * Creates a type from multiple types (union type).
+ * The first type that resolves to a non-void value is used.
+ * Each type will also be tagged using `tagged` with themselves.
+ * @param types - Types to use.
+ */
+ public static taggedUnion(...types: BushArgumentType[]): ArgumentTypeCaster {
+ return Argument.taggedUnion(...types);
+ }
+
+ /**
+ * Creates a type that parses as normal but also tags it with some data and carries the original input.
+ * Result is in an object `{ tag, input, value }` and wrapped in `Flag.fail` when failed.
+ * @param type - The type to use.
+ * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string.
+ */
+ public static taggedWithInput(type: BushArgumentType, tag?: any): ArgumentTypeCaster {
+ return Argument.taggedWithInput(type, tag);
+ }
+
+ /**
+ * Creates a type from multiple types (union type).
+ * The first type that resolves to a non-void value is used.
+ * @param types - Types to use.
+ */
+ public static union(...types: BushArgumentType[]): ArgumentTypeCaster {
+ return Argument.union(...types);
+ }
+
+ /**
+ * Creates a type with extra validation.
+ * If the predicate is not true, the value is considered invalid.
+ * @param type - The type to use.
+ * @param predicate - The predicate function.
+ */
+ public static validate(type: BushArgumentType, predicate: ParsedValuePredicate): ArgumentTypeCaster {
+ return Argument.validate(type, predicate);
+ }
+
+ /**
+ * Creates a type that parses as normal but also carries the original input.
+ * Result is in an object `{ input, value }` and wrapped in `Flag.fail` when failed.
+ * @param type - The type to use.
+ */
+ public static withInput(type: BushArgumentType): ArgumentTypeCaster {
+ return Argument.withInput(type);
+ }
+}
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index 448eaf3..499d2c7 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -1,5 +1,4 @@
import {
- BushArgumentType,
BushCache,
BushClient,
BushConstants,
@@ -11,30 +10,16 @@ import {
PronounCode
} from '@lib';
import { exec } from 'child_process';
-import {
- Argument,
- ArgumentTypeCaster,
- ClientUtil,
- Flag,
- ParsedValuePredicate,
- TypeResolver,
- Util as AkairoUtil
-} from 'discord-akairo';
+import { ClientUtil, Util as AkairoUtil } from 'discord-akairo';
import { APIMessage } from 'discord-api-types';
import {
ColorResolvable,
CommandInteraction,
- Constants,
GuildMember,
InteractionReplyOptions,
Message,
- MessageActionRow,
- MessageButton,
- MessageComponentInteraction,
- MessageEditOptions,
MessageEmbed,
- MessageEmbedOptions,
- MessageOptions,
+ PermissionResolvable,
Snowflake,
TextChannel,
ThreadMember,
@@ -46,443 +31,16 @@ import got from 'got';
import humanizeDuration from 'humanize-duration';
import _ from 'lodash';
import moment from 'moment';
-import { inspect, InspectOptions, promisify } from 'util';
+import { inspect, promisify } from 'util';
import CommandErrorListener from '../../../listeners/commands/commandError';
+import { Format } from '../../common/Format';
+import { BushInspectOptions } from '../../common/typings/BushInspectOptions';
+import { CodeBlockLang } from '../../common/typings/CodeBlockLang';
+import { Arg } from '../../common/util/Arg';
import { BushNewsChannel } from '../discord.js/BushNewsChannel';
import { BushTextChannel } from '../discord.js/BushTextChannel';
import { BushSlashEditMessageType, BushSlashSendMessageType, BushUserResolvable } from './BushClient';
-interface hastebinRes {
- key: string;
-}
-
-export interface uuidRes {
- uuid: string;
- username: string;
- username_history?: { username: string }[] | null;
- textures: {
- custom: boolean;
- slim: boolean;
- skin: {
- url: string;
- data: string;
- };
- raw: {
- value: string;
- signature: string;
- };
- };
- created_at: string;
-}
-
-interface MojangProfile {
- username: string;
- uuid: string;
-}
-
-// #region codeblock type
-export type CodeBlockLang =
- | '1c'
- | 'abnf'
- | 'accesslog'
- | 'actionscript'
- | 'ada'
- | 'arduino'
- | 'ino'
- | 'armasm'
- | 'arm'
- | 'avrasm'
- | 'actionscript'
- | 'as'
- | 'angelscript'
- | 'asc'
- | 'apache'
- | 'apacheconf'
- | 'applescript'
- | 'osascript'
- | 'arcade'
- | 'asciidoc'
- | 'adoc'
- | 'aspectj'
- | 'autohotkey'
- | 'autoit'
- | 'awk'
- | 'mawk'
- | 'nawk'
- | 'gawk'
- | 'bash'
- | 'sh'
- | 'zsh'
- | 'basic'
- | 'bnf'
- | 'brainfuck'
- | 'bf'
- | 'csharp'
- | 'cs'
- | 'c'
- | 'h'
- | 'cpp'
- | 'hpp'
- | 'cc'
- | 'hh'
- | 'c++'
- | 'h++'
- | 'cxx'
- | 'hxx'
- | 'cal'
- | 'cos'
- | 'cls'
- | 'cmake'
- | 'cmake.in'
- | 'coq'
- | 'csp'
- | 'css'
- | 'capnproto'
- | 'capnp'
- | 'clojure'
- | 'clj'
- | 'coffeescript'
- | 'coffee'
- | 'cson'
- | 'iced'
- | 'crmsh'
- | 'crm'
- | 'pcmk'
- | 'crystal'
- | 'cr'
- | 'd'
- | 'dns'
- | 'zone'
- | 'bind'
- | 'dos'
- | 'bat'
- | 'cmd'
- | 'dart'
- | 'dpr'
- | 'dfm'
- | 'pas'
- | 'pascal'
- | 'diff'
- | 'patch'
- | 'django'
- | 'jinja'
- | 'dockerfile'
- | 'docker'
- | 'dsconfig'
- | 'dts'
- | 'dust'
- | 'dst'
- | 'ebnf'
- | 'elixir'
- | 'elm'
- | 'erlang'
- | 'erl'
- | 'excel'
- | 'xls'
- | 'xlsx'
- | 'fsharp'
- | 'fs'
- | 'fix'
- | 'fortran'
- | 'f90'
- | 'f95'
- | 'gcode'
- | 'nc'
- | 'gams'
- | 'gms'
- | 'gauss'
- | 'gss'
- | 'gherkin'
- | 'go'
- | 'golang'
- | 'golo'
- | 'gololang'
- | 'gradle'
- | 'groovy'
- | 'xml'
- | 'html'
- | 'xhtml'
- | 'rss'
- | 'atom'
- | 'xjb'
- | 'xsd'
- | 'xsl'
- | 'plist'
- | 'svg'
- | 'http'
- | 'https'
- | 'haml'
- | 'handlebars'
- | 'hbs'
- | 'html.hbs'
- | 'html.handlebars'
- | 'haskell'
- | 'hs'
- | 'haxe'
- | 'hx'
- | 'hlsl'
- | 'hy'
- | 'hylang'
- | 'ini'
- | 'toml'
- | 'inform7'
- | 'i7'
- | 'irpf90'
- | 'json'
- | 'java'
- | 'jsp'
- | 'javascript'
- | 'js'
- | 'jsx'
- | 'julia'
- | 'julia-repl'
- | 'kotlin'
- | 'kt'
- | 'tex'
- | 'leaf'
- | 'lasso'
- | 'ls'
- | 'lassoscript'
- | 'less'
- | 'ldif'
- | 'lisp'
- | 'livecodeserver'
- | 'livescript'
- | 'ls'
- | 'lua'
- | 'makefile'
- | 'mk'
- | 'mak'
- | 'make'
- | 'markdown'
- | 'md'
- | 'mkdown'
- | 'mkd'
- | 'mathematica'
- | 'mma'
- | 'wl'
- | 'matlab'
- | 'maxima'
- | 'mel'
- | 'mercury'
- | 'mizar'
- | 'mojolicious'
- | 'monkey'
- | 'moonscript'
- | 'moon'
- | 'n1ql'
- | 'nsis'
- | 'nginx'
- | 'nginxconf'
- | 'nim'
- | 'nimrod'
- | 'nix'
- | 'ocaml'
- | 'ml'
- | 'objectivec'
- | 'mm'
- | 'objc'
- | 'obj-c'
- | 'obj-c++'
- | 'objective-c++'
- | 'glsl'
- | 'openscad'
- | 'scad'
- | 'ruleslanguage'
- | 'oxygene'
- | 'pf'
- | 'pf.conf'
- | 'php'
- | 'parser3'
- | 'perl'
- | 'pl'
- | 'pm'
- | 'plaintext'
- | 'txt'
- | 'text'
- | 'pony'
- | 'pgsql'
- | 'postgres'
- | 'postgresql'
- | 'powershell'
- | 'ps'
- | 'ps1'
- | 'processing'
- | 'prolog'
- | 'properties'
- | 'protobuf'
- | 'puppet'
- | 'pp'
- | 'python'
- | 'py'
- | 'gyp'
- | 'profile'
- | 'python-repl'
- | 'pycon'
- | 'k'
- | 'kdb'
- | 'qml'
- | 'r'
- | 'reasonml'
- | 're'
- | 'rib'
- | 'rsl'
- | 'graph'
- | 'instances'
- | 'ruby'
- | 'rb'
- | 'gemspec'
- | 'podspec'
- | 'thor'
- | 'irb'
- | 'rust'
- | 'rs'
- | 'sas'
- | 'scss'
- | 'sql'
- | 'p21'
- | 'step'
- | 'stp'
- | 'scala'
- | 'scheme'
- | 'scilab'
- | 'sci'
- | 'shell'
- | 'console'
- | 'smali'
- | 'smalltalk'
- | 'st'
- | 'sml'
- | 'ml'
- | 'stan'
- | 'stanfuncs'
- | 'stata'
- | 'stylus'
- | 'styl'
- | 'subunit'
- | 'swift'
- | 'tcl'
- | 'tk'
- | 'tap'
- | 'thrift'
- | 'tp'
- | 'twig'
- | 'craftcms'
- | 'typescript'
- | 'ts'
- | 'vbnet'
- | 'vb'
- | 'vbscript'
- | 'vbs'
- | 'vhdl'
- | 'vala'
- | 'verilog'
- | 'v'
- | 'vim'
- | 'axapta'
- | 'x++'
- | 'x86asm'
- | 'xl'
- | 'tao'
- | 'xquery'
- | 'xpath'
- | 'xq'
- | 'yml'
- | 'yaml'
- | 'zephir'
- | 'zep';
-//#endregion
-
-/**
- * {@link https://nodejs.org/api/util.html#util_util_inspect_object_options}
- */
-export interface BushInspectOptions extends InspectOptions {
- /**
- * If `true`, object's non-enumerable symbols and properties are included in the
- * formatted result. [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) and [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) entries are also included as well as
- * user defined prototype properties (excluding method properties).
- *
- * **Default**: `false`.
- */
- showHidden?: boolean | undefined;
- /**
- * Specifies the number of times to recurse while formatting `object`. This is useful
- * for inspecting large objects. To recurse up to the maximum call stack size pass
- * `Infinity` or `null`.
- *
- * **Default**: `2`.
- */
- depth?: number | null | undefined;
- /**
- * If `true`, the output is styled with ANSI color codes. Colors are customizable. See [Customizing util.inspect colors](https://nodejs.org/api/util.html#util_customizing_util_inspect_colors).
- *
- * **Default**: `false`.
- */
- colors?: boolean | undefined;
- /**
- * If `false`, `[util.inspect.custom](depth, opts)` functions are not invoked.
- *
- * **Default**: `true`.
- */
- customInspect?: boolean | undefined;
- /**
- * If `true`, `Proxy` inspection includes the [`target` and `handler`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology) objects.
- *
- * **Default**: `false`.
- */
- showProxy?: boolean | undefined;
- /**
- * Specifies the maximum number of `Array`, [`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) and
- * [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) elements to include when formatting. Set to `null` or `Infinity` to
- * show all elements. Set to `0` or negative to show no elements.
- *
- * **Default**: `100`.
- */
- maxArrayLength?: number | null | undefined;
- /**
- * Specifies the maximum number of characters to include when formatting. Set to
- * `null` or `Infinity` to show all elements. Set to `0` or negative to show no
- * characters.
- *
- * **Default**: `10000`.
- */
- maxStringLength?: number | null | undefined;
- /**
- * The length at which input values are split across multiple lines. Set to
- * `Infinity` to format the input as a single line (in combination with compact set
- * to `true` or any number >= `1`).
- *
- * **Default**: `80`.
- */
- breakLength?: number | undefined;
- /**
- * Setting this to `false` causes each object key to be displayed on a new line. It
- * will break on new lines in text that is longer than `breakLength`. If set to a
- * number, the most `n` inner elements are united on a single line as long as all
- * properties fit into `breakLength`. Short array elements are also grouped together.
- *
- * **Default**: `3`
- */
- compact?: boolean | number | undefined;
- /**
- * If set to `true` or a function, all properties of an object, and `Set` and `Map`
- * entries are sorted in the resulting string. If set to `true` the [default sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) is used.
- * If set to a function, it is used as a [compare function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#parameters).
- *
- * **Default**: `false`.
- */
- sorted?: boolean | ((a: string, b: string) => number) | undefined;
- /**
- * If set to `true`, getters are inspected. If set to `'get'`, only getters without a
- * corresponding setter are inspected. If set to `'set'`, only getters with a
- * corresponding setter are inspected. This might cause side effects depending on
- * the getter function.
- *
- * **Default**: `false`.
- */
- getters?: 'get' | 'set' | boolean | undefined;
-}
-
export class BushClientUtil extends ClientUtil {
/**
* The client.
@@ -504,22 +62,6 @@ export class BushClientUtil extends ClientUtil {
];
/**
- * Emojis used for {@link BushClientUtil.buttonPaginate}
- */
- #paginateEmojis = {
- beginning: '853667381335162910',
- back: '853667410203770881',
- stop: '853667471110570034',
- forward: '853667492680564747',
- end: '853667514915225640'
- };
-
- /**
- * A simple promise exec method
- */
- #exec = promisify(exec);
-
- /**
* Creates this client util
* @param client The client to initialize with
*/
@@ -554,7 +96,7 @@ export class BushClientUtil extends ClientUtil {
stdout: string;
stderr: string;
}> {
- return await this.#exec(command);
+ return await promisify(exec)(command);
}
/**
@@ -677,193 +219,13 @@ export class BushClientUtil extends ClientUtil {
}
/**
- * Paginates an array of embeds using buttons.
- */
- public async buttonPaginate(
- message: BushMessage | BushSlashMessage,
- embeds: MessageEmbed[] | MessageEmbedOptions[],
- text: string | null = null,
- deleteOnExit?: boolean,
- startOn?: number
- ): Promise<void> {
- const paginateEmojis = this.#paginateEmojis;
- if (deleteOnExit === undefined) deleteOnExit = true;
-
- if (embeds.length === 1) {
- return this.sendWithDeleteButton(message, { embeds: embeds });
- }
-
- embeds.forEach((_e, i) => {
- embeds[i] instanceof MessageEmbed
- ? (embeds[i] as MessageEmbed).setFooter(`Page ${(i + 1).toLocaleString()}/${embeds.length.toLocaleString()}`)
- : ((embeds[i] as MessageEmbedOptions).footer = {
- text: `Page ${(i + 1).toLocaleString()}/${embeds.length.toLocaleString()}`
- });
- });
-
- const style = Constants.MessageButtonStyles.PRIMARY;
- let curPage = startOn ? startOn - 1 : 0;
- if (typeof embeds !== 'object') throw new Error('embeds must be an object');
- const msg = (await message.util.reply({
- // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
- content: text || null,
- embeds: [embeds[curPage]],
- components: [getPaginationRow()]
- })) as Message;
- const filter = (interaction: MessageComponentInteraction) =>
- interaction.customId.startsWith('paginate_') && interaction.message.id === msg.id;
- const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
- collector.on('collect', async (interaction: MessageComponentInteraction) => {
- if (interaction.user.id === message.author.id || client.config.owners.includes(interaction.user.id)) {
- switch (interaction.customId) {
- case 'paginate_beginning': {
- curPage = 0;
- await edit(interaction);
- break;
- }
- case 'paginate_back': {
- curPage--;
- await edit(interaction);
- break;
- }
- case 'paginate_stop': {
- if (deleteOnExit) {
- await interaction.deferUpdate().catch(() => undefined);
- if (msg.deletable && !msg.deleted) {
- await msg.delete();
- }
- } else {
- await interaction
- ?.update({
- content: `${text ? `${text}\n` : ''}Command closed by user.`,
- embeds: [],
- components: []
- })
- .catch(() => undefined);
- }
- return;
- }
- case 'paginate_next': {
- curPage++;
- await edit(interaction);
- break;
- }
- case 'paginate_end': {
- curPage = embeds.length - 1;
- await edit(interaction);
- break;
- }
- }
- } else {
- return await interaction?.deferUpdate().catch(() => undefined);
- }
- });
-
- collector.on('end', async () => {
- await msg
- .edit({
- content: text,
- embeds: [embeds[curPage]],
- components: [getPaginationRow(true)]
- })
- .catch(() => undefined);
- });
-
- async function edit(interaction: MessageComponentInteraction): Promise<void> {
- return await interaction
- ?.update({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow()] })
- .catch(() => undefined);
- }
- function getPaginationRow(disableAll = false): MessageActionRow {
- return new MessageActionRow().addComponents(
- new MessageButton({
- style,
- customId: 'paginate_beginning',
- emoji: paginateEmojis.beginning,
- disabled: disableAll || curPage === 0
- }),
- new MessageButton({
- style,
- customId: 'paginate_back',
- emoji: paginateEmojis.back,
- disabled: disableAll || curPage === 0
- }),
- new MessageButton({
- style,
- customId: 'paginate_stop',
- emoji: paginateEmojis.stop,
- disabled: disableAll
- }),
- new MessageButton({
- style,
- customId: 'paginate_next',
- emoji: paginateEmojis.forward,
- disabled: disableAll || curPage === embeds.length - 1
- }),
- new MessageButton({
- style,
- customId: 'paginate_end',
- emoji: paginateEmojis.end,
- disabled: disableAll || curPage === embeds.length - 1
- })
- );
- }
- }
-
- /**
- * Sends a message with a button for the user to delete it.
- */
- public async sendWithDeleteButton(message: BushMessage | BushSlashMessage, options: MessageOptions): Promise<void> {
- const paginateEmojis = this.#paginateEmojis;
- updateOptions();
- const msg = (await message.util.reply(options as MessageOptions & { split?: false })) as Message;
- const filter = (interaction: MessageComponentInteraction) =>
- interaction.customId == 'paginate__stop' && interaction.message == msg;
- const collector = msg.createMessageComponentCollector({ filter, time: 300000 });
- collector.on('collect', async (interaction: MessageComponentInteraction) => {
- if (interaction.user.id == message.author.id || client.config.owners.includes(interaction.user.id)) {
- await interaction.deferUpdate().catch(() => undefined);
- if (msg.deletable && !msg.deleted) {
- await msg.delete();
- }
- return;
- } else {
- return await interaction?.deferUpdate().catch(() => undefined);
- }
- });
-
- collector.on('end', async () => {
- updateOptions(true, true);
- await msg.edit(options as MessageEditOptions).catch(() => undefined);
- });
-
- function updateOptions(edit?: boolean, disable?: boolean) {
- if (edit == undefined) edit = false;
- if (disable == undefined) disable = false;
- options.components = [
- new MessageActionRow().addComponents(
- new MessageButton({
- style: Constants.MessageButtonStyles.PRIMARY,
- customId: 'paginate__stop',
- emoji: paginateEmojis.stop,
- disabled: disable
- })
- )
- ];
- if (edit) {
- options.reply = undefined;
- }
- }
- }
-
- /**
* Surrounds text in a code block with the specified language and puts it in a hastebin if its too long.
* * Embed Description Limit = 4096 characters
* * Embed Field Limit = 1024 characters
*/
public async codeblock(code: string, length: number, language?: CodeBlockLang, substr = false): Promise<string> {
let hasteOut = '';
- code = util.discord.escapeCodeBlock(code);
+ code = this.discord.escapeCodeBlock(code);
const prefix = `\`\`\`${language}\n`;
const suffix = '\n```';
language = language ?? 'txt';
@@ -887,7 +249,12 @@ export class BushClientUtil extends ClientUtil {
return code3;
}
- public inspect(code: any, options?: BushInspectOptions): string {
+ /**
+ * Uses {@link inspect} with custom defaults
+ * @param object - The object you would like to inspect
+ * @param options - The options you would like to use to inspect the object
+ */
+ public inspect(object: any, options?: BushInspectOptions): string {
const {
showHidden: _showHidden = false,
depth: _depth = 2,
@@ -914,7 +281,7 @@ export class BushClientUtil extends ClientUtil {
sorted: _sorted,
getters: _getters
};
- return inspect(code, optionsWithDefaults);
+ return inspect(object, optionsWithDefaults);
}
#mapCredential(old: string): string {
@@ -931,6 +298,7 @@ export class BushClientUtil extends ClientUtil {
/**
* Redacts credentials from a string
+ * @param text - The text to redact credentials from
*/
public redact(text: string) {
for (const credentialName in { ...client.config.credentials, dbPassword: client.config.db.password }) {
@@ -1028,7 +396,7 @@ export class BushClientUtil extends ClientUtil {
const newValue = this.addOrRemoveFromArray(action, oldValue, value);
row[key] = newValue;
client.cache.global[key] = newValue;
- return await row.save().catch((e) => util.handleError('insertOrRemoveFromGlobal', e));
+ return await row.save().catch((e) => this.handleError('insertOrRemoveFromGlobal', e));
}
/**
@@ -1233,136 +601,108 @@ export class BushClientUtil extends ClientUtil {
return str.replace(/[\u0000-\u001F\u007F-\u009F\u200B]/g, '');
}
- get arg() {
- return class Arg {
- /**
- * Casts a phrase to this argument's type.
- * @param type - The type to cast to.
- * @param resolver - The type resolver.
- * @param message - Message that called the command.
- * @param phrase - Phrase to process.
- */
- public static cast(type: BushArgumentType, resolver: TypeResolver, message: Message, phrase: string): Promise<any> {
- return Argument.cast(type, resolver, message, phrase);
- }
+ public async uploadImageToImgur(image: string) {
+ const clientId = this.client.config.credentials.imgurClientId;
- /**
- * Creates a type that is the left-to-right composition of the given types.
- * If any of the types fails, the entire composition fails.
- * @param types - Types to use.
- */
- public static compose(...types: BushArgumentType[]): ArgumentTypeCaster {
- return Argument.compose(...types);
- }
+ const resp = (await got
+ .post('https://api.imgur.com/3/upload', {
+ headers: {
+ // Authorization: `Bearer ${token}`,
+ Authorization: `Client-ID ${clientId}`,
+ Accept: 'application/json'
+ },
+ form: {
+ image: image,
+ type: 'base64'
+ },
+ followRedirect: true
+ })
+ .json()) as { data: { link: string } };
- /**
- * Creates a type that is the left-to-right composition of the given types.
- * If any of the types fails, the composition still continues with the failure passed on.
- * @param types - Types to use.
- */
- public static composeWithFailure(...types: BushArgumentType[]): ArgumentTypeCaster {
- return Argument.composeWithFailure(...types);
- }
+ return resp.data.link;
+ }
- /**
- * Checks if something is null, undefined, or a fail flag.
- * @param value - Value to check.
- */
- public static isFailure(value: any): value is null | undefined | (Flag & { value: any }) {
- return Argument.isFailure(value);
- }
+ public userGuildPermCheck(message: BushMessage | BushSlashMessage, permissions: PermissionResolvable) {
+ const missing = message.member?.permissions.missing(permissions) ?? [];
- /**
- * Creates a type from multiple types (product type).
- * Only inputs where each type resolves with a non-void value are valid.
- * @param types - Types to use.
- */
- public static product(...types: BushArgumentType[]): ArgumentTypeCaster {
- return Argument.product(...types);
- }
+ return missing.length ? missing : null;
+ }
- /**
- * Creates a type where the parsed value must be within a range.
- * @param type - The type to use.
- * @param min - Minimum value.
- * @param max - Maximum value.
- * @param inclusive - Whether or not to be inclusive on the upper bound.
- */
- public static range(type: BushArgumentType, min: number, max: number, inclusive?: boolean): ArgumentTypeCaster {
- return Argument.range(type, min, max, inclusive);
- }
+ public clientGuildPermCheck(message: BushMessage | BushSlashMessage, permissions: PermissionResolvable) {
+ const missing = message.guild?.me?.permissions.missing(permissions) ?? [];
- /**
- * Creates a type that parses as normal but also tags it with some data.
- * Result is in an object `{ tag, value }` and wrapped in `Flag.fail` when failed.
- * @param type - The type to use.
- * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string.
- */
- public static tagged(type: BushArgumentType, tag?: any): ArgumentTypeCaster {
- return Argument.tagged(type, tag);
- }
+ return missing.length ? missing : null;
+ }
- /**
- * Creates a type from multiple types (union type).
- * The first type that resolves to a non-void value is used.
- * Each type will also be tagged using `tagged` with themselves.
- * @param types - Types to use.
- */
- public static taggedUnion(...types: BushArgumentType[]): ArgumentTypeCaster {
- return Argument.taggedUnion(...types);
- }
+ public clientSendAndPermCheck(
+ message: BushMessage | BushSlashMessage,
+ permissions: PermissionResolvable = [],
+ checkChannel = false
+ ) {
+ const missing = [];
+ const sendPerm = message.channel!.isThread() ? 'SEND_MESSAGES' : 'SEND_MESSAGES_IN_THREADS';
- /**
- * Creates a type that parses as normal but also tags it with some data and carries the original input.
- * Result is in an object `{ tag, input, value }` and wrapped in `Flag.fail` when failed.
- * @param type - The type to use.
- * @param tag - Tag to add. Defaults to the `type` argument, so useful if it is a string.
- */
- public static taggedWithInput(type: BushArgumentType, tag?: any): ArgumentTypeCaster {
- return Argument.taggedWithInput(type, tag);
- }
+ if (!message.guild!.me!.permissionsIn(message.channel!.id!).has(sendPerm)) missing.push(sendPerm);
- /**
- * Creates a type from multiple types (union type).
- * The first type that resolves to a non-void value is used.
- * @param types - Types to use.
- */
- public static union(...types: BushArgumentType[]): ArgumentTypeCaster {
- return Argument.union(...types);
- }
+ missing.push(
+ ...(checkChannel
+ ? message.guild!.me!.permissionsIn(message.channel!.id!).missing(permissions)
+ : this.clientGuildPermCheck(message, permissions) ?? [])
+ );
- /**
- * Creates a type with extra validation.
- * If the predicate is not true, the value is considered invalid.
- * @param type - The type to use.
- * @param predicate - The predicate function.
- */
- public static validate(type: BushArgumentType, predicate: ParsedValuePredicate): ArgumentTypeCaster {
- return Argument.validate(type, predicate);
- }
+ return missing.length ? missing : null;
+ }
- /**
- * Creates a type that parses as normal but also carries the original input.
- * Result is in an object `{ input, value }` and wrapped in `Flag.fail` when failed.
- * @param type - The type to use.
- */
- public static withInput(type: BushArgumentType): ArgumentTypeCaster {
- return Argument.withInput(type);
- }
- };
+ public get arg() {
+ return Arg;
+ }
+
+ /**
+ * Formats and escapes content for formatting
+ */
+ public get format() {
+ return Format;
}
/**
* Discord.js's Util class
*/
- get discord() {
+ public get discord() {
return DiscordUtil;
}
/**
* discord-akairo's Util class
*/
- get akairo() {
+ public get akairo() {
return AkairoUtil;
}
}
+
+interface hastebinRes {
+ key: string;
+}
+
+export interface uuidRes {
+ uuid: string;
+ username: string;
+ username_history?: { username: string }[] | null;
+ textures: {
+ custom: boolean;
+ slim: boolean;
+ skin: {
+ url: string;
+ data: string;
+ };
+ raw: {
+ value: string;
+ signature: string;
+ };
+ };
+ created_at: string;
+}
+
+interface MojangProfile {
+ username: string;
+ uuid: string;
+}
diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts
index 0be128b..22d4aae 100644
--- a/src/lib/extensions/discord-akairo/BushCommand.ts
+++ b/src/lib/extensions/discord-akairo/BushCommand.ts
@@ -158,9 +158,9 @@ export interface BushCommandOptions extends Omit<CommandOptions, 'userPermission
/** Allow this command to be run in channels that are blacklisted. */
bypassChannelBlacklist?: boolean;
/** Permissions required by the client to run this command. */
- clientPermissions?: PermissionResolvable | PermissionResolvable[] | BushMissingPermissionSupplier;
+ clientPermissions: PermissionResolvable | PermissionResolvable[] | BushMissingPermissionSupplier;
/** Permissions required by the user to run this command. */
- userPermissions?: PermissionResolvable | PermissionResolvable[] | BushMissingPermissionSupplier;
+ userPermissions: PermissionResolvable | PermissionResolvable[] | BushMissingPermissionSupplier;
}
export class BushCommand extends Command {
diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts
index 8d44cd4..51c2795 100644
--- a/src/lib/extensions/discord.js/BushGuild.ts
+++ b/src/lib/extensions/discord.js/BushGuild.ts
@@ -1,6 +1,6 @@
import { Guild, MessageOptions, UserResolvable } from 'discord.js';
import { RawGuildData } from 'discord.js/typings/rawDataTypes';
-import { Moderation } from '../../common/moderation';
+import { Moderation } from '../../common/Moderation';
import { Guild as GuildDB, GuildFeatures, GuildLogType, GuildModel } from '../../models/Guild';
import { ModLogType } from '../../models/ModLog';
import { BushClient, BushUserResolvable } from '../discord-akairo/BushClient';
diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts
index 954342d..77d03b1 100644
--- a/src/lib/extensions/discord.js/BushGuildMember.ts
+++ b/src/lib/extensions/discord.js/BushGuildMember.ts
@@ -1,6 +1,6 @@
import { GuildMember, MessageEmbed, Partialize, Role } from 'discord.js';
import { RawGuildMemberData } from 'discord.js/typings/rawDataTypes';
-import { Moderation } from '../../common/moderation';
+import { Moderation } from '../../common/Moderation';
import { ModLogType } from '../../models/ModLog';
import { BushClient } from '../discord-akairo/BushClient';
import { BushGuild } from './BushGuild';
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index d23bb0f..7b51e61 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -1,6 +1,6 @@
import { Snowflake } from 'discord.js';
import { DataTypes, Sequelize } from 'sequelize';
-import { BadWords } from '../common/autoMod';
+import { BadWords } from '../common/AutoMod';
import { BushClient } from '../extensions/discord-akairo/BushClient';
import { BaseModel } from './BaseModel';
import { jsonArrayInit, jsonParseGet, jsonParseSet, NEVER_USED } from './__helpers';
diff --git a/src/lib/utils/Config.ts b/src/lib/utils/Config.ts
index ed73d40..393dd44 100644
--- a/src/lib/utils/Config.ts
+++ b/src/lib/utils/Config.ts
@@ -1,25 +1,14 @@
import { Snowflake } from 'discord.js';
-export interface ConfigOptions {
- credentials: { token: string; betaToken: string; devToken: string; hypixelApiKey: string; wolframAlphaAppId: string };
- environment: 'production' | 'beta' | 'development';
- owners: Snowflake[];
- prefix: string;
- channels: { log: Snowflake; error: Snowflake; dm: Snowflake };
- db: { host: string; port: number; username: string; password: string };
- logging: { db: boolean; verbose: boolean; info: boolean };
- supportGuild: { id: Snowflake; invite: string };
-}
-
export class Config {
- public credentials: { token: string; betaToken: string; devToken: string; hypixelApiKey: string; wolframAlphaAppId: string };
- public environment: 'production' | 'beta' | 'development';
+ public credentials: Credentials;
+ public environment: Environment;
public owners: Snowflake[];
public prefix: string;
- public channels: { log: Snowflake; error: Snowflake; dm: Snowflake };
- public db: { host: string; port: number; username: string; password: string };
- public logging: { db: boolean; verbose: boolean; info: boolean };
- public supportGuild: { id: Snowflake; invite: string };
+ public channels: Channels;
+ public db: DataBase;
+ public logging: Logging;
+ public supportGuild: SupportGuild;
public constructor(options: ConfigOptions) {
this.credentials = options.credentials;
@@ -43,10 +32,59 @@ export class Config {
public get isProduction(): boolean {
return this.environment === 'production';
}
+
public get isBeta(): boolean {
return this.environment === 'beta';
}
+
public get isDevelopment(): boolean {
return this.environment === 'development';
}
}
+
+export interface ConfigOptions {
+ credentials: Credentials;
+ environment: Environment;
+ owners: Snowflake[];
+ prefix: string;
+ channels: Channels;
+ db: DataBase;
+ logging: Logging;
+ supportGuild: SupportGuild;
+}
+
+interface Credentials {
+ token: string;
+ betaToken: string;
+ devToken: string;
+ hypixelApiKey: string;
+ wolframAlphaAppId: string;
+ imgurClientId: string;
+ imgurClientSecret: string;
+}
+
+type Environment = 'production' | 'beta' | 'development';
+
+interface Channels {
+ log: Snowflake;
+ error: Snowflake;
+ dm: Snowflake;
+}
+
+interface DataBase {
+ host: string;
+ port: number;
+ username: string;
+ password: string;
+}
+
+interface Logging {
+ db: boolean;
+ verbose: boolean;
+ info: boolean;
+}
+
+interface SupportGuild {
+ id: Snowflake;
+ invite: string;
+}
diff --git a/src/listeners/client/interactionCreate.ts b/src/listeners/client/interactionCreate.ts
index d2e02ab..e2042d0 100644
--- a/src/listeners/client/interactionCreate.ts
+++ b/src/listeners/client/interactionCreate.ts
@@ -1,5 +1,5 @@
import { BushButtonInteraction, BushListener } from '@lib';
-import { AutoMod } from '../../lib/common/autoMod';
+import { AutoMod } from '../../lib/common/AutoMod';
import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents';
export default class InteractionCreateListener extends BushListener {
diff --git a/src/listeners/message/automodCreate.ts b/src/listeners/message/automodCreate.ts
index b801356..8857aee 100644
--- a/src/listeners/message/automodCreate.ts
+++ b/src/listeners/message/automodCreate.ts
@@ -1,5 +1,5 @@
import { BushListener } from '@lib';
-import { AutoMod } from '../../lib/common/autoMod';
+import { AutoMod } from '../../lib/common/AutoMod';
import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents';
export default class AutomodMessageCreateListener extends BushListener {
diff --git a/src/listeners/message/automodUpdate.ts b/src/listeners/message/automodUpdate.ts
index 08d4911..9464890 100644
--- a/src/listeners/message/automodUpdate.ts
+++ b/src/listeners/message/automodUpdate.ts
@@ -1,5 +1,5 @@
import { BushListener, BushMessage } from '@lib';
-import { AutoMod } from '../../lib/common/autoMod';
+import { AutoMod } from '../../lib/common/AutoMod';
import { BushClientEvents } from '../../lib/extensions/discord.js/BushClientEvents';
export default class AutomodMessageUpdateListener extends BushListener {