From 5e4736f97f8669c1a920265add5b2100fd48c577 Mon Sep 17 00:00:00 2001 From: "Navy.gif" Date: Wed, 27 Jul 2022 12:32:01 +0300 Subject: [PATCH] text based commands --- .../client/wrappers/MessageWrapper.js | 1 + .../components/commands/moderation/History.js | 8 +- .../settings/administration/IgnoreChannels.js | 14 +- .../settings/administration/PermissionType.js | 7 +- .../settings/administration/Protection.js | 14 +- .../settings/administration/Silent.js | 8 +- .../settings/logging/DmInfraction.js | 22 +- .../components/settings/logging/Errors.js | 16 +- .../components/settings/logging/Members.js | 24 +- .../components/settings/logging/Messages.js | 28 +- .../components/settings/logging/Moderation.js | 18 +- .../components/settings/logging/Nicknames.js | 12 +- .../components/settings/logging/Voice.js | 12 +- .../settings/moderation/AutoModeration.js | 28 +- .../settings/moderation/Grantable.js | 12 +- .../settings/moderation/InviteFilter.js | 22 +- .../settings/moderation/LinkFilter.js | 28 +- .../settings/moderation/MentionFilter.js | 32 +- .../settings/moderation/ModerationPoints.js | 32 +- .../components/settings/moderation/Mute.js | 35 +- .../components/settings/moderation/Staff.js | 2 +- .../settings/moderation/WordFilter.js | 26 +- .../settings/moderation/WordWatcher.js | 22 +- .../components/settings/utility/Autorole.js | 12 +- .../components/settings/utility/StickyRole.js | 12 +- .../components/settings/utility/Welcomer.js | 12 +- src/structure/interfaces/CommandOption.js | 316 ++++++++++++++++-- src/structure/interfaces/Setting.js | 67 ++-- src/structure/interfaces/commands/Command.js | 20 +- .../interfaces/commands/ModerationCommand.js | 16 +- .../interfaces/commands/SettingsCommand.js | 36 +- 31 files changed, 593 insertions(+), 321 deletions(-) diff --git a/src/structure/client/wrappers/MessageWrapper.js b/src/structure/client/wrappers/MessageWrapper.js index 9d71f1f..d32b2e5 100644 --- a/src/structure/client/wrappers/MessageWrapper.js +++ b/src/structure/client/wrappers/MessageWrapper.js @@ -90,6 +90,7 @@ class MessageWrapper { async editReply(options) { if (!this._reply) throw new Error('Message not replied to'); + if (!options.allowedMentions) options.allowedMentions = { repliedUser: false }; // Disables the mention in the inline reply return this._reply.edit(options); } diff --git a/src/structure/components/commands/moderation/History.js b/src/structure/components/commands/moderation/History.js index d433598..c58f222 100644 --- a/src/structure/components/commands/moderation/History.js +++ b/src/structure/components/commands/moderation/History.js @@ -50,8 +50,12 @@ class HistoryCommand extends SlashCommand { minimum: 1, flag: true }, { - name: ['user', 'moderator'], // - description: ['User whose infractions to query, overrides channel if both are given', 'Query by moderator'], + name: 'user', + description: 'User whose infractions to query, overrides channel if both are given', + type: 'USER' + }, { + name: 'moderator', + description: 'Query by moderator', type: 'USER', flag: true }, { diff --git a/src/structure/components/settings/administration/IgnoreChannels.js b/src/structure/components/settings/administration/IgnoreChannels.js index dbff60b..1cf7d58 100644 --- a/src/structure/components/settings/administration/IgnoreChannels.js +++ b/src/structure/components/settings/administration/IgnoreChannels.js @@ -1,5 +1,5 @@ const { Util } = require("../../../../utilities"); -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class IgnoreSetting extends Setting { @@ -19,7 +19,7 @@ class IgnoreSetting extends Setting { bypass: { ARRAY: 'GUILD_ROLE' } }, commandOptions: [ - new CommandOption({ + { name: 'list', description: 'List to act on', type: 'STRING', @@ -27,9 +27,9 @@ class IgnoreSetting extends Setting { { name: 'channels', value: 'channels' }, { name: 'bypass', value: 'bypass' } ], - dependsOn: ['method'] - }), - new CommandOption({ + dependsOn: ['method']//, valueAsAlias: true, flag: true + }, + { name: 'method', description: 'Method of modifying', type: 'STRING', @@ -39,8 +39,8 @@ class IgnoreSetting extends Setting { { name: 'set', value: 'set' }, { name: 'reset', value: 'reset' }, ], - dependsOn: ['list'] - }) + dependsOn: ['list']//, valueAsAlias: true, flag: true + } ] }); diff --git a/src/structure/components/settings/administration/PermissionType.js b/src/structure/components/settings/administration/PermissionType.js index 2ace0d2..f8ae481 100644 --- a/src/structure/components/settings/administration/PermissionType.js +++ b/src/structure/components/settings/administration/PermissionType.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require('../../../interfaces/'); +const { Setting } = require('../../../interfaces/'); class PermissionType extends Setting { @@ -16,15 +16,16 @@ class PermissionType extends Setting { type: 'STRING' }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'type', + description: 'Where to read permissions from', choices: [ { name: 'discord', value: 'discord' }, { name: 'both', value: 'both' }, { name: 'grant', value: 'grant' } ] - }) + } ] }); diff --git a/src/structure/components/settings/administration/Protection.js b/src/structure/components/settings/administration/Protection.js index 4397b89..402e951 100644 --- a/src/structure/components/settings/administration/Protection.js +++ b/src/structure/components/settings/administration/Protection.js @@ -1,5 +1,5 @@ const { Util } = require("../../../../utilities"); -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class ProtectionSetting extends Setting { @@ -21,7 +21,7 @@ class ProtectionSetting extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'type', description: 'Select protection type', @@ -30,8 +30,8 @@ class ProtectionSetting extends Setting { { name: 'position', value: 'position' } ], dependsOn: [] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'roles', description: 'Method of modifying', @@ -42,12 +42,12 @@ class ProtectionSetting extends Setting { { name: 'reset', value: 'reset' }, ], dependsOn: [] - }), - new CommandOption({ + }, + { type: 'BOOLEAN', name: 'enabled', description: 'Whether setting is active or not' - }) + } ] }); diff --git a/src/structure/components/settings/administration/Silent.js b/src/structure/components/settings/administration/Silent.js index 889bdb3..869e4fa 100644 --- a/src/structure/components/settings/administration/Silent.js +++ b/src/structure/components/settings/administration/Silent.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class SilentSetting extends Setting { @@ -17,11 +17,11 @@ class SilentSetting extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ - type: 'BOOLEAN', + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle state' - }) + } ] }); diff --git a/src/structure/components/settings/logging/DmInfraction.js b/src/structure/components/settings/logging/DmInfraction.js index 114e96b..23f1970 100644 --- a/src/structure/components/settings/logging/DmInfraction.js +++ b/src/structure/components/settings/logging/DmInfraction.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const Infractions = [ 'NOTE', 'WARN', @@ -57,13 +57,13 @@ class DmInfraction extends Setting { } }, commandOptions: [ - new CommandOption({ + { name: 'message', description: 'Set the message for an infraction type', type: 'STRING', dependsOn: ['infraction'] - }), - new CommandOption({ + }, + { name: 'infraction', description: 'Choose the infraction for which to modify the message', type: 'STRING', @@ -71,8 +71,8 @@ class DmInfraction extends Setting { return { name: inf, value: inf }; }), dependsOn: ['message'] - }), - new CommandOption({ + }, + { name: 'infractions', description: 'Modify the list of infractions that are sent', type: 'STRING', @@ -82,15 +82,15 @@ class DmInfraction extends Setting { { name: 'set', value: 'set' }, { name: 'reset', value: 'reset' }, ] - }), - new CommandOption({ + }, + { name: 'enabled', description: 'Enable or disable the sending of infractions in DMs', - type: 'BOOLEAN' - }), + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, { name: 'anonymous', - type: 'BOOLEAN', + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, description: 'Whether who issued the infraction is shown in moderation logs' } ] diff --git a/src/structure/components/settings/logging/Errors.js b/src/structure/components/settings/logging/Errors.js index 9952286..9d70d04 100644 --- a/src/structure/components/settings/logging/Errors.js +++ b/src/structure/components/settings/logging/Errors.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class MessageLog extends Setting { @@ -22,16 +22,16 @@ class MessageLog extends Setting { types: { ARRAY: 'ERROR_TYPES' } // TODO: Error types }, commandOptions: [ - new CommandOption({ + { name: 'channel', description: 'Channel in which to output logs', type: 'TEXT_CHANNEL' - }), - new CommandOption({ + }, + { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN' - }) + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + } ] }); @@ -39,14 +39,12 @@ class MessageLog extends Setting { async execute(interaction, opts, setting) { - const { guild } = interaction; - if (opts.enabled?.value === false) setting.channel = null; if (opts.channel) { const channel = opts.channel.value; - const perms = channel.permissionsFor(guild.members.me); + const perms = channel.permissionsFor(this.client.user); const missingPerms = perms.missing(['ViewChannel', 'EmbedLinks', 'SendMessages']); if (missingPerms.length) return { error: true, diff --git a/src/structure/components/settings/logging/Members.js b/src/structure/components/settings/logging/Members.js index 99b73b9..ea05539 100644 --- a/src/structure/components/settings/logging/Members.js +++ b/src/structure/components/settings/logging/Members.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class MemberLog extends Setting { @@ -21,26 +21,26 @@ class MemberLog extends Setting { leave: 'STRING' }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Enable/disable member logs', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'channel', description: 'Select the log output channel', type: 'TEXT_CHANNEL' - }), - new CommandOption({ + }, + { name: 'join', description: 'Set the join message', - type: 'STRING' - }), - new CommandOption({ + type: 'STRING', flag: true + }, + { name: 'leave', description: 'Set the leave message', - type: 'STRING' - }) + type: 'STRING', flag: true + } ] }); diff --git a/src/structure/components/settings/logging/Messages.js b/src/structure/components/settings/logging/Messages.js index 12125c6..dbbb964 100644 --- a/src/structure/components/settings/logging/Messages.js +++ b/src/structure/components/settings/logging/Messages.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const { Util } = require('../../../../utilities'); class MessageLog extends Setting { @@ -26,22 +26,22 @@ class MessageLog extends Setting { attachments: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ + { name: 'channel', description: 'Channel in which to output logs', type: 'TEXT_CHANNEL' - }), - new CommandOption({ + }, + { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'attachments', description: 'Whether to log attachments. PREMIUM TIER 1', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'list', description: 'Select which list to modify', type: 'STRING', @@ -50,8 +50,8 @@ class MessageLog extends Setting { { name: 'ignore', value: 'ignore' }, ], dependsOn: ['method'] - }), - new CommandOption({ + }, + { name: 'method', description: 'Select which modification method to use', type: 'STRING', @@ -62,7 +62,7 @@ class MessageLog extends Setting { { name: 'reset', value: 'reset' }, ], dependsOn: ['list'] - }), + }, ] }); @@ -82,7 +82,7 @@ class MessageLog extends Setting { if (opts.channel) { const channel = opts.channel.value; - const perms = channel.permissionsFor(guild.members.me); + const perms = channel.permissionsFor(this.client.user); const missingPerms = perms.missing(['ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks']); if (missingPerms.length) return { error: true, diff --git a/src/structure/components/settings/logging/Moderation.js b/src/structure/components/settings/logging/Moderation.js index 87731d6..28ab163 100644 --- a/src/structure/components/settings/logging/Moderation.js +++ b/src/structure/components/settings/logging/Moderation.js @@ -1,5 +1,5 @@ const { Infractions } = require("../../../../constants/Constants"); -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); // [ // 'NOTE', @@ -44,17 +44,17 @@ class ModerationLog extends Setting { } }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Enable/disable member logs', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'channel', description: 'Logging channel', type: 'TEXT_CHANNEL' - }), - new CommandOption({ + }, + { name: 'infractions', description: 'Modify the list of infractions that are sent', type: 'STRING', @@ -64,10 +64,10 @@ class ModerationLog extends Setting { { name: 'set', value: 'set' }, { name: 'reset', value: 'reset' }, ] - }), + }, { name: 'anonymous', - type: 'BOOLEAN', + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, description: 'Whether who issued the infraction is shown in moderation logs' } ] diff --git a/src/structure/components/settings/logging/Nicknames.js b/src/structure/components/settings/logging/Nicknames.js index 6ff906f..779a2af 100644 --- a/src/structure/components/settings/logging/Nicknames.js +++ b/src/structure/components/settings/logging/Nicknames.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class Nicknames extends Setting { @@ -16,16 +16,16 @@ class Nicknames extends Setting { channel: 'GUILD_TEXT' }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'channel', type: 'TEXT_CHANNEL', description: 'Set the channel for nickname logging' - }) + } ] }); } diff --git a/src/structure/components/settings/logging/Voice.js b/src/structure/components/settings/logging/Voice.js index ebbf632..e665cb6 100644 --- a/src/structure/components/settings/logging/Voice.js +++ b/src/structure/components/settings/logging/Voice.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class Voice extends Setting { @@ -15,16 +15,16 @@ class Voice extends Setting { channel: 'GUILD_TEXT' }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'channel', type: 'TEXT_CHANNEL', description: 'Set the channel for voice join/leave logging' - }) + } ] }); } diff --git a/src/structure/components/settings/moderation/AutoModeration.js b/src/structure/components/settings/moderation/AutoModeration.js index dc93074..015af18 100644 --- a/src/structure/components/settings/moderation/AutoModeration.js +++ b/src/structure/components/settings/moderation/AutoModeration.js @@ -1,5 +1,5 @@ const { Util } = require("../../../../utilities"); -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const Infractions = [ 'WARN', 'MUTE', @@ -17,7 +17,7 @@ class Automod extends Setting { super(client, { name: 'automod', description: 'Define automatic infraction escalation', - display: 'Automatic Moderation', + display: 'Automatic Infraction Escalation', module: 'moderation', default: { enabled: false, @@ -36,17 +36,17 @@ class Automod extends Setting { } }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Toggle state', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'useprevious', description: 'Use the previously passed threshold if the point total lands between two thresholds', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'threshold', description: 'The threshold at which to issue an infraction', type: 'INTEGER', @@ -54,8 +54,8 @@ class Automod extends Setting { maximum: 100, dependsOn: ['infraction', 'length'], dependsOnMode: 'OR' - }), - new CommandOption({ + }, + { name: 'infraction', description: 'The type of infraction to issue', type: 'STRING', @@ -63,13 +63,13 @@ class Automod extends Setting { return { name: inf, value: inf }; }), dependsOn: ['threshold'] - }), - new CommandOption({ + }, + { name: 'length', description: 'The duration for a tempban or a mute', type: 'TIME', dependsOn: ['threshold'] - }) + } ] }); } diff --git a/src/structure/components/settings/moderation/Grantable.js b/src/structure/components/settings/moderation/Grantable.js index 20c31f6..be636cc 100644 --- a/src/structure/components/settings/moderation/Grantable.js +++ b/src/structure/components/settings/moderation/Grantable.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const { Util } = require("../../../../utilities"); class Grantable extends Setting { @@ -18,12 +18,12 @@ class Grantable extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ - type: 'BOOLEAN', + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle state' - }), - new CommandOption({ + }, + { name: 'roles', description: '', type: 'STRING', @@ -33,7 +33,7 @@ class Grantable extends Setting { { name: 'set', value: 'set' }, { name: 'reset', value: 'reset' }, ] - }) + } ] }); } diff --git a/src/structure/components/settings/moderation/InviteFilter.js b/src/structure/components/settings/moderation/InviteFilter.js index ac31e9a..c5a08bb 100644 --- a/src/structure/components/settings/moderation/InviteFilter.js +++ b/src/structure/components/settings/moderation/InviteFilter.js @@ -1,4 +1,4 @@ -const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const { FilterSetting } = require('../../../interfaces/'); const { Util } = require("../../../../utilities"); class InviteFilterSetting extends FilterSetting { @@ -36,7 +36,7 @@ class InviteFilterSetting extends FilterSetting { actions: { ARRAY: 'ACTION' } }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'method', description: 'Select which modification method to use', @@ -49,8 +49,8 @@ class InviteFilterSetting extends FilterSetting { { name: 'list', value: 'list' } ], dependsOn: ['list'] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'list', description: 'Select which list to modify', @@ -61,17 +61,17 @@ class InviteFilterSetting extends FilterSetting { { name: 'actions', value: 'actions' }, ], dependsOn: ['method'] - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'silent', description: 'Toggle silent operation' - }) + } ] }); diff --git a/src/structure/components/settings/moderation/LinkFilter.js b/src/structure/components/settings/moderation/LinkFilter.js index 9a323c1..e7f5c8b 100644 --- a/src/structure/components/settings/moderation/LinkFilter.js +++ b/src/structure/components/settings/moderation/LinkFilter.js @@ -1,4 +1,4 @@ -const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const { FilterSetting } = require('../../../interfaces/'); const { Util } = require("../../../../utilities"); const { FilterPresets } = require('../../../../constants'); @@ -43,7 +43,7 @@ class LinkFilterSetting extends FilterSetting { actions: { ARRAY: 'ACTION' } }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'method', description: 'Select which modification method to use', @@ -56,8 +56,8 @@ class LinkFilterSetting extends FilterSetting { { name: 'list', value: 'list' } ], dependsOn: ['list'] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'list', description: 'Select which list to modify', @@ -71,22 +71,22 @@ class LinkFilterSetting extends FilterSetting { { name: 'presets', value: 'presets' } ], dependsOn: ['method'] - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'whitelist', description: 'Toggle whitelist mode' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'silent', description: 'Toggle silent operation' - }) + } ] }); diff --git a/src/structure/components/settings/moderation/MentionFilter.js b/src/structure/components/settings/moderation/MentionFilter.js index 452407b..0d32037 100644 --- a/src/structure/components/settings/moderation/MentionFilter.js +++ b/src/structure/components/settings/moderation/MentionFilter.js @@ -1,4 +1,4 @@ -const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const { FilterSetting } = require('../../../interfaces/'); const { Util } = require("../../../../utilities"); class MentionFilter extends FilterSetting { @@ -28,27 +28,27 @@ class MentionFilter extends FilterSetting { ignore: { ARRAY: 'GUILD_TEXT' } }, commandOptions: [ - new CommandOption({ + { name: 'enabled', description: 'Toggle state', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'silent', description: 'Whether the bot will respond in chat', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'unique', description: 'Mentions for the same user count as one', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'limit', description: 'How many mentions are allowed in a message', type: 'INTEGER' - }), - new CommandOption({ + }, + { type: 'STRING', name: 'method', description: 'Select which modification method to use', @@ -61,8 +61,8 @@ class MentionFilter extends FilterSetting { { name: 'list', value: 'list' } ], dependsOn: ['list'] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'list', description: 'Select which list to modify', @@ -72,7 +72,7 @@ class MentionFilter extends FilterSetting { { name: 'actions', value: 'actions' }, ], dependsOn: ['method'] - }), + }, ] }); } diff --git a/src/structure/components/settings/moderation/ModerationPoints.js b/src/structure/components/settings/moderation/ModerationPoints.js index ac58082..7566c9f 100644 --- a/src/structure/components/settings/moderation/ModerationPoints.js +++ b/src/structure/components/settings/moderation/ModerationPoints.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const { Util } = require("../../../../utilities"); const INFRACTIONS = ['WARN', 'MUTE', 'KICK', 'SOFTBAN', 'BAN', 'VCMUTE', 'VCKICK', 'VCBAN']; @@ -37,7 +37,7 @@ class ModerationPoints extends Setting { multiplier: false }, commandOptions: [ - new CommandOption({ + { name: 'points', description: 'Point value', type: 'INTEGER', @@ -45,14 +45,14 @@ class ModerationPoints extends Setting { dependsOnMode: 'OR', minimum: 0, maximum: 100 - }), - new CommandOption({ + }, + { name: 'expire', description: 'How long the points are counted for', type: 'TIME', dependsOn: ['type'] - }), - new CommandOption({ + }, + { name: 'infraction', description: 'Type of infraction', type: 'STRING', @@ -61,23 +61,23 @@ class ModerationPoints extends Setting { }), dependsOn: ['points', 'expire'], dependsOnMode: 'OR' - }), - new CommandOption({ + }, + { name: 'enabled', description: 'Toggle on or off', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'multiplier', description: 'Use points as a multiplier for the expiration', - type: 'BOOLEAN' - }), - new CommandOption({ + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true + }, + { name: 'associate', description: 'Associate a word within a reason to a point value', type: 'STRING', - dependsOn: ['points'] - }), + dependsOn: ['points'], flag: true + }, ] }); } diff --git a/src/structure/components/settings/moderation/Mute.js b/src/structure/components/settings/moderation/Mute.js index 4afe440..b8afbb0 100644 --- a/src/structure/components/settings/moderation/Mute.js +++ b/src/structure/components/settings/moderation/Mute.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require('../../../interfaces/'); +const { Setting } = require('../../../interfaces/'); const { inspect } = require('util'); const { Util } = require("../../../../utilities"); @@ -52,28 +52,28 @@ class MuteSetting extends Setting { permanent: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ - type: 'STRING', + { + type: 'STRING', flag: true, name: 'create', description: 'Create a mute role, mutually exclusive with role' - }), - new CommandOption({ + }, + { type: 'ROLE', name: 'role', description: 'Select the role to use for mutes, mutually exclusive with create' - }), - new CommandOption({ - type: 'TIME', + }, + { + type: 'TIME', flag: true, name: 'default', description: 'Set the default duration for mutes' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'permanent', description: 'Whether to allow permanent mutes or fall back to default mute duration' - }), - new CommandOption({ - type: 'INTEGER', + }, + { + type: 'INTEGER', flag: true, name: 'type', description: 'Select the type of mute behaviour', choices: [ { @@ -89,7 +89,7 @@ class MuteSetting extends Setting { name: 'Type 3 (Use Discord timeouts)', value: 3 } ] - }) + } ] }); @@ -187,7 +187,8 @@ class MuteSetting extends Setting { return role; }; - const hasPermission = guild.members.me.permissions.has('ManageRoles'); + const me = await guild.resolveMember(this.client.user); + const hasPermission = me.permissions.has('ManageRoles'); if (!hasPermission) return { index: 'SETTING_MUTE_ROLEMISSINGPERMISSION', error: true @@ -251,7 +252,7 @@ class MuteSetting extends Setting { for (const channel of channels.values()) { - if (!channel.permissionsFor(guild.members.me).has('ManageRoles')) { + if (!channel.permissionsFor(this.client.user).has('ManageRoles')) { issues.push({ type: 'permission', channel: channel.name }); continue; } diff --git a/src/structure/components/settings/moderation/Staff.js b/src/structure/components/settings/moderation/Staff.js index 6d1da2b..82633b0 100644 --- a/src/structure/components/settings/moderation/Staff.js +++ b/src/structure/components/settings/moderation/Staff.js @@ -23,7 +23,7 @@ class StaffSetting extends Setting { }, { name: 'enabled', description: 'Whether the staff command is in use', - type: 'BOOLEAN' + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true }] }); } diff --git a/src/structure/components/settings/moderation/WordFilter.js b/src/structure/components/settings/moderation/WordFilter.js index 78df6e5..ae7542f 100644 --- a/src/structure/components/settings/moderation/WordFilter.js +++ b/src/structure/components/settings/moderation/WordFilter.js @@ -1,5 +1,5 @@ /* eslint-disable camelcase */ -const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const { FilterSetting } = require('../../../interfaces/'); const { Util } = require("../../../../utilities"); const { FilterPresets } = require('../../../../constants'); @@ -44,7 +44,7 @@ class WordFilterSetting extends FilterSetting { actions: { ARRAY: 'ACTION' } }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'method', description: 'Select which modification method to use', @@ -57,8 +57,8 @@ class WordFilterSetting extends FilterSetting { { name: 'list', value: 'list' } ], dependsOn: ['list'] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'list', description: 'Select which list to modify', @@ -73,17 +73,17 @@ class WordFilterSetting extends FilterSetting { { name: 'actions', value: 'actions' }, ], dependsOn: ['method'] - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'silent', description: 'Toggle silent operation' - }) + } ] }); @@ -136,7 +136,7 @@ class WordFilterSetting extends FilterSetting { if (params.length && invalid.length) index += '_SOME'; } else if (list === 'presets') { for (const word of words) { - if (FilterPresets.regex[word]) params.push(FilterPresets.regex[word]); + if (FilterPresets.regex[word]) params.push(...FilterPresets.regex[word]); else invalid.push(word); } list = 'regex'; @@ -147,7 +147,7 @@ class WordFilterSetting extends FilterSetting { if (!params.length) { if (['roles', 'channels'].includes(list.value)) return { error: true, index: 'RESOLVE_FAIL', params: { type: list === 'bypass' ? 'roles' : 'channels' } }; - return { error: true, index }; + return { error: true, index: 'SETTING_WORDFILTER_INVALID' }; } const { modified } = this[method](setting[list], params.map((o) => o.id || o)); return { index, params: { updated: list, modified: params.filter((o) => modified.includes(o.id || o)).map((o) => o.name || o).join('__, __'), invalid: invalid.join('`, `') } }; diff --git a/src/structure/components/settings/moderation/WordWatcher.js b/src/structure/components/settings/moderation/WordWatcher.js index 514ae82..7d8a232 100644 --- a/src/structure/components/settings/moderation/WordWatcher.js +++ b/src/structure/components/settings/moderation/WordWatcher.js @@ -1,4 +1,4 @@ -const { FilterSetting, CommandOption } = require('../../../interfaces/'); +const { FilterSetting } = require('../../../interfaces/'); const { Util } = require("../../../../utilities"); class WordWatcher extends FilterSetting { @@ -10,6 +10,7 @@ class WordWatcher extends FilterSetting { description: 'Flag messages for potentially offensive content instead of deleting automatically', module: 'moderation', default: { + enabled: false, channel: null, words: [], regex: [], @@ -18,7 +19,7 @@ class WordWatcher extends FilterSetting { actions: [] }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'method', description: 'Select which modification method to use', @@ -31,8 +32,8 @@ class WordWatcher extends FilterSetting { { name: 'list', value: 'list' } ], dependsOn: ['list'] - }), - new CommandOption({ + }, + { type: 'STRING', name: 'list', description: 'Select which list to modify', @@ -44,12 +45,17 @@ class WordWatcher extends FilterSetting { { name: 'actions', value: 'actions' }, ], dependsOn: ['method'] - }), - new CommandOption({ + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, + name: 'enabled', + description: 'Toggle enable state' + }, + { name: 'channel', type: 'TEXT_CHANNEL', - description: 'Where to output flagged messages' - }) + description: 'Where to output flagged messages', + } ] }); } diff --git a/src/structure/components/settings/utility/Autorole.js b/src/structure/components/settings/utility/Autorole.js index 124dca7..cdfd252 100644 --- a/src/structure/components/settings/utility/Autorole.js +++ b/src/structure/components/settings/utility/Autorole.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const { Util } = require("../../../../utilities"); class Autorole extends Setting { @@ -18,7 +18,7 @@ class Autorole extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'roles', description: 'Modification method for roles', @@ -30,12 +30,12 @@ class Autorole extends Setting { { name: 'edit', value: 'edit' }, { name: 'list', value: 'list' } ] - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }) + } ] }); } diff --git a/src/structure/components/settings/utility/StickyRole.js b/src/structure/components/settings/utility/StickyRole.js index c930a9b..521b43a 100644 --- a/src/structure/components/settings/utility/StickyRole.js +++ b/src/structure/components/settings/utility/StickyRole.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); const { Util } = require("../../../../utilities"); class Autorole extends Setting { @@ -18,7 +18,7 @@ class Autorole extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'roles', description: 'Modification method for roles', @@ -30,12 +30,12 @@ class Autorole extends Setting { { name: 'edit', value: 'edit' }, { name: 'list', value: 'list' } ] - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }) + } ], premium: 1 }); diff --git a/src/structure/components/settings/utility/Welcomer.js b/src/structure/components/settings/utility/Welcomer.js index 820202f..b3c6065 100644 --- a/src/structure/components/settings/utility/Welcomer.js +++ b/src/structure/components/settings/utility/Welcomer.js @@ -1,4 +1,4 @@ -const { Setting, CommandOption } = require("../../../interfaces"); +const { Setting } = require("../../../interfaces"); class Autorole extends Setting { @@ -17,16 +17,16 @@ class Autorole extends Setting { enabled: 'BOOLEAN' }, commandOptions: [ - new CommandOption({ + { type: 'STRING', name: 'message', description: 'Set the welcome message' - }), - new CommandOption({ - type: 'BOOLEAN', + }, + { + type: 'BOOLEAN', flag: true, valueOptional: true, defaultValue: true, name: 'enabled', description: 'Toggle enable state' - }) + } ] }); } diff --git a/src/structure/interfaces/CommandOption.js b/src/structure/interfaces/CommandOption.js index 238b36d..1250467 100644 --- a/src/structure/interfaces/CommandOption.js +++ b/src/structure/interfaces/CommandOption.js @@ -1,5 +1,7 @@ /* eslint-disable camelcase */ +const { ChannelType } = require("discord.js"); + const Constants = { CommandOptionTypes: { SUB_COMMAND: 1, @@ -28,7 +30,8 @@ const Constants = { ROLE: 8, MENTIONABLE: 9, NUMBER: 10, - FLOAT: 10 + FLOAT: 10, + POINTS: 4 }, ChannelTypes: { TEXT_CHANNEL: 0, @@ -36,12 +39,17 @@ const Constants = { } }; +const PointsReg = /^([-+]?[0-9]+) ?(points|point|pts|pt|p)$/iu; + class CommandOption { constructor(options = {}) { + this._options = options; this.name = options.name; this.description = options.description || "A missing description, let a bot developer know."; + if(!options.client) throw new Error(`${this.name} is missing client`); + this.client = options.client; this.type = Object.keys(Constants.CommandOptionTypes).includes(options.type) ? options.type : 'STRING'; this.required = Boolean(options.required); @@ -52,8 +60,10 @@ class CommandOption { if (options.options) for (const opt of options.options) { // console.log(opt); - if (opt instanceof CommandOption) this.options.push(opt); - else if (opt.name instanceof Array) { + if (opt instanceof CommandOption) { + opt.client = this.client; + this.options.push(opt); + } else if (opt.name instanceof Array) { const { name: names, description, type, dependsOn, ...opts } = opt; for (const name of names) { // console.log(name); @@ -69,9 +79,12 @@ class CommandOption { if (dependsOn instanceof Array) { _dependsOn = dependsOn[index]; } - this.options.push(new CommandOption({ name, type: _type, description: desc, dependsOn: _dependsOn, ...opts })); + this.options.push(new CommandOption({ + client: this.client, name, type: _type, + description: desc, dependsOn: _dependsOn, ...opts + })); } - } else this.options.push(new CommandOption(opt)); + } else this.options.push(new CommandOption({ client: this.client, ...opt })); } // this.options = options.options || []; //Used for SUB_COMMAND/SUB_COMMAND_GROUP types. @@ -83,32 +96,47 @@ class CommandOption { this.minimum = typeof options.minimum === 'number' ? options.minimum : undefined; //Used for INTEGER/NUMBER/FLOAT types. this.maximum = typeof options.maximum === 'number' ? options.maximum : undefined; - this.flag = true; // used with message based command options + this.slashOption = options.slashOption || false; + this.flag = options.flag ?? false; // used with message based command options + this.valueOptional = options.valueOptional ?? false; + this.defaultValue = options.defaultValue ?? null; + this.valueAsAlias = options.choices?.length && (options.valueAsAlias ?? false); + // this.words = options.words ?? null; // Used when parsing strings if the command has multiple string types that aren't flags this.value = undefined; + // Used in cloned options when parsing final value + this.guild = options.guild || null; this._rawValue = options._rawValue ?? null; //Raw value input from Discord. -- use ?? where the value is potentially false, otherwise we end up with false -> null } - usage(guild) { - const name = `》 ${this.name.toUpperCase()} [${this.type}]`; + usage(guild, verbose = false) { + let name = `》 ${this.name.toUpperCase()} [${this.type}]`; + let flagProps = ['flag']; + if (this.valueOptional) flagProps.push('optional value'); + if (this.defaultValue !== null) flagProps.push(`default value: \`${this.defaultValue}\``); + flagProps = `(${flagProps.join(', ')})`; + if(this.flag) name += ` ${flagProps}`; let value = null; + const format = (...args) => guild ? guild.format(...args) : this.client.format(...args); + if (this.type === 'SUB_COMMAND_GROUP') { value = this.options.map((opt) => { - const usage = opt.usage(guild); + const usage = opt.usage(guild, true); return `__${usage.name.replace('》', '').trim()}__\n${usage.value}`; }).join('\n\n'); } else if (this.type === 'SUB_COMMAND') { - if (!this.options.length) value = guild.format('GENERAL_NO_ARGS'); - else value = this.options.map((opt) => opt.usage(guild).value).join('\n'); + if (!this.options.length) value = format('GENERAL_NO_ARGS'); + else value = this.options.map((opt) => opt.usage(guild, true).value).join('\n'); } else { - value = `**${this.name} [${this.type}]:** ${this.description}`; + value = `${verbose ? `**${this.name} [${this.type}]** ${this.flag ? flagProps : ''}\n` : ''}`; + value += `${this.description}`; if (this.choices.length) - value += `\n__${guild.format('GENERAL_CHOICES')}__: ${this.choices.map((choice) => choice.name).join(', ')}`; + value += `\n__${format('GENERAL_CHOICES')}__: ${this.choices.map((choice) => choice.name).join(', ')}`; if (this.dependsOn.length) - value += `\n${guild.format('GENERAL_DEPENDSON', { dependencies: this.dependsOn.join('`, `') })}`; + value += `\n${format('GENERAL_DEPENDSON', { dependencies: this.dependsOn.join('`, `') })}`; if (this.minimum !== undefined) value += `\nMIN: \`${this.minimum}\``; if (this.maximum !== undefined) { @@ -130,20 +158,268 @@ class CommandOption { * @return {CommandOption} * @memberof CommandOption */ - clone(value) { + clone(_rawValue, guild, slashOption = false) { return new CommandOption({ - name: this.name, type: this.type, - minimum: this.minimum, maximum: this.maximum, - dependsOn: this.dependsOn, dependsOnMode: this.dependsOnMode, - _rawValue: value, strict: this.strict + ...this._options, _rawValue, guild, slashOption }); } + async parse() { + if(!this._rawValue && !this.valueOptional) throw new Error(`Null _rawValue`); + // console.log('-------PARSE BEGIN---------'); + // console.log('1', this.name, this._rawValue, this.valueOptional); + const { removed, value, error } = await this.types[this.type](); + // console.log('2', removed, value, error); + // console.log('--------PARSE END----------'); + if(error) return { error }; + this.value = value; + return removed || []; + } + + format(index, params, opts) { + if (this.guild) return this.guild.format(index, params, opts); + return this.client.format(index, params, opts); + } + + get types() { + return { + POINTS: () => { + if(this.slashOption) return { value: this._rawValue }; + let value = null, + removed = null; + for (const str of this._rawValue) { + const num = parseInt(str); + if (isNaN(num)) continue; + if(PointsReg.test(str)) { + value = num; removed = [str]; + break; + } + const index = this._rawValue.indexOf(str); + const next = this._rawValue[index + 1]; + const tmp = str + next; + if (PointsReg.test(tmp)) { + value = num; removed= [str, next]; + break; + } + } + + if (this.minimum !== undefined && value < this.minimum) return { error: true }; + if (this.maximum !== undefined && value > this.maximum) return { error: true }; + return { value, removed }; + }, + ROLES: async () => { + const roles = [], + removed = []; + for (const str of this._rawValue) { + const role = await this.guild.resolveRole(str, this.strict); + if (role) { + roles.push(role); + removed.push(str); + } else break; + } + if (!roles.length) return { error: true }; + return { value: roles, removed }; + }, + MEMBERS: async () => { + const members = [], + removed = []; + for (const arg of this._rawValue) { + const member = await this.guild.resolveMember(arg, this.strict); + if (member) { + members.push(member); + removed.push(arg); + } else break; + } + if (!members.length) return { error: true, message: this.strict ? this.format('O_COMMANDHANDLER_TYPEMEMBER_STRICT') : null }; + return { value: members, removed }; + }, + USERS: async () => { + const users = [], + removed = []; + for (const arg of this._rawValue) { + const user = await this.client.resolveUser(arg, this.strict); + if (user) { + users.push(user); + removed.push(arg); + } else break; + } + if (!users.length) return { error: true, message: this.strict ? this.format('O_COMMANDHANDLER_TYPEUSERS_STRICT') : null }; + return { value: users, removed }; + }, + CHANNELS: async () => { + const channels = [], + removed = []; + for (const arg of this._rawValue) { + const channel = await this.guild.resolveChannel(arg, this.strict); + if (channel) { + channels.push(channel); + removed.push(arg); + } else break; + } + if (!channels.length) return { error: true }; + return { value: channels, removed }; + }, + TEXT_CHANNELS: async () => { + const channels = [], + removed = []; + for(const arg of this._rawValue) { + const channel = await this.guild.resolveChannel(arg, this.strict, (channel) => channel.type === ChannelType.GuildText); + if (channel) { + channels.push(channel); + removed.push(arg); + } else break; + } + if (!channels.length) return { error: true }; + return { value: channels, removed }; + }, + VOICE_CHANNELS: async () => { + const channels = [], + removed = []; + for (const arg of this._rawValue) { + const channel = await this.guild.resolveChannel(arg, this.strict, (channel) => channel.type === ChannelType.GuildVoice); + if (channel) { + channels.push(channel); + removed.push(arg); + } else break; + } + if (!channels.length) return { error: true }; + return { value: channels, removed }; + }, + TIME: () => { + const value = this.client.resolver.resolveTime(this._rawValue); + if (value === null) return { error: true }; + return { value, removed: [this._rawValue] }; + }, + COMPONENT: () => { + const [component] = this.client.resolver.components(this._rawValue, 'any'); + if (!component) return { error: true }; + return { value: component, removed: [this._rawValue] }; + }, + COMPONENTS: () => { + const strings = this._rawValue; + const components = [], + removed = []; + for (const str of strings) { + const [component] = this.client.resolver.components(str, 'any'); + if (component && !components.includes(component)) { + components.push(component); + removed.push(str); + } else break; + } + if (!components.length) return { error: true }; + return { value: components, removed }; + }, + COMMAND: () => { + const [command] = this.client.resolver.components(this._rawValue, 'command'); + if (!command) return { error: true }; + return { value: command, removed: [this._rawValue] }; + }, + COMMANDS: () => { + const strings = this._rawValue; + const commands = [], + removed = []; + for (const str of strings) { + const [command] = this.client.resolver.components(str, 'command'); + if (command && !commands.includes(command)) { + commands.push(command); + removed.push(str); + } else break; + } + if (!commands.length) return { error: true }; + return { value: commands, removed }; + }, + MODULE: () => { + const [module] = this.client.resolver.components(this._rawValue, 'module'); + if (!module) return { error: true }; + return { value: module, removed: [this._rawValue] }; + }, + STRING: () => { + if (this.slashOption) return { value: this._rawValue }; + if (this._aliased) return { value: this._rawValue, removed: [] }; + if (this.choices.length) { + const found = this.choices.find((c) => c.value.toLowerCase() === this._rawValue.toLowerCase()); + if (found) return { value: found.value, removed: [this._rawValue] }; + return { error: true }; + } + return { value: this._rawValue, removed: [this._rawValue] }; + }, + INTEGER: () => { + const integer = parseInt(this._rawValue); + if(isNaN(integer)) return { error: true }; + if (this.minimum !== undefined && integer < this.minimum) return { error: true }; + if (this.maximum !== undefined && integer > this.maximum) return { error: true }; + return { value: integer, removed: [this._rawValue] }; + }, + BOOLEAN: () => { + const boolean = this.client.resolver.resolveBoolean(this._rawValue); + if (boolean === null && this.valueOptional) return { value: this.defaultValue, removed: [] }; + else if(boolean === null) return { error: true }; + return { value: boolean, removed: [this._rawValue] }; + }, + MEMBER: async () => { + const member = await this.guild.resomveMember(this._rawValue, this.strict); + if (!member) return { error: true }; + return { value: member, removed: [this._rawValue] }; + }, + USER: async () => { + const user = await this.client.resolver.resolveUser(this._rawValue, this.strict); + if(!user) return { error: true }; + return { value: user, removed: [this._rawValue] }; + }, + TEXT_CHANNEL: async () => { + const channel = await this.guild.resolveChannel(this._rawValue); + if (!channel || channel.type !== ChannelType.GuildText) return { error: true }; + return { value: channel, removed: [this._rawValue] }; + }, + VOICE_CHANNEL: async () => { + const channel = await this.guild.resolveChannel(this._rawValue); + if (!channel || channel.type !== ChannelType.GuildVoice) return { error: true }; + return { value: channel, removed: [this._rawValue] }; + }, + CHANNEL: async () => { + const channel = await this.guild.resolveChannel(this._rawValue); + if(!channel) return { error: true }; + return { value: channel, removed: [this._rawValue] }; + }, + ROLE: async () => { + const role = await this.guild.resolveRole(this._rawValue); + if(!role) return { error: true }; + return { value: role, removed: [this._rawValue] }; + }, + MENTIONABLE: (mentionable) => { + return { value: mentionable }; + }, + NUMBER: () => { + const number = parseFloat(this._rawValue); + if(isNaN(number))return { error: true }; + if (this.minimum !== undefined && number < this.minimum) return { error: true }; + if (this.maximum !== undefined && number > this.maximum) return { error: true }; + return { value: number, removed: [this._rawValue] }; + }, + FLOAT: () => { + const float = parseFloat(this._rawValue); + if(isNaN(float)) return { error: true }; + if (this.minimum !== undefined && float < this.minimum) return { error: true }; + if (this.maximum !== undefined && float > this.maximum) return { error: true }; + return { value: parseFloat(float), removed: [this._rawValue] }; + }, + DATE: async () => { + const date = await this.client.resolver.resolveDate(this._rawValue); + if (!date) return { error: true }; + return { value: date, removed: [this._rawValue] }; + } + }; + } + + get plural() { + return this.type.endsWith('S'); + } + get raw() { return { name: this.name, type: this.type, - options: [] + options: this.options.map((opt) => opt.raw) }; } diff --git a/src/structure/interfaces/Setting.js b/src/structure/interfaces/Setting.js index 768568e..151b3a3 100644 --- a/src/structure/interfaces/Setting.js +++ b/src/structure/interfaces/Setting.js @@ -7,9 +7,8 @@ const Component = require("./Component.js"); // Imports to enable JSDocs typing // eslint-disable-next-line no-unused-vars const InteractionWrapper = require("../client/wrappers/InteractionWrapper.js"); -const CommandOption = require('./CommandOption.js'); // eslint-disable-next-line no-unused-vars -// const { DiscordClient } = require("../DiscordClient.js"); +const { DiscordClient } = require("../DiscordClient.js"); const TypeResolves = ['USER', 'GUILD']; @@ -70,29 +69,9 @@ class Setting extends Component { this.default = { [this.name]: options.default || {} }; this.definitions = options.definitions || {}; // Used for the API for field definitions - this.commandOptions = []; + this.commandOptions = options.commandOptions || []; this.commandType = options.commandType || 'SUB_COMMAND'; - if (options.commandOptions) - for (const opt of options.commandOptions) { - if (opt instanceof CommandOption) this.commandOptions.push(opt); - else if (opt.name instanceof Array) { - const { name: names, description, type, ...opts } = opt; - for (const name of names) { - // console.log(name); - const index = names.indexOf(name); - let desc = description, - _type = type; - if (description instanceof Array) desc = description[index] || 'Missing description'; - if (type instanceof Array) { - _type = type[index]; - if (!_type) throw new Error(`Missing type for option ${name} in command ${this.name}`); - } - this.commandOptions.push(new CommandOption({ name, type: _type, description: desc, ...opts })); - } - } else this.commandOptions.push(new CommandOption(opt)); - } - this.clientPermissions = options.clientPermissions || []; this.memberPermissions = options.memberPermissions || []; // Idk if we'll end up using this but it's here anyway @@ -125,22 +104,23 @@ class Setting extends Component { fields.push({ name: `》 ${guild.format(`GENERAL_OPTIONS`)}`, - value: options.map( - (opt) => { - let msg = `**${opt.name} [${opt.type}]:** ${opt.description}`; - if (opt.choices.length) - msg += `\n__${guild.format('GENERAL_CHOICES')}__: ${opt.choices.map((choice) => choice.name).join(', ')}`; - if (opt.dependsOn.length) - msg += `\n${guild.format('GENERAL_DEPENDSON', { dependencies: opt.dependsOn.join('`, `') })}`; - if (opt.minimum !== undefined) - msg += `\nMIN: \`${opt.minimum}\``; - if (opt.maximum !== undefined) { - const newline = opt.minimum !== undefined ? ', ' : '\n'; - msg += `${newline}MAX: \`${opt.maximum}\``; - } - return msg; - } - ).join('\n\n') + value: options.map((opt) => opt.usage(guild, true)).map((f) => f.value).join('\n\n') + // value: options.map( + // (opt) => { + // let msg = `**${opt.name} [${opt.type}]:** ${opt.description}`; + // if (opt.choices.length) + // msg += `\n__${guild.format('GENERAL_CHOICES')}__: ${opt.choices.map((choice) => choice.name).join(', ')}`; + // if (opt.dependsOn.length) + // msg += `\n${guild.format('GENERAL_DEPENDSON', { dependencies: opt.dependsOn.join('`, `') })}`; + // if (opt.minimum !== undefined) + // msg += `\nMIN: \`${opt.minimum}\``; + // if (opt.maximum !== undefined) { + // const newline = opt.minimum !== undefined ? ', ' : '\n'; + // msg += `${newline}MAX: \`${opt.maximum}\``; + // } + // return msg; + // } + // ).join('\n\n') }); } @@ -203,16 +183,16 @@ class Setting extends Component { if (!message.length && !index && !embed) throw new Error('Must declare either message, index or embeds'); const response = await invoker.promptMessage( index ? invoker.format(index, params) : message, - { time, editReply: true, embed } + { time, editReply: invoker.replied, embed } ); if (!response) return { error: true, message: invoker.format('ERR_TIMEOUT') }; const content = response.content.toLowerCase(); - if(invoker.channel.permissionsFor(invoker.guild.members.me).has('ManageMessages')) + if (invoker.channel.permissionsFor(this.client.user).has('ManageMessages')) await response.delete().catch(() => null); if (['cancel', 'abort', 'exit'].includes(content)) return { error: true, - message: invoker.format('ERR_CANCEL') + content: invoker.format('ERR_CANCEL') }; else if (!content.length) return { error: true, index: 'SETTING_NOCONTENT' }; @@ -241,7 +221,8 @@ class Setting extends Component { for (const param of params) { if (list.includes(param)) { - list.splice(list.indexOf(!caseSensitive ? param : param.toLowerCase()), 1); + const [removed] = list.splice(list.indexOf(!caseSensitive ? param : param.toLowerCase()), 1); + modified.push(removed); } else skipped.push(param); } diff --git a/src/structure/interfaces/commands/Command.js b/src/structure/interfaces/commands/Command.js index 681f54d..55d47f7 100644 --- a/src/structure/interfaces/commands/Command.js +++ b/src/structure/interfaces/commands/Command.js @@ -51,8 +51,10 @@ class Command extends Component { this.options = []; if (options.options) for (const opt of options.options) { - if (opt instanceof CommandOption) this.options.push(opt); - else if (opt.name instanceof Array) { + if (opt instanceof CommandOption) { + opt.client = client; + this.options.push(opt); + } else if (opt.name instanceof Array) { // Allows easy templating of subcommands that share arguments const { name: names, description, type, ...opts } = opt; for (const name of names) { @@ -64,9 +66,9 @@ class Command extends Component { _type = type[index]; if (!_type) throw new Error(`Missing type for option ${name} in command ${this.name}`); } - this.options.push(new CommandOption({ name, type: _type, description: desc, ...opts })); + this.options.push(new CommandOption({ name, type: _type, description: desc, ...opts, client })); } - } else this.options.push(new CommandOption(opt)); + } else this.options.push(new CommandOption({ ...opt, client })); } this.options.sort((a, b) => { @@ -107,7 +109,11 @@ class Command extends Component { const fields = []; const { guild, subcommand, subcommandGroup } = invoker; - const { permissions: { type } } = guild._settings; + + let type = null; + const format = (index) => guild ? guild.format(index) : this.client.format(index); + if (guild) ({ permissions: { type } } = guild._settings); + if (this.options.length) { if (verbose) fields.push(...this.options.map((opt) => opt.usage(guild))); @@ -126,7 +132,7 @@ class Command extends Component { else if (type === 'grant') required = [this.resolveable]; else required = [this.resolveable, ...this.memberPermissions]; fields.push({ - name: `》 ${guild.format('GENERAL_PERMISSIONS')}`, + name: `》 ${format('GENERAL_PERMISSIONS')}`, value: `\`${required.join('`, `')}\`` }); } @@ -135,7 +141,7 @@ class Command extends Component { author: { name: `${this.name} [module:${this.module.name}]` }, - description: guild.format(`COMMAND_${this.name.toUpperCase()}_HELP`), + description: format(`COMMAND_${this.name.toUpperCase()}_HELP`), fields }); diff --git a/src/structure/interfaces/commands/ModerationCommand.js b/src/structure/interfaces/commands/ModerationCommand.js index ecf339b..ade1577 100644 --- a/src/structure/interfaces/commands/ModerationCommand.js +++ b/src/structure/interfaces/commands/ModerationCommand.js @@ -4,6 +4,7 @@ class ModerationCommand extends SlashCommand { constructor(client, opts) { + const flag = true; let baseOptions = [ { name: 'users', @@ -13,28 +14,32 @@ class ModerationCommand extends SlashCommand { strict: true }, { name: 'points', - type: 'INTEGER', + type: 'POINTS', description: 'The amount of points to assign to the infraction', minimum: 0, maximum: 100 }, { name: 'expiration', type: 'TIME', - description: 'How long until the points expire' + description: 'How long until the points expire', + flag }, { name: 'prune', type: 'INTEGER', description: 'How many messages to prune', minimum: 2, - maximum: 100 + maximum: 100, + flag }, { name: 'force', type: 'BOOLEAN', - description: 'Whether to override automod' + description: 'Whether to override automod', + flag, valueOptional: true, defaultValue: true }, { name: 'silent', type: 'BOOLEAN', - description: 'Whether the user should receive the infraction' + description: 'Whether the user should receive the infraction', + flag, valueOptional: true, defaultValue: true }, { name: 'reason', type: 'STRING', @@ -42,7 +47,6 @@ class ModerationCommand extends SlashCommand { } ]; - // Probably temporary if(!opts.memberPermissions) throw new Error(`MISSING PERMS ${opts.name}`); if (opts.skipOptions) for (const opt of opts.skipOptions) { diff --git a/src/structure/interfaces/commands/SettingsCommand.js b/src/structure/interfaces/commands/SettingsCommand.js index 64b034d..dfa3866 100644 --- a/src/structure/interfaces/commands/SettingsCommand.js +++ b/src/structure/interfaces/commands/SettingsCommand.js @@ -10,26 +10,16 @@ class SettingsCommand extends SlashCommand { super(client, { ...options, guildOnly: true, - memberPermissions: ['ManageGuild'] - }); - - /* - { - name: 'settings', - description: "Configure the bot's behaviour in your server", - module: 'administration', + memberPermissions: ['ManageGuild'], options: [ - - ], - guildOnly: true - } - */ - - this.options.push(new CommandOption({ - type: 'SUB_COMMAND', - name: 'list', - description: 'List available settings' - })); + { + type: 'SUB_COMMAND', + name: 'list', + description: 'List available settings' + } + ] + }); + this.build(); } @@ -50,9 +40,12 @@ class SettingsCommand extends SlashCommand { name: setting.name, description: setting.description, type: setting.commandType, //'SUB_COMMAND', - options: setting.commandOptions + options: setting.commandOptions, + client: this.client }); this.options.push(subCommand); + // Overwrite the setting options with the built CommandOption structures + setting.commandOptions = subCommand.options; } // for (const module of modules) { @@ -89,7 +82,8 @@ class SettingsCommand extends SlashCommand { if (!setting) return invoker.reply('Something went wrong, could not find setting'); if (setting.clientPermissions.length) { - const missing = guild.members.me.permissions.missing(setting.clientPermissions); + const me = await guild.resolveMember(this.client.user); + const missing = me.permissions.missing(setting.clientPermissions); if (missing.length) return invoker.reply({ emoji: 'failure', index: 'SETTING_MISSING_CLIENTPERMISSIONS',