aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-17 10:25:46 -0400
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-17 10:25:46 -0400
commitd1724227abfb8f0fcd9e573f7e9772cf0be8257a (patch)
tree52c9dbae1fbbbd3c777d9c16ab643c477141ae21
parent53d2b18f7f73d5696fb7cd86d1c164a790dfdcc3 (diff)
downloadtanzanite-d1724227abfb8f0fcd9e573f7e9772cf0be8257a.tar.gz
tanzanite-d1724227abfb8f0fcd9e573f7e9772cf0be8257a.tar.bz2
tanzanite-d1724227abfb8f0fcd9e573f7e9772cf0be8257a.zip
honestly no idea what I did at this point
-rwxr-xr-x.pnp.cjs33
-rw-r--r--.vscode/extensions.json6
-rw-r--r--package.json6
-rw-r--r--src/commands/_fake-command/test.ts18
-rw-r--r--src/commands/admin/channelPermissions.ts97
-rw-r--r--src/commands/config/autoPublishChannel.ts21
-rw-r--r--src/commands/config/blacklist.ts132
-rw-r--r--src/commands/config/disable.ts128
-rw-r--r--src/commands/config/prefix.ts20
-rw-r--r--src/commands/dev/__template.ts61
-rw-r--r--src/commands/dev/eval.ts10
-rw-r--r--src/commands/dev/servers.ts48
-rw-r--r--src/commands/dev/setLevel.ts6
-rw-r--r--src/commands/dev/sh.ts84
-rw-r--r--src/commands/info/color.ts47
-rw-r--r--src/commands/info/help.ts42
-rw-r--r--src/commands/info/userInfo.ts2
-rw-r--r--src/commands/moderation/ban.ts15
-rw-r--r--src/commands/moderation/kick.ts13
-rw-r--r--src/commands/moderation/lockdown.ts45
-rw-r--r--src/commands/moderation/modlog.ts2
-rw-r--r--src/commands/moderation/mute.ts10
-rw-r--r--src/commands/moderation/warn.ts10
-rw-r--r--src/commands/moulberry-bush/capePerms.ts33
-rw-r--r--src/commands/moulberry-bush/level.ts8
-rw-r--r--src/commands/moulberry-bush/report.ts121
-rw-r--r--src/commands/moulberry-bush/rule.ts25
-rw-r--r--src/commands/skyblock-reborn/chooseColorCommand.ts (renamed from src/commands/skyblockReborn/chooseColorCommand.ts)0
-rw-r--r--src/commands/utilities/_whoHasRole.ts0
-rw-r--r--src/commands/utilities/hash.ts42
-rw-r--r--src/commands/utilities/price.ts220
-rw-r--r--src/commands/utilities/viewraw.ts2
-rw-r--r--src/commands/utilities/whoHasRole.ts53
-rw-r--r--src/config/example-options.ts3
-rw-r--r--src/inhibitors/blacklist/channelGlobalBlacklist.ts25
-rw-r--r--src/inhibitors/blacklist/channelGuildBlacklist.ts25
-rw-r--r--src/inhibitors/blacklist/guildBlacklist.ts8
-rw-r--r--src/inhibitors/blacklist/userBlacklist.ts20
-rw-r--r--src/inhibitors/blacklist/userGlobalBlacklist.ts25
-rw-r--r--src/inhibitors/blacklist/userGuildBlacklist.ts25
-rw-r--r--src/inhibitors/commands/disabledCommand.ts19
-rw-r--r--src/inhibitors/commands/globalDisabledCommand.ts19
-rw-r--r--src/inhibitors/commands/guildDisabledCommand.ts21
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts30
-rw-r--r--src/lib/extensions/discord-akairo/BushCommand.ts5
-rw-r--r--src/lib/extensions/discord.js/BushGuild.ts2
-rw-r--r--src/lib/models/Global.ts15
-rw-r--r--src/lib/models/Guild.ts48
-rw-r--r--src/lib/models/Level.ts19
-rw-r--r--src/lib/utils/BushConstants.ts6
-rw-r--r--src/listeners/client/interactionCreate.ts (renamed from src/listeners/client/interaction.ts)6
-rw-r--r--src/listeners/commands/commandBlocked.ts10
-rw-r--r--src/listeners/commands/commandError.ts8
-rw-r--r--src/listeners/commands/slashBlocked.ts10
-rw-r--r--src/listeners/commands/slashCommandError.ts8
-rw-r--r--src/listeners/message/level.ts24
-rw-r--r--yarn.lock24
57 files changed, 1551 insertions, 214 deletions
diff --git a/.pnp.cjs b/.pnp.cjs
index f02a88a..7a9fd13 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -41,6 +41,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/humanize-duration", "npm:3.25.1"],
["@types/module-alias", "npm:2.0.1"],
["@types/node", "npm:14.17.5"],
+ ["@types/tinycolor2", "npm:1.4.3"],
["@types/uuid", "npm:8.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.2"],
["@typescript-eslint/parser", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.2"],
@@ -54,6 +55,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["esbuild", "npm:0.12.15"],
["eslint", "npm:7.30.0"],
["eslint-config-prettier", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.3.0"],
+ ["fuse.js", "npm:6.4.6"],
["got", "npm:11.8.2"],
["humanize-duration", "npm:3.27.0"],
["madge", "npm:5.0.1"],
@@ -65,6 +67,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["rimraf", "npm:3.0.2"],
["sequelize", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:6.6.5"],
["source-map-support", "npm:0.5.19"],
+ ["tinycolor2", "npm:1.4.2"],
["typescript", "patch:typescript@npm%3A4.2.4#~builtin<compat/typescript>::version=4.2.4&hash=d8b4e7"],
["uuid", "npm:8.3.2"]
],
@@ -445,6 +448,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["@types/tinycolor2", [
+ ["npm:1.4.3", {
+ "packageLocation": "./.yarn/cache/@types-tinycolor2-npm-1.4.3-90e6bf0ed8-61984b2825.zip/node_modules/@types/tinycolor2/",
+ "packageDependencies": [
+ ["@types/tinycolor2", "npm:1.4.3"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["@types/uuid", [
["npm:8.3.1", {
"packageLocation": "./.yarn/cache/@types-uuid-npm-8.3.1-4239b14bac-b41bdc5e86.zip/node_modules/@types/uuid/",
@@ -924,6 +936,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["@types/humanize-duration", "npm:3.25.1"],
["@types/module-alias", "npm:2.0.1"],
["@types/node", "npm:14.17.5"],
+ ["@types/tinycolor2", "npm:1.4.3"],
["@types/uuid", "npm:8.3.1"],
["@typescript-eslint/eslint-plugin", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.2"],
["@typescript-eslint/parser", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:4.28.2"],
@@ -937,6 +950,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["esbuild", "npm:0.12.15"],
["eslint", "npm:7.30.0"],
["eslint-config-prettier", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:8.3.0"],
+ ["fuse.js", "npm:6.4.6"],
["got", "npm:11.8.2"],
["humanize-duration", "npm:3.27.0"],
["madge", "npm:5.0.1"],
@@ -948,6 +962,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["rimraf", "npm:3.0.2"],
["sequelize", "virtual:d7ae587dddcefd495158f5c047acecbca3203324d75e681c7d8657c07f901f74e152f0b39978f7428d3a91daad7b5020c47ece28de69c22fcbd49d04707bf15c#npm:6.6.5"],
["source-map-support", "npm:0.5.19"],
+ ["tinycolor2", "npm:1.4.2"],
["typescript", "patch:typescript@npm%3A4.2.4#~builtin<compat/typescript>::version=4.2.4&hash=d8b4e7"],
["uuid", "npm:8.3.2"]
],
@@ -1940,6 +1955,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["fuse.js", [
+ ["npm:6.4.6", {
+ "packageLocation": "./.yarn/cache/fuse.js-npm-6.4.6-0fa81ef443-012dfacdc9.zip/node_modules/fuse.js/",
+ "packageDependencies": [
+ ["fuse.js", "npm:6.4.6"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["get-amd-module-type", [
["npm:3.0.0", {
"packageLocation": "./.yarn/cache/get-amd-module-type-npm-3.0.0-2fcd610976-a2df61d329.zip/node_modules/get-amd-module-type/",
@@ -3792,6 +3816,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD",
}]
]],
+ ["tinycolor2", [
+ ["npm:1.4.2", {
+ "packageLocation": "./.yarn/cache/tinycolor2-npm-1.4.2-462ba30c26-57ed262e08.zip/node_modules/tinycolor2/",
+ "packageDependencies": [
+ ["tinycolor2", "npm:1.4.2"]
+ ],
+ "linkType": "HARD",
+ }]
+ ]],
["to-fast-properties", [
["npm:2.0.0", {
"packageLocation": "./.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-be2de62fe5.zip/node_modules/to-fast-properties/",
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index d3de569..d56ae08 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -5,6 +5,8 @@
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"esbenp.prettier-vscode",
- "streetsidesoftware.code-spell-checker"
+ "streetsidesoftware.code-spell-checker",
+ "github.vscode-pull-request-github",
+ "ckolkman.vscode-postgres"
]
-}
+} \ No newline at end of file
diff --git a/package.json b/package.json
index ba908f8..2a4b4a1 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,8 @@
"scripts": {
"build-esbuild": "yarn rimraf dist && yarn esbuild --sourcemap=inline --minify-whitespace --minify-syntax --outdir=dist --platform=node --target=es2020 --format=cjs --log-level=warning src/**/*.ts",
"build-tsc": "yarn rimraf dist && yarn tsc",
- "start": "yarn build-esbuild && node --trace-warnings -r source-map-support/register dist/bot.js",
+ "_start": "yarn build-esbuild && node --trace-warnings -r source-map-support/register dist/bot.js",
+ "start": "yarn build-tsc && node --trace-warnings -r source-map-support/register dist/bot.js",
"dev": "yarn build-tsc && node --trace-warnings -r source-map-support/register dist/bot.js",
"test": "yarn lint && yarn tsc --noEmit",
"format": "yarn prettier . --write",
@@ -26,6 +27,7 @@
"@types/humanize-duration": "^3",
"@types/module-alias": "^2",
"@types/node": "^14.14.22",
+ "@types/tinycolor2": "^1",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
@@ -46,6 +48,7 @@
"discord-api-types": "0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e",
"discord.js": "NotEnoughUpdates/discord.js",
"discord.js-minesweeper": "^1.0.6",
+ "fuse.js": "^6.4.6",
"got": "^11.8.2",
"humanize-duration": "^3.27.0",
"madge": "^5.0.1",
@@ -54,6 +57,7 @@
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
"sequelize": "^6.5.0",
+ "tinycolor2": "^1.4.2",
"uuid": "^8.3.2"
},
"eslintConfig": {
diff --git a/src/commands/_fake-command/test.ts b/src/commands/_fake-command/test.ts
new file mode 100644
index 0000000..8eeca9e
--- /dev/null
+++ b/src/commands/_fake-command/test.ts
@@ -0,0 +1,18 @@
+import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+
+export default class TestCommand extends BushCommand {
+ public constructor() {
+ super('test', {
+ category: 'fake-commands',
+ description: { content: '', examples: '', usage: '' },
+ condition: (message: BushMessage) => {
+ if (message.content.toLowerCase().includes('ironmoon')) return true;
+ else return false;
+ },
+ completelyHide: true
+ });
+ }
+ public async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
+ return await message.util.reply('Your message included the word ironmoon.');
+ }
+}
diff --git a/src/commands/admin/channelPermissions.ts b/src/commands/admin/channelPermissions.ts
new file mode 100644
index 0000000..249789d
--- /dev/null
+++ b/src/commands/admin/channelPermissions.ts
@@ -0,0 +1,97 @@
+import { Argument, Constants } from 'discord-akairo';
+import { GuildChannel, GuildMember, MessageEmbed, Role } from 'discord.js';
+import { BushCommand, BushMessage } from '../../lib';
+
+export default class ChannelPermissionsCommand extends BushCommand {
+ public constructor() {
+ super('channelpermissions', {
+ aliases: ['channelperms', 'cperms', 'cperm', 'chanperms', 'chanperm', 'channelpermissions'],
+ category: 'admin',
+ typing: true,
+ description: {
+ content: 'Use to mass change the channel ',
+ usage: 'ChannelPerms <role_id> <perm> <state>',
+ examples: ['ChannelPerms 783794633129197589 read_messages deny']
+ },
+ args: [
+ {
+ id: 'target',
+ type: Argument.union(Constants.ArgumentTypes.ROLE, Constants.ArgumentTypes.MEMBER),
+ match: Constants.ArgumentMatches.PHRASE,
+ prompt: {
+ start: 'What user/role would you like to change?',
+ retry: 'Invalid response. What user/role would you like to change?'
+ }
+ },
+ {
+ id: 'permission',
+ type: 'permission',
+ match: Constants.ArgumentMatches.PHRASE,
+ prompt: {
+ start: 'What permission would you like to change?',
+ retry: '{error} Choose a valid permission.'
+ }
+ },
+ {
+ id: 'state',
+ type: [
+ ['true', '1', 'yes', 'enable', 'allow'],
+ ['false', '0', 'no', 'disable', 'disallow', 'deny'],
+ ['neutral', 'remove', 'none']
+ ],
+ match: Constants.ArgumentMatches.PHRASE,
+ prompt: {
+ start: 'What should that permission be set to?',
+ retry: '{error} Set the state to either `enable`, `disable`, or `remove`.'
+ }
+ }
+ ],
+ ratelimit: 4,
+ cooldown: 4000,
+ clientPermissions: ['MANAGE_CHANNELS', 'SEND_MESSAGES'],
+ userPermissions: ['ADMINISTRATOR'],
+ channel: 'guild'
+ });
+ }
+
+ public async exec(
+ message: BushMessage,
+ {
+ target,
+ permission,
+ state
+ }: {
+ target: Role | GuildMember;
+ permission: string;
+ state: 'true' | 'false' | 'neutral';
+ }
+ ): Promise<void> {
+ const failedChannels = [];
+ for (const channel of message.guild.channels.cache.array()) {
+ try {
+ if (channel.isThread()) return;
+ if (channel.permissionsLocked) return;
+ const permissionState = state === 'true' ? true : state === 'false' ? false : null;
+ await channel.permissionOverwrites.create(
+ target.id,
+ { [permission]: permissionState },
+ { reason: 'Changing overwrites for mass channel channel perms command' }
+ );
+ } catch (e) {
+ this.client.console.debug(e.stack);
+ failedChannels.push(channel);
+ }
+ }
+ const failure = failedChannels.map((e: GuildChannel) => `<#${e.id}>`).join(' ');
+ if (failure.length > 2000) {
+ const paginate: MessageEmbed[] = [];
+ for (let i = 0; i < failure.length; i += 2000) {
+ paginate.push(new MessageEmbed().setDescription(failure.substring(i, Math.min(failure.length, i + 2000))));
+ }
+ const normalMessage = `Finished changing perms! Failed channels:`;
+ this.client.util.buttonPaginate(message, paginate, normalMessage);
+ } else {
+ await message.util.reply({ content: `Finished changing perms! Failed channels:`, embeds: [{ description: failure }] });
+ }
+ }
+}
diff --git a/src/commands/config/autoPublishChannel.ts b/src/commands/config/autoPublishChannel.ts
index 421e030..a2692e2 100644
--- a/src/commands/config/autoPublishChannel.ts
+++ b/src/commands/config/autoPublishChannel.ts
@@ -1,4 +1,4 @@
-import { BushCommand, BushMessage } from '@lib';
+import { AllowedMentions, BushCommand, BushMessage } from '@lib';
import { Channel } from 'discord.js';
export default class AutoPublishChannelCommand extends BushCommand {
@@ -40,14 +40,17 @@ export default class AutoPublishChannelCommand extends BushCommand {
public async exec(message: BushMessage, { channel }: { channel: Channel }): Promise<unknown> {
const autoPublishChannels = await message.guild.getSetting('autoPublishChannels');
- autoPublishChannels.includes(channel.id)
- ? autoPublishChannels.splice(autoPublishChannels.indexOf(channel.id), 1)
- : autoPublishChannels.push(channel.id);
- await message.guild.setSetting('autoPublishChannels', autoPublishChannels);
- return await message.util.reply(
- `${this.client.util.emojis.success} Successfully ${
- autoPublishChannels.includes(channel.id) ? 'disabled' : 'enabled'
- } auto publishing in <#${channel.id}>.`
+ const newValue = this.client.util.addOrRemoveFromArray(
+ autoPublishChannels.includes(channel.id) ? 'remove' : 'add',
+ autoPublishChannels,
+ channel.id
);
+ await message.guild.setSetting('autoPublishChannels', newValue);
+ return await message.util.reply({
+ content: `${this.client.util.emojis.success} Successfully ${
+ autoPublishChannels.includes(channel.id) ? 'disabled' : 'enabled'
+ } auto publishing in <#${channel.id}>.`,
+ allowedMentions: AllowedMentions.none()
+ });
}
}
diff --git a/src/commands/config/blacklist.ts b/src/commands/config/blacklist.ts
new file mode 100644
index 0000000..4706041
--- /dev/null
+++ b/src/commands/config/blacklist.ts
@@ -0,0 +1,132 @@
+import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, Global } from '@lib';
+import { Argument } from 'discord-akairo';
+import { Channel, User } from 'discord.js';
+
+export default class BlacklistCommand extends BushCommand {
+ public constructor() {
+ super('blacklist', {
+ aliases: ['blacklist', 'unblacklist'],
+ category: 'config',
+ description: {
+ content: 'A command to blacklist users and channels.',
+ usage: 'blacklist|unblacklist <user|channel>',
+ examples: ['blacklist @user', 'unblacklist #channel']
+ },
+ args: [
+ {
+ id: 'target',
+ type: Argument.union('channel', 'user'),
+ match: 'phrase',
+ prompt: {
+ start: 'What channel or user that you would like to blacklist/unblacklist?',
+ retry: '{error} Pick a valid command.',
+ optional: false
+ }
+ },
+ {
+ id: 'global',
+ match: 'flag',
+ flag: '--global'
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'action',
+ description: 'Would you like to add or remove someone or something from/to the blacklist?',
+ type: 'STRING',
+ choices: [
+ {
+ name: 'blacklist',
+ value: 'blacklist'
+ },
+ {
+ name: 'unblacklist',
+ value: 'unblacklist'
+ }
+ ],
+ required: true
+ },
+ {
+ name: 'target',
+ description: 'What channel or user that you would like to blacklist/unblacklist?',
+ type: 'STRING',
+ required: true
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ });
+ }
+
+ public async exec(
+ message: BushMessage | BushSlashMessage,
+ args: { action: 'blacklist' | 'unblacklist'; target: Channel | User | string; global: boolean }
+ ): Promise<unknown> {
+ let action: 'blacklist' | 'unblacklist' | 'toggle' =
+ args.action ?? (message?.util?.parsed?.alias as 'blacklist' | 'unblacklist') ?? 'toggle';
+ const global = args.global && message.author.isOwner();
+ const target =
+ typeof args.target === 'string'
+ ? (await Argument.cast('channel', this.client.commandHandler.resolver, message as BushMessage, args.target)) ??
+ (await Argument.cast('user', this.client.commandHandler.resolver, message as BushMessage, args.target))
+ : args.target;
+ if (!target) return await message.util.reply(`${this.client.util.emojis.error} Choose a valid channel or user.`);
+ const targetID = target.id;
+
+ if (global) {
+ if (action === 'toggle') {
+ const blacklistedUsers = (await Global.findByPk(this.client.config.dev ? 'development' : 'production'))
+ .blacklistedUsers;
+ const blacklistedChannels = (await Global.findByPk(this.client.config.dev ? 'development' : 'production'))
+ .blacklistedChannels;
+ action = blacklistedUsers.includes(targetID) || blacklistedChannels.includes(targetID) ? 'unblacklist' : 'blacklist';
+ }
+ const success = await this.client.util
+ .insertOrRemoveFromGlobal(
+ action === 'blacklist' ? 'add' : 'remove',
+ target instanceof User ? 'blacklistedUsers' : 'blacklistedChannels',
+ targetID
+ )
+ .catch(() => false);
+ if (!success)
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} There was an error globally **${action}ing** ${
+ target?.tag ?? target.name
+ }.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ else
+ return await message.util.reply({
+ content: `${this.client.util.emojis.success} Successfully **${action}ed** ${target?.tag ?? target.name} globally.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ // guild disable
+ } else {
+ const blacklistedChannels = await message.guild.getSetting('blacklistedChannels');
+ const blacklistedUsers = await message.guild.getSetting('blacklistedUsers');
+ if (action === 'toggle') {
+ action = blacklistedChannels.includes(targetID) ?? blacklistedUsers.includes(targetID) ? 'unblacklist' : 'blacklist';
+ }
+ const newValue = this.client.util.addOrRemoveFromArray(
+ action === 'blacklist' ? 'add' : 'remove',
+ target instanceof User ? blacklistedUsers : blacklistedChannels,
+ targetID
+ );
+ const success = await message.guild
+ .setSetting(target instanceof User ? 'blacklistedUsers' : 'blacklistedChannels', newValue)
+ .catch(() => false);
+ if (!success)
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} There was an error **${action}ing** ${target?.tag ?? target.name}.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ else
+ return await message.util.reply({
+ content: `${this.client.util.emojis.success} Successfully **${action}ed** ${target?.tag ?? target.name}.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+ }
+}
diff --git a/src/commands/config/disable.ts b/src/commands/config/disable.ts
new file mode 100644
index 0000000..007cdb1
--- /dev/null
+++ b/src/commands/config/disable.ts
@@ -0,0 +1,128 @@
+import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage, Global } from '@lib';
+
+export default class DisableCommand extends BushCommand {
+ public constructor() {
+ super('disable', {
+ aliases: ['disable', 'enable'],
+ category: 'config',
+ description: {
+ content: 'A command to disable and enable commands.',
+ usage: 'disable|enable <command>',
+ examples: ['enable ban', 'disable kick']
+ },
+ args: [
+ {
+ id: 'command',
+ type: 'commandAlias',
+ match: 'phrase',
+ prompt: {
+ start: 'What command would you like to enable/disable?',
+ retry: '{error} Pick a valid command.',
+ optional: false
+ }
+ },
+ {
+ id: 'global',
+ match: 'flag',
+ flag: '--global'
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'action',
+ description: 'Would you like to disable or enable a command?',
+ type: 'STRING',
+ choices: [
+ {
+ name: 'enable',
+ value: 'enable'
+ },
+ {
+ name: 'disable',
+ value: 'disable'
+ }
+ ],
+ required: true
+ },
+ {
+ name: 'command',
+ description: 'What command would you like to enable/disable?',
+ type: 'STRING',
+ required: true
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD']
+ });
+ }
+
+ blacklistedCommands = ['eval', 'disable'];
+
+ public async exec(
+ 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';
+ const global = args.global && message.author.isOwner();
+ const commandID = (args.command as BushCommand).id;
+
+ if (global) {
+ if (action === 'toggle') {
+ const disabledCommands = (await Global.findByPk(this.client.config.dev ? 'development' : 'production'))
+ .disabledCommands;
+ action = disabledCommands.includes(commandID) ? 'disable' : 'enable';
+ }
+ const success = await this.client.util
+ .insertOrRemoveFromGlobal(action === 'disable' ? 'remove' : 'add', 'disabledCommands', commandID)
+ .catch(() => false);
+ if (!success)
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} There was an error globally **${action.substr(
+ 0,
+ action.length - 2
+ )}ing** the **${commandID}** command.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ else
+ return await message.util.reply({
+ content: `${this.client.util.emojis.success} Successfully **${action.substr(
+ 0,
+ action.length - 2
+ )}ed** the **${commandID}** command globally.`,
+ allowedMentions: AllowedMentions.none()
+ });
+
+ // guild disable
+ } else {
+ const disabledCommands = await message.guild.getSetting('disabledCommands');
+ if (action === 'toggle') {
+ action = disabledCommands.includes(commandID) ? 'disable' : 'enable';
+ }
+ const newValue = this.client.util.addOrRemoveFromArray(
+ action === 'disable' ? 'remove' : 'add',
+ disabledCommands,
+ commandID
+ );
+ const success = await message.guild.setSetting('disabledCommands', newValue).catch(() => false);
+ if (!success)
+ return await message.util.reply({
+ content: `${this.client.util.emojis.error} There was an error **${action.substr(
+ 0,
+ action.length - 2
+ )}ing** the **${commandID}** command.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ else
+ return await message.util.reply({
+ content: `${this.client.util.emojis.success} Successfully **${action.substr(
+ 0,
+ action.length - 2
+ )}ed** the **${commandID}** command.`,
+ allowedMentions: AllowedMentions.none()
+ });
+ }
+ }
+}
diff --git a/src/commands/config/prefix.ts b/src/commands/config/prefix.ts
index 79956be..380442b 100644
--- a/src/commands/config/prefix.ts
+++ b/src/commands/config/prefix.ts
@@ -1,4 +1,4 @@
-import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage } from '@lib';
export default class PrefixCommand extends BushCommand {
public constructor() {
@@ -40,15 +40,17 @@ export default class PrefixCommand extends BushCommand {
const oldPrefix = await message.guild.getSetting('prefix');
await message.guild.setSetting('prefix', args.prefix ?? this.client.config.prefix);
if (args.prefix) {
- return await message.util.send(
- `${this.client.util.emojis.success} changed the server's prefix ${oldPrefix ? `from \`${oldPrefix}\`` : ''} to \`${
- args.prefix
- }\`.`
- );
+ return await message.util.send({
+ content: `${this.client.util.emojis.success} changed the server's prefix ${
+ oldPrefix ? `from \`${oldPrefix}\`` : ''
+ } to \`${args.prefix}\`.`,
+ allowedMentions: AllowedMentions.none()
+ });
} else {
- return await message.util.send(
- `${this.client.util.emojis.success} reset the server's prefix to \`${this.client.config.prefix}\`.`
- );
+ return await message.util.send({
+ content: `${this.client.util.emojis.success} reset the server's prefix to \`${this.client.config.prefix}\`.`,
+ allowedMentions: AllowedMentions.none()
+ });
}
}
}
diff --git a/src/commands/dev/__template.ts b/src/commands/dev/__template.ts
new file mode 100644
index 0000000..ffc67ae
--- /dev/null
+++ b/src/commands/dev/__template.ts
@@ -0,0 +1,61 @@
+import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+
+export default class TemplateCommand extends BushCommand {
+ public constructor() {
+ super('template', {
+ aliases: ['template'],
+ category: 'template',
+ description: {
+ content: 'Command description.',
+ usage: 'template <requiredArg> [optionalArg]',
+ examples: ['template 1 2']
+ },
+ args: [
+ {
+ id: 'required_argument',
+ type: 'string',
+ match: 'phrase',
+ prompt: {
+ start: 'What would you like to set your first argument to be?',
+ retry: '{error} Pick a valid argument.',
+ optional: false
+ }
+ },
+ {
+ id: 'optional_argument',
+ type: 'string',
+ match: 'phrase',
+ prompt: {
+ start: 'What would you like to set your second argument to be?',
+ retry: '{error} Pick a valid argument.',
+ optional: true
+ }
+ }
+ ],
+ slash: false, //set this to true
+ slashOptions: [
+ {
+ name: 'required_argument',
+ description: 'What would you like to set your first argument to be?',
+ type: 'STRING',
+ required: true
+ },
+ {
+ name: 'optional_argument',
+ description: 'What would you like to set your second argument to be?',
+ type: 'STRING',
+ required: false
+ }
+ ],
+ superUserOnly: true,
+ ownerOnly: true,
+ channel: 'guild',
+ hidden: true,
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES']
+ });
+ }
+ public async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
+ return await message.util.reply(`${this.client.util.emojis.error} Do not use the template command.`);
+ }
+}
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts
index f3a30ab..76a78ba 100644
--- a/src/commands/dev/eval.ts
+++ b/src/commands/dev/eval.ts
@@ -20,7 +20,7 @@ export default class EvalCommand extends BushCommand {
aliases: ['eval', 'ev'],
category: 'dev',
description: {
- content: 'Use the command to eval stuff in the bot.',
+ content: 'Evaluate code.',
usage: 'eval [--depth #] <code> [--sudo] [--silent] [--delete] [--proto] [--hidden] [--ts]',
examples: ['eval message.guild.name', 'eval this.client.ownerID']
},
@@ -72,7 +72,6 @@ export default class EvalCommand extends BushCommand {
}
}
],
- ownerOnly: true,
slash: true,
slashOptions: [
{
@@ -117,7 +116,8 @@ export default class EvalCommand extends BushCommand {
type: 'BOOLEAN',
required: false
}
- ]
+ ],
+ ownerOnly: true
});
}
@@ -141,8 +141,8 @@ export default class EvalCommand extends BushCommand {
}
const code: { js?: string | null; ts?: string | null; lang?: 'js' | 'ts' } = {};
args.code = args.code.replace(/[“”]/g, '"');
- args.code = args.code.replace(/```/g, '');
- if (args.typescript) {
+ args.code = args.code.replace(/```*(?:js|ts)?/g, '');
+ if (args.typescript || message) {
code.ts = args.code;
code.js = transpile(args.code);
code.lang = 'ts';
diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts
new file mode 100644
index 0000000..08f74e8
--- /dev/null
+++ b/src/commands/dev/servers.ts
@@ -0,0 +1,48 @@
+import { Guild, MessageEmbed } from 'discord.js';
+import { BushCommand, BushMessage, BushSlashMessage } from '../../lib';
+
+export default class ServersCommand extends BushCommand {
+ public constructor() {
+ super('servers', {
+ aliases: ['servers'],
+ category: 'dev',
+ description: {
+ content: 'Displays all the severs the bot is in',
+ usage: 'servers',
+ examples: ['servers']
+ },
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES'],
+ superUserOnly: true
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage): Promise<unknown> {
+ const maxLength = 10;
+ const guilds = this.client.guilds.cache.sort((a, b) => (a.memberCount < b.memberCount ? 1 : -1)).array();
+ 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 = this.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')
+ .setColor(this.client.util.colors.default);
+ });
+ embeds.push(embed);
+ });
+
+ return await this.client.util.buttonPaginate(message, embeds);
+ }
+}
diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts
index f2ae6c7..4ec4c08 100644
--- a/src/commands/dev/setLevel.ts
+++ b/src/commands/dev/setLevel.ts
@@ -54,10 +54,12 @@ export default class SetLevelCommand extends BushCommand {
const [levelEntry] = await Level.findOrBuild({
where: {
- id: user.id
+ user: user.id,
+ guild: message.guild.id
},
defaults: {
- id: user.id
+ user: user.id,
+ guild: message.guild.id
}
});
await levelEntry.update({ xp: Level.convertLevelToXp(level) });
diff --git a/src/commands/dev/sh.ts b/src/commands/dev/sh.ts
new file mode 100644
index 0000000..d53e500
--- /dev/null
+++ b/src/commands/dev/sh.ts
@@ -0,0 +1,84 @@
+import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+import chalk from 'chalk';
+import { exec } from 'child_process';
+import { Constants } from 'discord-akairo';
+import { MessageEmbed, Util } from 'discord.js';
+import { promisify } from 'util';
+
+const sh = promisify(exec);
+const clean = (text) => {
+ chalk.toString;
+ if (typeof text === 'string') {
+ return (text = Util.cleanCodeBlockContent(text));
+ } else return text;
+};
+export default class ShCommand extends BushCommand {
+ public constructor() {
+ super('sh', {
+ aliases: ['sh', 'shell', 'cmd'],
+ category: 'dev',
+ description: {
+ content: 'Run shell commands.',
+ usage: 'sh <command>',
+ examples: ['sh git pull']
+ },
+ args: [
+ {
+ id: 'command',
+ type: Constants.ArgumentTypes.STRING,
+ match: Constants.ArgumentMatches.REST,
+ prompt: {
+ start: 'What would you like run',
+ retry: '{error} Invalid command to run.'
+ }
+ }
+ ],
+ ownerOnly: true
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage, { command }: { command: string }): Promise<unknown> {
+ if (!this.client.config.owners.includes(message.author.id))
+ return await message.util.reply(`${this.client.util.emojis.error} Only my developers can run this command.`);
+ const input = clean(command);
+
+ const embed = new MessageEmbed()
+ .setColor(this.client.util.colors.gray)
+ .setFooter(message.author.tag, message.author.avatarURL({ dynamic: true }))
+ .setTimestamp()
+ .setTitle('Shell Command')
+ .addField('📥 Input', await this.client.util.codeblock(input, 1024, 'sh'))
+ .addField('Running', this.client.util.emojis.loading);
+
+ await message.util.reply({ embeds: [embed] });
+
+ const pattern = [
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'
+ ].join('|');
+ function strip(abc: string): string {
+ return abc.replace(new RegExp(pattern, 'g'), '');
+ }
+ try {
+ const output = await sh(command);
+ const stdout = strip(clean(output.stdout));
+ const stderr = strip(clean(output.stderr));
+
+ embed
+ .setTitle(`${this.client.util.emojis.successFull} Executed command successfully.`)
+ .setColor(this.client.util.colors.success)
+ .spliceFields(1, 1);
+
+ if (stdout) embed.addField('📤 stdout', await this.client.util.codeblock(stdout, 1024, 'json'));
+ if (stderr) embed.addField('📤 stderr', await this.client.util.codeblock(stderr, 1024, 'json'));
+ } catch (e) {
+ embed
+ .setTitle(`${this.client.util.emojis.errorFull} An error occurred while executing.`)
+ .setColor(this.client.util.colors.error)
+ .spliceFields(1, 1);
+
+ embed.addField('📤 Output', await this.client.util.codeblock(e?.stack, 1024, 'js'));
+ }
+ await message.util.edit({ embeds: [embed] });
+ }
+}
diff --git a/src/commands/info/color.ts b/src/commands/info/color.ts
index 4db20e7..45c2545 100644
--- a/src/commands/info/color.ts
+++ b/src/commands/info/color.ts
@@ -1,6 +1,15 @@
-import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
-import { Constants } from 'discord-akairo';
-import { ColorResolvable, MessageEmbed } from 'discord.js';
+import { BushCommand, BushGuildMember, BushMessage, BushRole, BushSlashMessage } from '@lib';
+import { Argument } from 'discord-akairo';
+import { ColorResolvable, MessageEmbed, Role } from 'discord.js';
+import { Constructor } from 'tinycolor2';
+
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const tinycolor: Constructor = require('tinycolor2'); // this is the only way I got it to work consistently
+const isValidTinyColor = (_message: BushMessage, phase: string) => {
+ // if the phase is a number it converts it to hex incase it could be representing a color in decimal
+ const newPhase = Number.isNaN(phase) ? phase : `#${Number(phase).toString(16)}`;
+ return tinycolor(newPhase).isValid() ? newPhase : null;
+};
export default class ColorCommand extends BushCommand {
public constructor() {
@@ -8,18 +17,17 @@ export default class ColorCommand extends BushCommand {
aliases: ['color'],
category: 'info',
description: {
- content: 'See what color a hex code is.',
- usage: 'color <color>',
+ content: 'Find the color of a hex code, user, or role.',
+ usage: 'color <color|role|user>',
examples: ['color #0000FF']
},
args: [
{
id: 'color',
- type: /^#?(?<code>[0-9A-F]{6})$/i,
- match: Constants.ArgumentMatches.PHRASE,
+ type: Argument.union(isValidTinyColor, 'role', 'member'),
prompt: {
- start: 'What color value would you like to see the color of',
- retry: '{error} Choose a valid hex color code.'
+ start: 'What color code, role, or user would you like to find the color of?',
+ retry: '{error} Choose a valid color, role, or member.'
}
}
],
@@ -28,14 +36,27 @@ export default class ColorCommand extends BushCommand {
});
}
+ public removePrefixAndParenthesis(color: string): string{
+ return color.substr(4, color.length-5)
+ }
+
public async exec(
message: BushMessage | BushSlashMessage,
- { color: { match } }: { color: { match: RegExpMatchArray; matches: RegExpMatchArray[] } }
+ args: { color: string | BushRole | BushGuildMember }
): Promise<unknown> {
+ const color =
+ typeof args.color === 'string'
+ ? tinycolor(args.color)
+ : args.color instanceof Role
+ ? tinycolor(args.color.hexColor)
+ : tinycolor(args.color.displayHexColor);
+
const embed = new MessageEmbed()
- .addField('Hex', match.groups.code, false)
- .addField('RGB', this.client.util.hexToRgb(match.groups.code), false)
- .setColor(match.groups.code as ColorResolvable);
+ .addField('» Hexadecimal', color.toHexString())
+ .addField('» Decimal', `${parseInt(color.toHex(), 16)}`)
+ .addField('» HSL', this.removePrefixAndParenthesis(color.toHslString()))
+ .addField('» RGB', this.removePrefixAndParenthesis(color.toRgbString()))
+ .setColor(color.toHex() as ColorResolvable);
return await message.util.reply({ embeds: [embed] });
}
diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts
index 0977c36..a0b03a0 100644
--- a/src/commands/info/help.ts
+++ b/src/commands/info/help.ts
@@ -47,16 +47,19 @@ export default class HelpCommand extends BushCommand {
args: { command: BushCommand | string; showHidden?: boolean }
): Promise<unknown> {
const prefix = this.client.config.dev ? 'dev ' : message.util.parsed.prefix;
- let ButtonRow: MessageActionRow;
- if (!this.client.config.dev) {
- ButtonRow = new MessageActionRow().addComponents(
- new MessageButton({
- style: 'LINK',
- label: 'Invite Me',
- url: `https://discord.com/api/oauth2/authorize?client_id=${this.client.user.id}&permissions=2147483647&scope=bot%20applications.commands`
- })
- );
- }
+ const components =
+ !this.client.config.dev || !this.client.guilds.cache.some((guild) => guild.ownerId === message.author.id)
+ ? [
+ new MessageActionRow().addComponents(
+ new MessageButton({
+ style: 'LINK',
+ label: 'Invite Me',
+ url: `https://discord.com/api/oauth2/authorize?client_id=${this.client.user.id}&permissions=2147483647&scope=bot%20applications.commands`
+ })
+ )
+ ]
+ : undefined;
+
const isOwner = this.client.isOwner(message.author);
const isSuperUser = this.client.isSuperUser(message.author);
const command = args.command
@@ -65,20 +68,21 @@ export default class HelpCommand extends BushCommand {
: args.command
: null;
if (!isOwner) args.showHidden = false;
- if (!command) {
+ if (!command || command.completelyHide) {
const embed = new MessageEmbed().setColor(this.client.util.colors.default).setTimestamp();
if (message.guild) {
- embed.setFooter(`For more information about a command use '${prefix}help <command>'`);
+ embed.setFooter(`For more information about a command use ${prefix}help <command>`);
}
for (const [, category] of this.handler.categories) {
const categoryFilter = category.filter((command) => {
+ if (command.completelyHide) return false;
if (command.hidden && !args.showHidden) return false;
if (command.channel == 'guild' && !message.guild && !args.showHidden) return false;
if (command.ownerOnly && !isOwner) return false;
if (command.superUserOnly && !isSuperUser) {
return false;
}
- return !(command.restrictedGuilds?.includes(message.guild.id) == false && !args.showHidden);
+ return !(command.restrictedGuilds?.includes(message.guild.id) === false && !args.showHidden);
});
const categoryNice = category.id
.replace(/(\b\w)/gi, (lc): string => lc.toUpperCase())
@@ -90,24 +94,24 @@ export default class HelpCommand extends BushCommand {
embed.addField(`${categoryNice}`, `${categoryCommands.join(' ')}`);
}
}
- return await message.util.reply({ embeds: [embed], components: ButtonRow ? [ButtonRow] : undefined });
+ return await message.util.reply({ embeds: [embed], components });
}
const embed = new MessageEmbed()
.setColor(this.client.util.colors.default)
- .setTitle(`\`${command.description?.usage ? command.description.usage : 'This command does not have usages.'}\``)
+ .setTitle(`\`${command.description?.usage || `${this.client.util.emojis.error} This command does not have usages.`}\``)
.addField(
'Description',
- `${command.description?.content ? command.description.content : '*This command does not have a description.*'} ${
- command.ownerOnly ? '\n__Dev Only__' : ''
+ `${command.description?.content || `${this.client.util.emojis.error} This command does not have a description.`} ${
+ command.ownerOnly ? '\n__Developer Only__' : ''
} ${command.superUserOnly ? '\n__Super User Only__' : ''}`
);
if (command.aliases?.length > 1) embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true);
- if (command.description?.examples && command.description.examples.length) {
+ if (command.description?.examples?.length) {
embed.addField('Examples', `\`${command.description.examples.join('`\n`')}\``, true);
}
- return await message.util.reply({ embeds: [embed], components: ButtonRow ? [ButtonRow] : undefined });
+ return await message.util.reply({ embeds: [embed], components });
}
}
diff --git a/src/commands/info/userInfo.ts b/src/commands/info/userInfo.ts
index 50756a6..5e70323 100644
--- a/src/commands/info/userInfo.ts
+++ b/src/commands/info/userInfo.ts
@@ -78,7 +78,7 @@ export default class UserInfoCommand extends BushCommand {
if (user.premiumSinceTimestamp) emojis.push(this.client.consts.mappings.otherEmojis.BOOSTER);
const createdAt = user.user.createdAt.toLocaleString(),
- createdAtDelta = moment(user.user.createdAt).diff(moment()).toLocaleString(),
+ createdAtDelta = moment(moment(user.user.createdAt).diff(moment())).toLocaleString(),
joinedAt = user.joinedAt?.toLocaleString(),
joinedAtDelta = moment(user.joinedAt)?.diff(moment()).toLocaleString(),
premiumSince = user.premiumSince?.toLocaleString(),
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts
index c5833fc..874d5ed 100644
--- a/src/commands/moderation/ban.ts
+++ b/src/commands/moderation/ban.ts
@@ -37,6 +37,11 @@ export default class BanCommand extends BushCommand {
match: 'option',
type: Argument.range('integer', 0, 7, true),
default: 0
+ },
+ {
+ id: 'force',
+ flag: '--force',
+ match: 'flag'
}
],
slash: true,
@@ -77,10 +82,16 @@ export default class BanCommand extends BushCommand {
}
async exec(
message: BushMessage | BushSlashMessage,
- { user, reason, days }: { user: User; reason?: { duration: number; contentWithoutTime: string }; days?: number }
+ {
+ user,
+ reason,
+ days,
+ force
+ }: { user: User; reason?: { duration: number; contentWithoutTime: string }; days?: number; force: boolean }
): Promise<unknown> {
const member = message.guild.members.cache.get(user.id) as BushGuildMember;
- const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'ban');
+ const useForce = force && message.author.isOwner();
+ const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'ban', true, useForce);
if (canModerateResponse !== true) {
return message.util.reply(canModerateResponse);
diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts
index ccf35a9..74ace94 100644
--- a/src/commands/moderation/kick.ts
+++ b/src/commands/moderation/kick.ts
@@ -28,6 +28,11 @@ export default class KickCommand extends BushCommand {
retry: '{error} Choose a valid kick reason.',
optional: true
}
+ },
+ {
+ id: 'force',
+ flag: '--force',
+ match: 'flag'
}
],
slash: true,
@@ -50,9 +55,13 @@ export default class KickCommand extends BushCommand {
});
}
- async exec(message: BushMessage | BushSlashMessage, { user, reason }: { user: BushUser; reason?: string }): Promise<unknown> {
+ async exec(
+ 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 canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'kick');
+ const useForce = force && message.author.isOwner();
+ const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'kick', true, useForce);
if (canModerateResponse !== true) {
return message.util.reply(canModerateResponse);
diff --git a/src/commands/moderation/lockdown.ts b/src/commands/moderation/lockdown.ts
new file mode 100644
index 0000000..e67d110
--- /dev/null
+++ b/src/commands/moderation/lockdown.ts
@@ -0,0 +1,45 @@
+import { BushCommand, BushMessage, BushNewsChannel, BushSlashMessage, BushTextChannel } from '@lib';
+
+export default class LockdownCommand extends BushCommand {
+ public constructor() {
+ super('lockdown', {
+ aliases: ['lockdown', 'unlockdown'],
+ category: 'moderation',
+ description: {
+ content: 'Allows you to lockdown a channel or all configured channels..',
+ usage: 'lockdown [--all]',
+ examples: ['lockdown', 'lockdown --all']
+ },
+ args: [
+ {
+ id: 'all',
+ type: 'flag',
+ flag: '--all'
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'all',
+ description: 'Would you like to lockdown all channels?',
+ type: 'BOOLEAN',
+ required: false
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES']
+ });
+ }
+ public async exec(message: BushMessage | BushSlashMessage, { all }: { all: boolean }): Promise<unknown> {
+ return await message.util.reply('no');
+ if (!all) {
+ if (!['GUILD_TEXT', 'GUILD_NEWS'].includes(message.channel.type))
+ return message.util.reply(`${this.client.util.emojis.error} You can only lock down text and announcement channels.`);
+ const lockdownSuccess = await this.client.util.lockdownChannel({
+ channel: message.channel as BushTextChannel | BushNewsChannel,
+ moderator: message.author
+ });
+ }
+ }
+}
diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts
index 4b57a4f..e3da45f 100644
--- a/src/commands/moderation/modlog.ts
+++ b/src/commands/moderation/modlog.ts
@@ -72,7 +72,7 @@ export default class ModlogCommand extends BushCommand {
color: this.client.util.colors.default
})
);
- this.client.util.buttonPaginate(message, embedPages, '', true);
+ return await this.client.util.buttonPaginate(message, embedPages, '', true);
} else if (search) {
const entry = await ModLog.findByPk(search as string);
if (!entry) return message.util.send(`${this.client.util.emojis.error} That modlog does not exist.`);
diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts
index 7443c55..2004eb8 100644
--- a/src/commands/moderation/mute.ts
+++ b/src/commands/moderation/mute.ts
@@ -29,6 +29,11 @@ export default class MuteCommand extends BushCommand {
retry: '{error} Choose a valid mute reason and duration.',
optional: true
}
+ },
+ {
+ id: 'force',
+ flag: '--force',
+ match: 'flag'
}
],
slash: true,
@@ -53,11 +58,12 @@ export default class MuteCommand extends BushCommand {
}
async exec(
message: BushMessage | BushSlashMessage,
- { user, reason }: { user: BushUser; reason?: { duration: number; contentWithoutTime: string } }
+ { user, reason, force }: { user: BushUser; reason?: { duration: number; contentWithoutTime: string }; force: boolean }
): Promise<unknown> {
const error = this.client.util.emojis.error;
const member = message.guild.members.cache.get(user.id) as BushGuildMember;
- const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'mute');
+ const useForce = force && message.author.isOwner();
+ const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'mute', true, useForce);
const victimBoldTag = `**${member.user.tag}**`;
if (canModerateResponse !== true) {
diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts
index 3d353ca..d5bf009 100644
--- a/src/commands/moderation/warn.ts
+++ b/src/commands/moderation/warn.ts
@@ -28,6 +28,11 @@ export default class WarnCommand extends BushCommand {
retry: '{error} Choose a valid warn reason.',
optional: true
}
+ },
+ {
+ id: 'force',
+ flag: '--force',
+ match: 'flag'
}
],
slash: true,
@@ -52,10 +57,11 @@ export default class WarnCommand extends BushCommand {
}
public async exec(
message: BushMessage | BushSlashMessage,
- { user, reason }: { user: BushUser; reason: string }
+ { user, reason, force }: { user: BushUser; reason: string; force: boolean }
): Promise<unknown> {
const member = message.guild.members.cache.get(user.id) as BushGuildMember;
- const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'warn');
+ const useForce = force && message.author.isOwner();
+ const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'warn', true, useForce);
const victimBoldTag = `**${member.user.tag}**`;
if (canModerateResponse !== true) {
diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts
index b19d3bc..08b42ff 100644
--- a/src/commands/moulberry-bush/capePerms.ts
+++ b/src/commands/moulberry-bush/capePerms.ts
@@ -4,34 +4,6 @@ import { MessageEmbed } from 'discord.js';
import got from 'got';
export default class CapePermissionsCommand extends BushCommand {
- private nameMap = {
- patreon1: 'Patreon Tier 1',
- patreon2: 'Patreon Tier 2',
- fade: 'Fade',
- contrib: 'Contributor',
- nullzee: 'Patreon Tier 1',
- gravy: 'Patreon Tier 1',
- space: 'Patreon Tier 1',
- mcworld: 'Patreon Tier 1',
- lava: 'Patreon Tier 1',
- packshq: 'Patreon Tier 1',
- mbstaff: 'Patreon Tier 1',
- thebakery: 'Patreon Tier 1',
- negative: 'Patreon Tier 1',
- void: 'Patreon Tier 1',
- ironmoon: 'Patreon Tier 1',
- krusty: 'Patreon Tier 1',
- furf: 'Patreon Tier 1',
- soldier: 'Patreon Tier 1',
- dsm: 'Patreon Tier 1',
- zera: 'Patreon Tier 1',
- tunnel: 'Patreon Tier 1',
- alexxoffi: 'Patreon Tier 1',
- parallax: 'Patreon Tier 1',
- jakethybro: 'Patreon Tier 1',
- planets: 'Patreon Tier 1'
- };
-
public constructor() {
super('capepermissions', {
aliases: ['capeperms', 'capeperm', 'capepermissions'],
@@ -80,7 +52,7 @@ export default class CapePermissionsCommand extends BushCommand {
let capeperms: Capeperms, uuid: string;
try {
- uuid = await this.client.util.mcUUID(args.ign);
+ uuid = await this.client.util.findUUID(args.ign);
} catch (e) {
return await message.util.reply(
`${this.client.util.emojis.error} \`${args.ign}\` doesn't appear to be a valid username.`
@@ -88,7 +60,7 @@ export default class CapePermissionsCommand extends BushCommand {
}
try {
- capeperms = await got.get('https://moulberry.codes/permscapes.json').json();
+ capeperms = await got.get('http://moulberry.codes/permscapes.json').json();
} catch (error) {
capeperms = null;
}
@@ -105,6 +77,7 @@ export default class CapePermissionsCommand extends BushCommand {
index = i;
break;
}
+ continue;
}
if (index == null)
return await message.util.reply(
diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts
index fc1e93e..86ab985 100644
--- a/src/commands/moulberry-bush/level.ts
+++ b/src/commands/moulberry-bush/level.ts
@@ -1,4 +1,4 @@
-import { BushCommand, BushMessage, BushSlashMessage, BushUser, Level } from '@lib';
+import { BushCommand, BushGuild, BushMessage, BushSlashMessage, BushUser, Level } from '@lib';
/*
import canvas from 'canvas';
import { MessageAttachment } from 'discord.js';
@@ -127,8 +127,8 @@ export default class LevelCommand extends BushCommand {
return image.toBuffer();
} */
- private async getResponse(user: BushUser): Promise<string> {
- const userLevelRow = await Level.findByPk(user.id);
+ private async getResponse(user: BushUser, guild: BushGuild): Promise<string> {
+ const userLevelRow = await Level.findOne({ where: { user: user.id, guild: guild.id } });
if (userLevelRow) {
return `${user ? `${user.tag}'s` : 'Your'} level is ${userLevelRow.level} (${userLevelRow.xp} XP)`;
} else {
@@ -143,6 +143,6 @@ export default class LevelCommand extends BushCommand {
// 'lel.png'
// )
// );
- await message.reply(await this.getResponse(user || message.author));
+ await message.reply(await this.getResponse(user || message.author, message.guild));
}
}
diff --git a/src/commands/moulberry-bush/report.ts b/src/commands/moulberry-bush/report.ts
new file mode 100644
index 0000000..ebc8f1d
--- /dev/null
+++ b/src/commands/moulberry-bush/report.ts
@@ -0,0 +1,121 @@
+import { Constants } from 'discord-akairo';
+import { GuildMember, MessageEmbed, TextChannel } from 'discord.js';
+import moment from 'moment';
+import { AllowedMentions, BushCommand, BushMessage } from '../../lib';
+
+export default class ReportCommand extends BushCommand {
+ public constructor() {
+ super('report', {
+ aliases: ['report'],
+ category: "Moulberry's Bush",
+ description: {
+ content: 'A command to report a user..',
+ usage: 'report <user> <reason/evidence>',
+ examples: ['report IRONM00N']
+ },
+ args: [
+ {
+ id: 'member',
+ type: Constants.ArgumentTypes.MEMBER,
+ match: Constants.ArgumentMatches.PHRASE,
+ prompt: {
+ start: 'Who would you like to report?',
+ retry: `{error} Choose a valid user to report.`,
+ optional: false
+ }
+ },
+ {
+ id: 'evidence',
+ type: Constants.ArgumentTypes.STRING,
+ match: Constants.ArgumentMatches.REST,
+ prompt: {
+ start: 'What evidence do you have?',
+ retry: `{error} Provide what did they do wrong.`,
+ optional: true
+ }
+ }
+ ],
+ clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'],
+ channel: 'guild',
+ restrictedGuilds: ['516977525906341928'],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'user',
+ description: 'The user you would like to report.',
+ type: 'USER',
+ required: true
+ },
+ {
+ name: 'evidence',
+ description: 'What did the user do wrong?',
+ type: 'STRING',
+ required: false
+ }
+ ],
+ slashGuilds: ['516977525906341928']
+ });
+ }
+
+ public async exec(message: BushMessage, { member, evidence }: { member: GuildMember; evidence: string }): Promise<unknown> {
+ if (message.guild.id != this.client.consts.mappings.guilds.bush)
+ return await message.util.reply(`${this.client.util.emojis.error} This command can only be run in Moulberry's bush.`);
+ if (!member) return await message.util.reply(`${this.client.util.emojis.error} Choose someone to report`);
+ if (member.user.id === '322862723090219008')
+ return await message.util.reply({
+ content: `Thank you for your report! We take these allegations very seriously and have reported <@${member.user.id}> to the FBI!`,
+ allowedMentions: AllowedMentions.none()
+ });
+ if (member.user.bot)
+ return await message.util.reply(
+ `${this.client.util.emojis.error} You cannot report a bot <:WeirdChamp:756283321301860382>.`
+ );
+
+ //// if (!evidence) evidence = 'No Evidence.';
+ //todo: Add channel id to db instead of hard coding it & allow in any guild
+ //The formatting of the report is mostly copied from carl since it is pretty good when it actually works
+ const reportEmbed = new MessageEmbed()
+ .setFooter(`Reporter ID: ${message.author.id} Reported ID: ${member.user.id}`)
+ .setTimestamp()
+ .setAuthor(`Report From: ${message.author.tag}`, message.author.avatarURL({ dynamic: true }))
+ .setTitle('New Report')
+ .setColor(this.client.util.colors.red)
+ .setDescription(evidence)
+ .addField(
+ 'Reporter',
+ `**Name:**${message.author.tag} <@${message.author.id}>\n**Joined:** ${moment(
+ message.member.joinedTimestamp
+ ).fromNow()}\n**Created:** ${moment(message.author.createdTimestamp).fromNow()}\n**Sent From**: <#${
+ message.channel.id
+ }> [Jump to context](${message.url})`,
+ true
+ )
+ .addField(
+ 'Reported User',
+ `**Name:**${member.user.tag} <@${member.user.id}>\n**Joined:** ${moment(
+ member.joinedTimestamp
+ ).fromNow()}\n**Created:** ${moment(member.user.createdTimestamp).fromNow()}`,
+ 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')) {
+ reportEmbed.setImage(message.attachments.first().url);
+ } else {
+ reportEmbed.addField('Attachment', message.attachments.first().url);
+ }
+ }
+ const reportChannel = <TextChannel>this.client.channels.cache.get('782972723654688848');
+ await reportChannel.send({ embeds: [reportEmbed] }).then(async (ReportMessage) => {
+ try {
+ await ReportMessage.react(this.client.util.emojis.success);
+ await ReportMessage.react(this.client.util.emojis.error);
+ } catch {
+ this.client.console.warn('ReportCommand', 'Could not react to report message.');
+ }
+ });
+ return await message.util.reply('Successfully made a report.');
+ }
+}
diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts
index a2b8c78..1681a1b 100644
--- a/src/commands/moulberry-bush/rule.ts
+++ b/src/commands/moulberry-bush/rule.ts
@@ -1,6 +1,6 @@
-import { AllowedMentions, BushCommand, BushMessage, BushSlashMessage } from '@lib';
import { Argument, Constants } from 'discord-akairo';
import { MessageEmbed, User } from 'discord.js';
+import { AllowedMentions, BushCommand, BushMessage } from '../../lib';
const rules = [
{
@@ -106,10 +106,7 @@ export default class RuleCommand extends BushCommand {
});
}
- public async exec(
- message: BushMessage | BushSlashMessage,
- { rule, user }: { rule: undefined | number; user: User }
- ): Promise<unknown> {
+ public async exec(message: BushMessage, { rule, user }: { rule: undefined | number; user: User }): Promise<unknown> {
const rulesEmbed = new MessageEmbed()
.setColor('#ef3929')
.setFooter(`Triggered by ${message.author.tag}`, message.author.avatarURL({ dynamic: true }))
@@ -133,19 +130,21 @@ export default class RuleCommand extends BushCommand {
return;
async function respond(): Promise<unknown> {
if (!user) {
- // If the original message was a reply -> imitate it
- (message as BushMessage).reference?.messageId && !message.util.isSlash
- ? await message.channel.messages.fetch((message as BushMessage).reference.messageId).then(async (message) => {
- await message.util.reply({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() });
- })
- : await message.util.send({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() });
+ return (
+ // If the original message was a reply -> imitate it
+ message.reference?.messageId && !message.util.isSlash
+ ? await message.channel.messages.fetch(message.reference.messageId).then(async (message) => {
+ await message.util.reply({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() });
+ })
+ : await message.util.send({ embeds: [rulesEmbed], allowedMentions: AllowedMentions.users() })
+ );
} else {
- return (message as BushMessage).reference?.messageId && !message.util.isSlash
+ return message.reference?.messageId && !message.util.isSlash
? await message.util.send({
content: `<@!${user.id}>`,
embeds: [rulesEmbed],
allowedMentions: AllowedMentions.users(),
- reply: { messageReference: (message as BushMessage).reference.messageId }
+ reply: { messageReference: message.reference.messageId }
})
: await message.util.send({
content: `<@!${user.id}>`,
diff --git a/src/commands/skyblockReborn/chooseColorCommand.ts b/src/commands/skyblock-reborn/chooseColorCommand.ts
index ce70419..ce70419 100644
--- a/src/commands/skyblockReborn/chooseColorCommand.ts
+++ b/src/commands/skyblock-reborn/chooseColorCommand.ts
diff --git a/src/commands/utilities/_whoHasRole.ts b/src/commands/utilities/_whoHasRole.ts
deleted file mode 100644
index e69de29..0000000
--- a/src/commands/utilities/_whoHasRole.ts
+++ /dev/null
diff --git a/src/commands/utilities/hash.ts b/src/commands/utilities/hash.ts
new file mode 100644
index 0000000..4b5b01c
--- /dev/null
+++ b/src/commands/utilities/hash.ts
@@ -0,0 +1,42 @@
+import crypto from 'crypto';
+import { Constants } from 'discord-akairo';
+import got from 'got';
+import { BushCommand, BushMessage } from '../../lib';
+
+export default class HashCommand extends BushCommand {
+ constructor() {
+ super('hash', {
+ aliases: ['hash'],
+ category: 'utilities',
+ description: {
+ content: 'Gets the file hash of the given discord link',
+ usage: 'hash <file url>',
+ examples: ['hash https://cdn.discordapp.com/emojis/782630946435366942.png?v=1'] //nice
+ },
+ args: [
+ {
+ id: 'url',
+ type: Constants.ArgumentTypes.URL,
+ match: Constants.ArgumentMatches.PHRASE,
+ prompt: {
+ start: 'What url would you like to find the hash of?',
+ retry: '{error} Enter a valid url.'
+ }
+ }
+ ],
+ clientPermissions: ['SEND_MESSAGES']
+ });
+ }
+
+ public async exec(message: BushMessage, { url }: { url: string }): Promise<void> {
+ try {
+ const req = await got.get(url);
+ const rawHash = crypto.createHash('md5');
+ rawHash.update(req.rawBody.toString('binary'));
+ const hash = rawHash.digest('hex');
+ await message.util.reply(`\`${hash}\``);
+ } catch {
+ await message.util.reply('Unable to calculate hash.');
+ }
+ }
+}
diff --git a/src/commands/utilities/price.ts b/src/commands/utilities/price.ts
new file mode 100644
index 0000000..231930c
--- /dev/null
+++ b/src/commands/utilities/price.ts
@@ -0,0 +1,220 @@
+import { Constants } from 'discord-akairo';
+import { ColorResolvable, MessageEmbed } from 'discord.js';
+import Fuse from 'fuse.js';
+import got from 'got';
+import { BushCommand, BushMessage } from '../../lib';
+
+interface Summary {
+ amount: number;
+ pricePerUnit: number;
+ orders: number;
+}
+
+interface Bazaar {
+ success: boolean;
+ lastUpdated: number;
+ products: {
+ [key: string]: {
+ product_id: string;
+ sell_summary: Summary[];
+ buy_summary: Summary[];
+ quick_status: {
+ productId: string;
+ sellPrice: number;
+ sellVolume: number;
+ sellMovingWeek: number;
+ sellOrders: number;
+ buyPrice: number;
+ buyVolume: number;
+ buyMovingWeek: number;
+ buyOrders: number;
+ };
+ };
+ };
+}
+
+interface LowestBIN {
+ [key: string]: number;
+}
+
+interface AuctionAverages {
+ [key: string]: {
+ price?: number;
+ count?: number;
+ sales?: number;
+ clean_price?: number;
+ clean_sales?: number;
+ };
+}
+
+export default class PriceCommand extends BushCommand {
+ public constructor() {
+ 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',
+ match: Constants.ArgumentMatches.CONTENT,
+ type: Constants.ArgumentTypes.STRING,
+ prompt: {
+ start: 'What item would you like to find the price of?',
+ retry: '{error} Choose a valid item.'
+ }
+ },
+ {
+ id: 'strict',
+ match: Constants.ArgumentMatches.FLAG,
+ flag: '--strict',
+ default: false
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'item',
+ description: 'The item that you would you like to find the price of.',
+ type: 'STRING',
+ required: true
+ },
+ {
+ name: 'strict',
+ description: 'Whether or not to bypass the fuzzy search.',
+ type: 'BOOLEAN',
+ required: false
+ }
+ ]
+ });
+ }
+
+ public async exec(message: BushMessage, { item, strict }: { item: string; strict: boolean }): Promise<unknown> {
+ const errors = new Array<string>();
+ const bazaar: Bazaar = await get('https://api.hypixel.net/skyblock/bazaar').catch(() => errors.push('bazaar'));
+ const currentLowestBIN: LowestBIN = await get('https://moulberry.codes/lowestbin.json').catch(() =>
+ errors.push('current lowest BIN')
+ );
+ const averageLowestBIN: LowestBIN = await get('https://moulberry.codes/auction_averages_lbin/3day.json').catch(() =>
+ errors.push('average Lowest BIN')
+ );
+ const auctionAverages: AuctionAverages = await get('https://moulberry.codes/auction_averages/3day.json').catch(() =>
+ errors.push('auction average')
+ );
+ // adds _ to item name
+ let parsedItem = item.toString().toUpperCase().replace(/ /g, '_').replace(/'S/g, '');
+ const priceEmbed = new MessageEmbed();
+
+ if (errors?.length) {
+ priceEmbed.setFooter;
+ }
+
+ //combines all the item names from each
+ const itemNames = Array.from(
+ new Set(
+ (averageLowestBIN ? Object.keys(averageLowestBIN) : []).concat(
+ currentLowestBIN ? Object.keys(currentLowestBIN) : [],
+ auctionAverages ? Object.keys(auctionAverages) : [],
+ bazaar?.products ? Object.keys(bazaar.products) : []
+ )
+ )
+ );
+
+ // fuzzy search
+ if (!strict) {
+ parsedItem = new Fuse(itemNames)?.search(parsedItem)[0]?.item;
+ }
+
+ // If bazaar item then it there should not be any ah data
+ if (bazaar['products'][parsedItem]) {
+ const bazaarPriceEmbed = new MessageEmbed()
+ .setColor(
+ errors?.length
+ ? (this.client.util.emojis.warn as ColorResolvable)
+ : (this.client.util.colors.success as ColorResolvable)
+ )
+ .setTitle(`Bazaar Information for \`${parsedItem}\``)
+ .addField('Sell Price', Bazaar('sellPrice', 2, true))
+ .addField('Buy Price', Bazaar('buyPrice', 2, true))
+ .addField('Margin', (Number(Bazaar('buyPrice', 2, false)) - Number(Bazaar('sellPrice', 2, false))).toLocaleString())
+ .addField('Current Sell Orders', Bazaar('sellOrders', 0, true))
+ .addField('Current Buy Orders', Bazaar('buyOrders', 0, true));
+ return await message.util.reply({ embeds: [bazaarPriceEmbed] });
+ }
+
+ // Checks if the item exists in any of the action information otherwise it is not a valid item
+ if (currentLowestBIN?.[parsedItem] || averageLowestBIN?.[parsedItem] || auctionAverages?.[parsedItem]) {
+ priceEmbed
+ .setColor(this.client.util.colors.success)
+ .setTitle(`Price Information for \`${parsedItem}\``)
+ .setFooter('All information is based on the last 3 days.');
+ } else {
+ const errorEmbed = new MessageEmbed();
+ errorEmbed
+ .setColor(this.client.util.colors.error)
+ .setDescription(
+ `${this.client.util.emojis.error} \`${parsedItem}\` is not a valid item id, or it has no auction data.`
+ );
+ return await message.util.reply({ embeds: [errorEmbed] });
+ }
+
+ if (currentLowestBIN?.[parsedItem]) {
+ const currentLowestBINPrice = currentLowestBIN[parsedItem].toLocaleString();
+ priceEmbed.addField('Current Lowest BIN', currentLowestBINPrice);
+ }
+ if (averageLowestBIN?.[parsedItem]) {
+ const averageLowestBINPrice = averageLowestBIN[parsedItem].toLocaleString();
+ priceEmbed.addField('Average Lowest BIN', averageLowestBINPrice);
+ }
+ if (auctionAverages?.[parsedItem]?.price) {
+ const auctionAveragesPrice = auctionAverages[parsedItem].price.toLocaleString();
+ priceEmbed.addField('Average Auction Price', auctionAveragesPrice);
+ }
+ if (auctionAverages?.[parsedItem]?.count) {
+ const auctionAveragesCountPrice = auctionAverages[parsedItem].count.toLocaleString();
+ priceEmbed.addField('Average Auction Count', auctionAveragesCountPrice);
+ }
+ if (auctionAverages?.[parsedItem]?.sales) {
+ const auctionAveragesSalesPrice = auctionAverages[parsedItem].sales.toLocaleString();
+ priceEmbed.addField('Average Auction Sales', auctionAveragesSalesPrice);
+ }
+ if (auctionAverages?.[parsedItem]?.clean_price) {
+ const auctionAveragesCleanPrice = auctionAverages[parsedItem].clean_price.toLocaleString();
+ priceEmbed.addField('Average Auction Clean Price', auctionAveragesCleanPrice);
+ }
+ if (auctionAverages?.[parsedItem]?.clean_sales) {
+ const auctionAveragesCleanSales = auctionAverages[parsedItem].clean_sales.toLocaleString();
+ priceEmbed.addField('Average Auction Clean Sales', auctionAveragesCleanSales);
+ }
+ return await message.util.reply({ embeds: [priceEmbed] });
+
+ //Helper functions
+ function Bazaar(Information: string, digits: number, commas: boolean): string {
+ const price = bazaar?.products[parsedItem]?.quick_status?.[Information];
+ const a = Number(Number(price).toFixed(digits));
+ return commas ? a?.toLocaleString() : a?.toString();
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ async function get(url: string): Promise<any> {
+ const data = await got.get(url).catch((error) => {
+ this.client.console.warn('PriceCommand', `There was an problem fetching data from <<${url}>> with error:\n${error}`);
+ throw 'Error Fetching price data';
+ });
+ try {
+ const json = JSON.parse(data.body);
+ return json;
+ } catch (error) {
+ this.client.console.warn('PriceCommand', `There was an problem parsing data from <<${url}>> with error:\n${error}`);
+ throw 'json error';
+ }
+ }
+ }
+}
diff --git a/src/commands/utilities/viewraw.ts b/src/commands/utilities/viewraw.ts
index 7642b2a..3658bde 100644
--- a/src/commands/utilities/viewraw.ts
+++ b/src/commands/utilities/viewraw.ts
@@ -7,7 +7,7 @@ export default class ViewRawCommand extends BushCommand {
public constructor() {
super('viewraw', {
aliases: ['viewraw'],
- category: 'info',
+ category: 'utilities',
clientPermissions: ['EMBED_LINKS'],
description: {
usage: 'viewraw <message id> <channel>',
diff --git a/src/commands/utilities/whoHasRole.ts b/src/commands/utilities/whoHasRole.ts
new file mode 100644
index 0000000..1828c95
--- /dev/null
+++ b/src/commands/utilities/whoHasRole.ts
@@ -0,0 +1,53 @@
+import { BushCommand, BushMessage, BushSlashMessage } from '@lib';
+import { MessageEmbed, Role, Util } from 'discord.js';
+
+export default class WhoHasRoleCommand extends BushCommand {
+ public constructor() {
+ super('whohasrole', {
+ aliases: ['whohasrole'],
+ category: 'utilities',
+ description: {
+ content: 'Allows you to view what users have a certain role.',
+ usage: 'template <requiredArg> [optionalArg]',
+ examples: ['template 1 2']
+ },
+ args: [
+ {
+ id: 'role',
+ type: 'role',
+ prompt: {
+ start: 'What role would you like to find the users of?',
+ retry: '{error} Pick a valid role.',
+ optional: false
+ }
+ }
+ ],
+ slash: true,
+ slashOptions: [
+ {
+ name: 'role',
+ description: 'What role would you like to find the users of?',
+ type: 'ROLE',
+ required: true
+ }
+ ],
+ channel: 'guild',
+ clientPermissions: ['SEND_MESSAGES'],
+ userPermissions: ['SEND_MESSAGES']
+ });
+ }
+ public async exec(message: BushMessage | BushSlashMessage, args: { role: Role }): Promise<unknown> {
+ const roleMembers = args.role.members.map((member) => `${member.user} (${Util.escapeMarkdown(member.user.tag)})`);
+
+ const chunkedRoleMembers = this.client.util.chunk(roleMembers, 30);
+ const embedPages = chunkedRoleMembers.map(
+ (chunk) =>
+ new MessageEmbed({
+ title: `${args.role.name}'s Members`,
+ description: chunk.join('\n'),
+ color: this.client.util.colors.default
+ })
+ );
+ return await this.client.util.buttonPaginate(message, embedPages, null, true);
+ }
+}
diff --git a/src/config/example-options.ts b/src/config/example-options.ts
index fadd640..1b5d15a 100644
--- a/src/config/example-options.ts
+++ b/src/config/example-options.ts
@@ -16,11 +16,10 @@ export const owners: Snowflake[] = [
export const prefix = '-' as string;
export const dev = true as boolean;
export const devGuild = '1000000000000000' as Snowflake;
-export const channels: { log: Snowflake; error: Snowflake; dm: Snowflake; command: Snowflake } = {
+export const channels: { log: Snowflake; error: Snowflake; dm: Snowflake; } = {
log: '1000000000000000',
error: '1000000000000000',
dm: '1000000000000000',
- command: '1000000000000000'
};
// Database specific
diff --git a/src/inhibitors/blacklist/channelGlobalBlacklist.ts b/src/inhibitors/blacklist/channelGlobalBlacklist.ts
new file mode 100644
index 0000000..9dc5df9
--- /dev/null
+++ b/src/inhibitors/blacklist/channelGlobalBlacklist.ts
@@ -0,0 +1,25 @@
+import { BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class UserGlobalBlacklistInhibitor extends BushInhibitor {
+ public constructor() {
+ super('channelGlobalBlacklist', {
+ reason: 'channelGlobalBlacklist',
+ category: 'blacklist',
+ type: 'all'
+ });
+ }
+
+ public exec(message: BushMessage | BushSlashMessage): boolean {
+ if (!message.author) return false;
+ if (
+ this.client.isOwner(message.author) ||
+ this.client.isSuperUser(message.author) ||
+ this.client.user.id === message.author.id
+ )
+ return false;
+ if (this.client.cache.global.blacklistedChannels.includes(message.channel.id)) {
+ this.client.console.debug(`channelGlobalBlacklist blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/inhibitors/blacklist/channelGuildBlacklist.ts b/src/inhibitors/blacklist/channelGuildBlacklist.ts
new file mode 100644
index 0000000..cc72182
--- /dev/null
+++ b/src/inhibitors/blacklist/channelGuildBlacklist.ts
@@ -0,0 +1,25 @@
+import { BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class ChannelGuildBlacklistInhibitor extends BushInhibitor {
+ public constructor() {
+ super('channelGuildBlacklist', {
+ reason: 'channelGuildBlacklist',
+ category: 'blacklist',
+ type: 'all'
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage): Promise<boolean> {
+ if (!message.author || !message.guild) return false;
+ if (
+ this.client.isOwner(message.author) ||
+ this.client.isSuperUser(message.author) ||
+ this.client.user.id === message.author.id
+ )
+ return false;
+ if ((await message.guild.getSetting('blacklistedChannels'))?.includes(message.channel.id)) {
+ this.client.console.debug(`channelGuildBlacklist blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/inhibitors/blacklist/guildBlacklist.ts b/src/inhibitors/blacklist/guildBlacklist.ts
index d9a42da..7d08157 100644
--- a/src/inhibitors/blacklist/guildBlacklist.ts
+++ b/src/inhibitors/blacklist/guildBlacklist.ts
@@ -11,7 +11,13 @@ export default class GuildBlacklistInhibitor extends BushInhibitor {
public exec(message: BushMessage | BushSlashMessage): boolean {
if (!message.guild) return false;
- if (message.author && (this.client.isOwner(message.author) || this.client.isSuperUser(message.author))) return false;
+ if (
+ message.author &&
+ (this.client.isOwner(message.author) ||
+ this.client.isSuperUser(message.author) ||
+ this.client.user.id === message.author.id)
+ )
+ return false;
if (this.client.cache.global.blacklistedGuilds.includes(message.guild.id)) {
this.client.console.debug(`GuildBlacklistInhibitor blocked message.`);
return true;
diff --git a/src/inhibitors/blacklist/userBlacklist.ts b/src/inhibitors/blacklist/userBlacklist.ts
deleted file mode 100644
index f3cc642..0000000
--- a/src/inhibitors/blacklist/userBlacklist.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
-
-export default class UserBlacklistInhibitor extends BushInhibitor {
- public constructor() {
- super('userBlacklist', {
- reason: 'userBlacklist',
- category: 'blacklist',
- type: 'all'
- });
- }
-
- public exec(message: BushMessage | BushSlashMessage): boolean {
- if (!message.author) return false;
- if (this.client.isOwner(message.author) || this.client.isSuperUser(message.author)) return false;
- if (this.client.cache.global.blacklistedUsers.includes(message.author.id)) {
- this.client.console.debug(`UserBlacklistInhibitor blocked message.`);
- return true;
- }
- }
-}
diff --git a/src/inhibitors/blacklist/userGlobalBlacklist.ts b/src/inhibitors/blacklist/userGlobalBlacklist.ts
new file mode 100644
index 0000000..061bae9
--- /dev/null
+++ b/src/inhibitors/blacklist/userGlobalBlacklist.ts
@@ -0,0 +1,25 @@
+import { BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class UserGlobalBlacklistInhibitor extends BushInhibitor {
+ public constructor() {
+ super('userGlobalBlacklist', {
+ reason: 'userGlobalBlacklist',
+ category: 'blacklist',
+ type: 'all'
+ });
+ }
+
+ public exec(message: BushMessage | BushSlashMessage): boolean {
+ if (!message.author) return false;
+ if (
+ this.client.isOwner(message.author) ||
+ this.client.isSuperUser(message.author) ||
+ this.client.user.id === message.author.id
+ )
+ return false;
+ if (this.client.cache.global.blacklistedUsers.includes(message.author.id)) {
+ this.client.console.debug(`userGlobalBlacklist blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/inhibitors/blacklist/userGuildBlacklist.ts b/src/inhibitors/blacklist/userGuildBlacklist.ts
new file mode 100644
index 0000000..02a3762
--- /dev/null
+++ b/src/inhibitors/blacklist/userGuildBlacklist.ts
@@ -0,0 +1,25 @@
+import { BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class UserGuildBlacklistInhibitor extends BushInhibitor {
+ public constructor() {
+ super('userGuildBlacklist', {
+ reason: 'userGuildBlacklist',
+ category: 'blacklist',
+ type: 'all'
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage): Promise<boolean> {
+ if (!message.author || !message.guild) return false;
+ if (
+ this.client.isOwner(message.author) ||
+ this.client.isSuperUser(message.author) ||
+ this.client.user.id === message.author.id
+ )
+ return false;
+ if ((await message.guild.getSetting('blacklistedUsers'))?.includes(message.author.id)) {
+ this.client.console.debug(`userGuildBlacklist blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/inhibitors/commands/disabledCommand.ts b/src/inhibitors/commands/disabledCommand.ts
deleted file mode 100644
index fb31375..0000000
--- a/src/inhibitors/commands/disabledCommand.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { BushCommand, BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
-
-export default class DisabledCommandInhibitor extends BushInhibitor {
- public constructor() {
- super('disabledCommand', {
- reason: 'disabled',
- type: 'pre',
- priority: 3
- });
- }
-
- public async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise<boolean> {
- if (this.client.isOwner(message.author)) return false;
- if (this.client.cache.global.disabledCommands.includes(command?.id)) {
- this.client.console.debug(`DisabledCommandInhibitor blocked message.`);
- return true;
- }
- }
-}
diff --git a/src/inhibitors/commands/globalDisabledCommand.ts b/src/inhibitors/commands/globalDisabledCommand.ts
new file mode 100644
index 0000000..3ce39c2
--- /dev/null
+++ b/src/inhibitors/commands/globalDisabledCommand.ts
@@ -0,0 +1,19 @@
+import { BushCommand, BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class DisabledGuildCommandInhibitor extends BushInhibitor {
+ public constructor() {
+ super('disabledGlobalCommand', {
+ reason: 'disabledGlobal',
+ type: 'pre',
+ priority: 4
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise<boolean> {
+ if (message.author.isOwner()) return false;
+ if (this.client.cache.global.disabledCommands?.includes(command?.id)) {
+ this.client.console.debug(`disabledGlobalCommand blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/inhibitors/commands/guildDisabledCommand.ts b/src/inhibitors/commands/guildDisabledCommand.ts
new file mode 100644
index 0000000..a036ec5
--- /dev/null
+++ b/src/inhibitors/commands/guildDisabledCommand.ts
@@ -0,0 +1,21 @@
+import { BushCommand, BushInhibitor, BushMessage, BushSlashMessage } from '@lib';
+
+export default class DisabledGuildCommandInhibitor extends BushInhibitor {
+ public constructor() {
+ super('disabledGuildCommand', {
+ reason: 'disabledGuild',
+ type: 'pre',
+ priority: 3
+ });
+ }
+
+ public async exec(message: BushMessage | BushSlashMessage, command: BushCommand): Promise<boolean> {
+ if (!message.guild || !message.guild) return;
+ if (message.author.isOwner() || message.author.isSuperUser()) return false; // super users bypass guild disabled commands
+
+ if ((await message.guild.getSetting('disabledCommands'))?.includes(command?.id)) {
+ this.client.console.debug(`disabledGuildCommand blocked message.`);
+ return true;
+ }
+ }
+}
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index 6538ebf..6a08c54 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -45,6 +45,9 @@ import got from 'got';
import humanizeDuration from 'humanize-duration';
import { Op } from 'sequelize';
import { promisify } from 'util';
+import { BushNewsChannel } from '../discord.js/BushNewsChannel';
+import { BushTextChannel } from '../discord.js/BushTextChannel';
+import { BushUserResolvable } from './BushClient';
interface hastebinRes {
key: string;
@@ -322,7 +325,7 @@ export class BushClientUtil extends ClientUtil {
let curPage = 0;
if (typeof embeds !== 'object') throw 'embeds must be an object';
const msg: Message = await message.util.reply({
- content: text,
+ content: text || null,
embeds: [embeds[curPage]],
components: [getPaginationRow()]
});
@@ -527,16 +530,22 @@ export class BushClientUtil extends ClientUtil {
const environment = this.client.config.dev ? 'development' : 'production';
const row = await Global.findByPk(environment);
const oldValue: any[] = row[key];
+ const newValue = this.addOrRemoveFromArray(action, oldValue, value);
+ row[key] = newValue;
+ this.client.cache.global[key] = newValue;
+ return await row.save().catch((e) => this.client.logger.error('insertOrRemoveFromGlobal', e?.stack || e));
+ }
+
+ public addOrRemoveFromArray(action: 'add' | 'remove', array: any[], value: any): any[] {
let newValue: any[];
+ if (!array) return null;
if (action === 'add') {
- if (!oldValue.includes(action)) oldValue.push(value);
- newValue = oldValue;
+ if (!array.includes(action)) array.push(value);
+ newValue = array;
} else {
- newValue = oldValue.filter((ae) => ae !== value);
+ newValue = array.filter((ae) => ae !== value);
}
- row[key] = newValue;
- this.client.cache.global[key] = newValue;
- return await row.save().catch((e) => this.client.logger.error('insertOrRemoveFromGlobal', e?.stack || e));
+ return newValue;
}
/**
@@ -587,11 +596,13 @@ export class BushClientUtil extends ClientUtil {
moderator: BushGuildMember,
victim: BushGuildMember,
type: 'mute' | 'unmute' | 'warn' | 'kick' | 'ban' | 'unban' | 'add a punishment role to' | 'remove a punishment role from',
- checkModerator = true
+ checkModerator = true,
+ force = false
): true | string {
if (moderator.guild.id !== victim.guild.id) {
throw 'moderator and victim not in same guild';
}
+ if (force) return true;
const isOwner = moderator.guild.ownerId === moderator.id;
if (moderator.id === victim.id) {
return `${this.client.util.emojis.error} You cannot ${type} yourself.`;
@@ -708,7 +719,6 @@ export class BushClientUtil extends ClientUtil {
public async findExpiredEntries<K extends keyof punishmentModels>(type: K): Promise<punishmentModels[K][]> {
const dbModel = this.findPunishmentModel(type);
- console.log(dbModel);
//@ts-ignore: stfu idc
return await dbModel.findAll({
where: {
@@ -768,4 +778,6 @@ export class BushClientUtil extends ClientUtil {
return arrByte[1] + ', ' + arrByte[2] + ', ' + arrByte[3];
}
+
+ public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) {}
}
diff --git a/src/lib/extensions/discord-akairo/BushCommand.ts b/src/lib/extensions/discord-akairo/BushCommand.ts
index 90c68df..6cf981b 100644
--- a/src/lib/extensions/discord-akairo/BushCommand.ts
+++ b/src/lib/extensions/discord-akairo/BushCommand.ts
@@ -86,6 +86,7 @@ export interface BushCommandOptions extends CommandOptions {
};
args?: BushArgumentOptions[] | ArgumentGenerator;
category: string;
+ completelyHide?: boolean;
}
export class BushCommand extends Command {
@@ -104,12 +105,16 @@ export class BushCommand extends Command {
/** Whether the command is hidden from the help command. */
public hidden: boolean;
+ /** Completely hide this command from the help command. */
+ public completelyHide: boolean;
+
public constructor(id: string, options?: BushCommandOptions) {
super(id, options);
this.options = options;
this.hidden = options.hidden || false;
this.restrictedChannels = options.restrictedChannels;
this.restrictedGuilds = options.restrictedGuilds;
+ this.completelyHide = options.completelyHide;
}
public exec(message: BushMessage, args: any): any;
diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts
index 6eca44d..f695f8b 100644
--- a/src/lib/extensions/discord.js/BushGuild.ts
+++ b/src/lib/extensions/discord.js/BushGuild.ts
@@ -12,7 +12,7 @@ export class BushGuild extends Guild {
}
public async getSetting<K extends keyof GuildModel>(setting: K): Promise<GuildModel[K]> {
- return ((await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id })).get(setting);
+ return ((await GuildDB.findByPk(this.id)) ?? GuildDB.build({ id: this.id }))[setting];
}
public async setSetting<K extends keyof GuildModel>(setting: K, value: GuildDB[K]): Promise<GuildDB> {
diff --git a/src/lib/models/Global.ts b/src/lib/models/Global.ts
index ba77302..8664365 100644
--- a/src/lib/models/Global.ts
+++ b/src/lib/models/Global.ts
@@ -60,7 +60,8 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
set: function (val: Snowflake[]) {
return this.setDataValue('superUsers', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
},
disabledCommands: {
type: DataTypes.STRING,
@@ -70,7 +71,8 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
set: function (val: Snowflake[]) {
return this.setDataValue('disabledCommands', JSON.stringify(val) as unknown as string[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
},
blacklistedUsers: {
type: DataTypes.STRING,
@@ -80,7 +82,8 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
set: function (val: Snowflake[]) {
return this.setDataValue('blacklistedUsers', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
},
blacklistedGuilds: {
type: DataTypes.STRING,
@@ -90,7 +93,8 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
set: function (val: Snowflake[]) {
return this.setDataValue('blacklistedGuilds', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
},
blacklistedChannels: {
type: DataTypes.STRING,
@@ -100,7 +104,8 @@ export class Global extends BaseModel<GlobalModel, GlobalModelCreationAttributes
set: function (val: Snowflake[]) {
return this.setDataValue('blacklistedChannels', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
}
},
{ sequelize: sequelize }
diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts
index 43fcd64..30072b3 100644
--- a/src/lib/models/Guild.ts
+++ b/src/lib/models/Guild.ts
@@ -8,9 +8,12 @@ export interface GuildModel {
prefix: string;
autoPublishChannels: Snowflake[];
blacklistedChannels: Snowflake[];
+ blacklistedUsers: Snowflake[];
welcomeChannel: Snowflake;
muteRole: Snowflake;
punishmentEnding: string;
+ disabledCommands: string[];
+ lockdownChannels: Snowflake[];
}
export interface GuildModelCreationAttributes {
@@ -18,9 +21,12 @@ export interface GuildModelCreationAttributes {
prefix?: string;
autoPublishChannels?: Snowflake[];
blacklistedChannels?: Snowflake[];
+ blacklistedUsers?: Snowflake[];
welcomeChannel?: Snowflake;
muteRole?: Snowflake;
punishmentEnding?: string;
+ disabledCommands?: string[];
+ lockdownChannels?: Snowflake[];
}
export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> implements GuildModel {
@@ -28,9 +34,12 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
prefix!: string;
autoPublishChannels: Snowflake[];
blacklistedChannels: Snowflake[];
+ blacklistedUsers: Snowflake[];
welcomeChannel: Snowflake;
muteRole: Snowflake;
punishmentEnding: string;
+ disabledCommands: string[];
+ lockdownChannels: Snowflake[];
static initModel(sequelize: Sequelize, client: BushClient): void {
Guild.init(
@@ -52,7 +61,8 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
set: function (val: Snowflake[]) {
return this.setDataValue('autoPublishChannels', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
},
blacklistedChannels: {
type: DataTypes.STRING,
@@ -62,7 +72,19 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
set: function (val: Snowflake[]) {
return this.setDataValue('blacklistedChannels', JSON.stringify(val) as unknown as Snowflake[]);
},
- allowNull: true
+ allowNull: false,
+ defaultValue: '[]'
+ },
+ blacklistedUsers: {
+ type: DataTypes.STRING,
+ get: function () {
+ return JSON.parse(this.getDataValue('blacklistedUsers') as unknown as string);
+ },
+ set: function (val: Snowflake[]) {
+ return this.setDataValue('blacklistedUsers', JSON.stringify(val) as unknown as Snowflake[]);
+ },
+ allowNull: false,
+ defaultValue: '[]'
},
welcomeChannel: {
type: DataTypes.STRING,
@@ -75,6 +97,28 @@ export class Guild extends BaseModel<GuildModel, GuildModelCreationAttributes> i
punishmentEnding: {
type: DataTypes.TEXT,
allowNull: true
+ },
+ disabledCommands: {
+ type: DataTypes.STRING,
+ get: function () {
+ return JSON.parse(this.getDataValue('disabledCommands') as unknown as string);
+ },
+ set: function (val: string[]) {
+ return this.setDataValue('disabledCommands', JSON.stringify(val) as unknown as string[]);
+ },
+ allowNull: false,
+ defaultValue: '[]'
+ },
+ lockdownChannels: {
+ type: DataTypes.STRING,
+ get: function () {
+ return JSON.parse(this.getDataValue('lockdownChannels') as unknown as string);
+ },
+ set: function (val: Snowflake[]) {
+ return this.setDataValue('lockdownChannels', JSON.stringify(val) as unknown as Snowflake[]);
+ },
+ allowNull: false,
+ defaultValue: '[]'
}
},
{ sequelize: sequelize }
diff --git a/src/lib/models/Level.ts b/src/lib/models/Level.ts
index b834992..755b1c6 100644
--- a/src/lib/models/Level.ts
+++ b/src/lib/models/Level.ts
@@ -3,12 +3,14 @@ import { DataTypes, Sequelize } from 'sequelize';
import { BaseModel } from './BaseModel';
export interface LevelModel {
- id: Snowflake;
+ user: Snowflake;
+ guild: Snowflake;
xp: number;
}
export interface LevelModelCreationAttributes {
- id: Snowflake;
+ user: Snowflake;
+ guild: Snowflake;
xp?: number;
}
@@ -16,7 +18,11 @@ export class Level extends BaseModel<LevelModel, LevelModelCreationAttributes> {
/**
* The user's id.
*/
- public id: Snowflake;
+ public user: Snowflake;
+ /**
+ * The guild where the user is gaining xp.
+ */
+ public guild: Snowflake;
/**
* The user's xp.
*/
@@ -28,9 +34,12 @@ export class Level extends BaseModel<LevelModel, LevelModelCreationAttributes> {
static initModel(sequelize: Sequelize): void {
Level.init(
{
- id: {
+ user: {
+ type: DataTypes.STRING,
+ allowNull: false
+ },
+ guild: {
type: DataTypes.STRING,
- primaryKey: true,
allowNull: false
},
xp: {
diff --git a/src/lib/utils/BushConstants.ts b/src/lib/utils/BushConstants.ts
index 747c6d6..3ca2e8c 100644
--- a/src/lib/utils/BushConstants.ts
+++ b/src/lib/utils/BushConstants.ts
@@ -351,9 +351,11 @@ export class BushConstants {
DISABLED_GUILD: 'disabledGuild',
DISABLED_GLOBAL: 'disabledGlobal',
ROLE_BLACKLIST: 'roleBlacklist',
- USER_BLACKLIST: 'userBlacklist',
+ USER_GUILD_BLACKLIST: 'userGuildBlacklist',
+ USER_GLOBAL_BLACKLIST: 'userGlobalBlacklist',
RESTRICTED_GUILD: 'restrictedGuild',
- CHANNEL_BLACKLIST: 'channelBlacklist',
+ CHANNEL_GUILD_BLACKLIST: 'channelGuildBlacklist',
+ CHANNEL_GLOBAL_BLACKLIST: 'channelGlobalBlacklist',
RESTRICTED_CHANNEL: 'restrictedChannel'
};
diff --git a/src/listeners/client/interaction.ts b/src/listeners/client/interactionCreate.ts
index c848d15..1183004 100644
--- a/src/listeners/client/interaction.ts
+++ b/src/listeners/client/interactionCreate.ts
@@ -1,11 +1,11 @@
import { BushListener } from '@lib';
import { ButtonInteraction, CommandInteraction, Interaction, SelectMenuInteraction } from 'discord.js';
-export default class InteractionListener extends BushListener {
+export default class InteractionCreateListener extends BushListener {
public constructor() {
- super('interaction', {
+ super('interactionCreate', {
emitter: 'client',
- event: 'interaction',
+ event: 'interactionCreate',
category: 'client'
});
}
diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts
index 03050c2..24d46af 100644
--- a/src/listeners/commands/commandBlocked.ts
+++ b/src/listeners/commands/commandBlocked.ts
@@ -37,12 +37,10 @@ export default class CommandBlockedListener extends BushListener {
content: `${this.client.util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.`
});
}
- case reasons.CHANNEL_BLACKLIST: {
- return;
- }
- case reasons.USER_BLACKLIST: {
- return;
- }
+ case reasons.CHANNEL_GLOBAL_BLACKLIST:
+ case reasons.CHANNEL_GUILD_BLACKLIST:
+ case reasons.USER_GLOBAL_BLACKLIST:
+ case reasons.USER_GUILD_BLACKLIST:
case reasons.ROLE_BLACKLIST: {
return;
}
diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts
index 9d51ea8..696b59b 100644
--- a/src/listeners/commands/commandError.ts
+++ b/src/listeners/commands/commandError.ts
@@ -38,20 +38,20 @@ export default class CommandErrorListener extends BushListener {
errorUserEmbed.setDescription(
`Oh no! While running the command \`${command.id}\`, an error occurred. Please give the developers code \`${errorNo}\`.`
);
- await message.util.send({ embeds: [errorUserEmbed] }).catch((e) => {
+ (await message.util?.send({ embeds: [errorUserEmbed] }).catch((e) => {
const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name;
this.client.console.warn('CommandError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack || e);
- });
+ })) ?? this.client.console.error('CommandError', `Failed to send user error embed.` + error?.stack || error, false);
} else {
const errorDevEmbed = new MessageEmbed()
.setTitle('A Command Error Occurred')
.setColor(this.client.util.colors.error)
.setTimestamp()
.setDescription(await this.client.util.codeblock(`${error?.stack || error}`, 2048, 'js'));
- await message.util.send({ embeds: [errorDevEmbed] }).catch((e) => {
+ (await message.util?.send({ embeds: [errorDevEmbed] }).catch((e) => {
const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name;
this.client.console.warn('CommandError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack || e);
- });
+ })) ?? this.client.console.error('CommandError', `Failed to send owner error stack.` + error?.stack || error, false);
}
}
const channel = message.channel.type === 'DM' ? message.channel.recipient.tag : message.channel.name;
diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts
index bf98734..2443efb 100644
--- a/src/listeners/commands/slashBlocked.ts
+++ b/src/listeners/commands/slashBlocked.ts
@@ -38,12 +38,10 @@ export default class SlashBlockedListener extends BushListener {
content: `${this.client.util.emojis.error} The \`${command.aliases[0]}\` command is currently disabled in \`${message.guild.name}\`.`
});
}
- case reasons.CHANNEL_BLACKLIST: {
- return;
- }
- case reasons.USER_BLACKLIST: {
- return;
- }
+ case reasons.CHANNEL_GLOBAL_BLACKLIST:
+ case reasons.CHANNEL_GUILD_BLACKLIST:
+ case reasons.USER_GLOBAL_BLACKLIST:
+ case reasons.USER_GUILD_BLACKLIST:
case reasons.ROLE_BLACKLIST: {
return;
}
diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts
index 9bf5f6a..8abe788 100644
--- a/src/listeners/commands/slashCommandError.ts
+++ b/src/listeners/commands/slashCommandError.ts
@@ -37,18 +37,18 @@ export default class SlashCommandErrorListener extends BushListener {
errorUserEmbed.setDescription(
`Oh no! While running the command \`${command.id}\`, an error occurred. Please give the developers code \`${errorNo}\`.`
);
- await message.util.send({ embeds: [errorUserEmbed] }).catch((e) => {
+ (await message.util?.send({ embeds: [errorUserEmbed] }).catch((e) => {
this.client.console.warn('SlashError', `Failed to send user error embed in <<${channel}>>:\n` + e?.stack || e);
- });
+ })) ?? this.client.console.error('SlashError', `Failed to send user error embed.` + error?.stack || error, false);
} else {
const errorDevEmbed = new MessageEmbed()
.setTitle('A Slash Command Error Occurred')
.setColor(this.client.util.colors.error)
.setTimestamp()
.setDescription(await this.client.util.codeblock(`${error?.stack || error}`, 2048, 'js'));
- await message.util.send({ embeds: [errorDevEmbed] }).catch((e) => {
+ (await message.util?.send({ embeds: [errorDevEmbed] }).catch((e) => {
this.client.console.warn('SlashError', `Failed to send owner error stack in <<${channel}>>.` + e?.stack || e);
- });
+ })) ?? this.client.console.error('SlashError', `Failed to send user error embed.` + error?.stack || error, false);
}
}
const channel = (message.channel as GuildChannel)?.name || message.interaction.user.tag;
diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts
index a35766b..a50f580 100644
--- a/src/listeners/message/level.ts
+++ b/src/listeners/message/level.ts
@@ -12,27 +12,37 @@ export default class LevelListener extends BushListener {
}
async exec(message: Message): Promise<void> {
if (message.author.bot) return;
+ if (!message.author) return;
+ if (!message.guild) return;
if (message.util?.parsed?.command) return;
- if (this.levelCooldowns.has(message.author.id)) return;
- if (!this.client.config.dev && message.guild.id != '516977525906341928') return;
+ if (this.levelCooldowns.has(message.guild.id + message.author.id)) return;
if (this.blacklistedChannels.includes(message.channel.id)) return;
if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc
const [user] = await Level.findOrBuild({
where: {
- id: message.author.id
+ user: message.author.id,
+ guild: message.guild.id
},
defaults: {
- id: message.author.id
+ user: message.author.id,
+ guild: message.guild.id
}
});
const xpToGive = Level.genRandomizedXp();
- user.xp = user.xp + xpToGive;
+ user.increment('xp', { by: xpToGive });
const success = await user.save().catch((e) => {
+ console.debug(`User: ${message.author.id}`);
+ console.debug(`Guild: ${message.author.id}`);
+ console.debug(`Model: ${user}`);
this.client.logger.error('LevelMessageListener', e?.stack || e);
return false;
});
- if (success) this.client.logger.verbose(`LevelMessageListener`, `Gave <<${xpToGive}>> XP to <<${message.author.tag}>>.`);
- this.levelCooldowns.add(message.author.id);
+ if (success)
+ this.client.logger.verbose(
+ `LevelMessageListener`,
+ `Gave <<${xpToGive}>> XP to <<${message.author.tag}>> in <<${message.guild}>>.`
+ );
+ this.levelCooldowns.add(message.guild.id + message.author.id);
setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000);
}
}
diff --git a/yarn.lock b/yarn.lock
index 0634c8d..985728c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -329,6 +329,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/tinycolor2@npm:^1":
+ version: 1.4.3
+ resolution: "@types/tinycolor2@npm:1.4.3"
+ checksum: 61984b2825d4ee902016ef24777787bb2fb9e4999ccd4f7e5a709442c00cf90ba4afa510b9c78f18dcc83c03305d597d5fe3825a6aad38354f95c68af70ebc1b
+ languageName: node
+ linkType: hard
+
"@types/uuid@npm:^8.3.0":
version: 8.3.1
resolution: "@types/uuid@npm:8.3.1"
@@ -674,6 +681,7 @@ __metadata:
"@types/humanize-duration": ^3
"@types/module-alias": ^2
"@types/node": ^14.14.22
+ "@types/tinycolor2": ^1
"@types/uuid": ^8.3.0
"@typescript-eslint/eslint-plugin": ^4.14.1
"@typescript-eslint/parser": ^4.14.1
@@ -687,6 +695,7 @@ __metadata:
esbuild: ^0.12.11
eslint: ^7.29.0
eslint-config-prettier: ^8.3.0
+ fuse.js: ^6.4.6
got: ^11.8.2
humanize-duration: ^3.27.0
madge: ^5.0.1
@@ -698,6 +707,7 @@ __metadata:
rimraf: ^3.0.2
sequelize: ^6.5.0
source-map-support: ^0.5.19
+ tinycolor2: ^1.4.2
typescript: 4.2.4
uuid: ^8.3.2
dependenciesMeta:
@@ -1560,6 +1570,13 @@ discord.js@NotEnoughUpdates/discord.js:
languageName: node
linkType: hard
+"fuse.js@npm:^6.4.6":
+ version: 6.4.6
+ resolution: "fuse.js@npm:6.4.6"
+ checksum: 012dfacdc9a3c065d05d031b4eaaad7dd460f66dca100fcef182ed4e70a6b763b6753071702589d0b28def34b48ad077f01426d4ac1d75ef374564f6baa943fd
+ languageName: node
+ linkType: hard
+
"get-amd-module-type@npm:^3.0.0":
version: 3.0.0
resolution: "get-amd-module-type@npm:3.0.0"
@@ -3150,6 +3167,13 @@ resolve@^1.19.0:
languageName: node
linkType: hard
+"tinycolor2@npm:^1.4.2":
+ version: 1.4.2
+ resolution: "tinycolor2@npm:1.4.2"
+ checksum: 57ed262e08815a4ab0ed933edafdbc6555a17081781766149813b44a080ecbe58b3ee281e81c0e75b42e4d41679f138cfa98eabf043f829e0683c04adb12c031
+ languageName: node
+ linkType: hard
+
"to-fast-properties@npm:^2.0.0":
version: 2.0.0
resolution: "to-fast-properties@npm:2.0.0"