diff --git a/src/@types/Client.ts b/src/@types/Client.d.ts similarity index 98% rename from src/@types/Client.ts rename to src/@types/Client.d.ts index eca6837..b751244 100644 --- a/src/@types/Client.ts +++ b/src/@types/Client.d.ts @@ -20,16 +20,16 @@ import { ThreadChannel, APIEmbedField, VoiceState, - Invite, - Interaction + Invite } from 'discord.js'; -import { InvokerWrapper, MemberWrapper, UserWrapper } from '../client/components/wrappers/index.js'; -import GuildWrapper from '../client/components/wrappers/GuildWrapper.js'; -import DiscordClient from '../client/DiscordClient.js'; -import { CommandOption, Inhibitor } from '../client/interfaces/index.js'; +import { InvokerWrapper, MemberWrapper, UserWrapper } from '../client/components/wrappers/index.ts'; +import GuildWrapper from '../client/components/wrappers/GuildWrapper.ts'; +import DiscordClient from '../client/DiscordClient.ts'; +import { CommandOption, Inhibitor } from '../client/interfaces/index.ts'; import { MuteType } from './Settings.js'; import { ClientEvents } from './Events.js'; import { FilterResult } from './Utils.js'; +import { GuildSettingTypes } from './Guild.js'; export type ManagerEvalOptions = { context?: object, @@ -276,7 +276,7 @@ export type SettingOptions = { description?: string, display?: string, emoji?: SettingEmojiOption, - default?: BaseSetting + default: GuildSettingTypes definitions?: SettingApiDefinitions, commandOptions?: CommandOptionParams[], commandType?: CommandOptionType, diff --git a/src/@types/Commands/Moderation.ts b/src/@types/Commands/Moderation.d.ts similarity index 100% rename from src/@types/Commands/Moderation.ts rename to src/@types/Commands/Moderation.d.ts diff --git a/src/@types/Commands/Settings.d.ts b/src/@types/Commands/Settings.d.ts new file mode 100644 index 0000000..dfe1c4b --- /dev/null +++ b/src/@types/Commands/Settings.d.ts @@ -0,0 +1,16 @@ +import { SlashCommandOptions } from '../Client.js'; +import { ReplyOptions } from '../Wrappers.js'; + +export type SettingsCommandOptions = { + // +} & SlashCommandOptions + +export type SettingResult = { + error?: boolean, +} & ReplyOptions + +export type SettingModifyResult = { + list: T[], + modified: T[], + skipped?: T[], +} \ No newline at end of file diff --git a/src/@types/Commands/Settings.ts b/src/@types/Commands/Settings.ts deleted file mode 100644 index 286214a..0000000 --- a/src/@types/Commands/Settings.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { SlashCommandOptions } from '../Client.js'; - -export type SettingsCommandOptions = { - // -} & SlashCommandOptions \ No newline at end of file diff --git a/src/@types/Controller.ts b/src/@types/Controller.d.ts similarity index 96% rename from src/@types/Controller.ts rename to src/@types/Controller.d.ts index d0679dd..e7de0c2 100644 --- a/src/@types/Controller.ts +++ b/src/@types/Controller.d.ts @@ -1,5 +1,5 @@ import { LoggerMasterOptions, LoggerClientOptions } from '@navy.gif/logger'; -import { ClientOptions } from './Client.js'; +import { ClientOptions } from './Client.ts'; // export type ClientOptions = { // prefix: string, diff --git a/src/@types/Events.ts b/src/@types/Events.d.ts similarity index 89% rename from src/@types/Events.ts rename to src/@types/Events.d.ts index 659c51c..f1a4696 100644 --- a/src/@types/Events.ts +++ b/src/@types/Events.d.ts @@ -1,6 +1,6 @@ -import { Component } from '../client/interfaces/index.js'; +import { Component } from '../client/interfaces/index.ts'; import { ClientEvents as CE, InvalidRequestWarningData, RateLimitData, ResponseLike } from 'discord.js'; -import { ExtendedGuildBan, ExtendedMessage } from './Client.js'; +import { ExtendedGuildBan, ExtendedMessage } from './Client.ts'; import { APIRequest } from '@discordjs/rest'; import { AutomodErrorProps, LinkFilterWarnProps, LogErrorProps, MissingPermsProps, UtilityErrorProps, WordWatcherErrorProps } from './Moderation.js'; diff --git a/src/@types/Guild.ts b/src/@types/Guild.d.ts similarity index 81% rename from src/@types/Guild.ts rename to src/@types/Guild.d.ts index fdd2a7d..093cbf8 100644 --- a/src/@types/Guild.ts +++ b/src/@types/Guild.d.ts @@ -10,6 +10,7 @@ import { IndexingSettings, InviteFilterSettings, LinkfilterSettings, + LocaleSettings, MemberLogSettings, MentionFilterSettings, MessagesSettings, @@ -31,14 +32,45 @@ import { } from './Settings.js'; import { ObjectId } from 'mongodb'; +export type GuildSettingTypes = + | AutomodSettings + | AutoroleSettings + | CommandSettings + | DMInfractionSettings + | ErrorLogSettings + | GrantableSettings + | IgnoreSettings + | IndexingSettings + | InviteFilterSettings + | LinkfilterSettings + | MemberLogSettings + | MentionFilterSettings + | MessagesSettings + | ModerationPoints + | ModerationSettings + | MuteSettings + | NicknameLogSettings + | PermissionSettings + | ProtectionSettings + | SelfroleSettings + | SilenceSettings + | StaffSettings + | StickyRoleSettings + | TextCommands + | VoiceSettings + | WelcomerSettings + | WordFilter + | WordWatcherSettings + | LocaleSettings + export type PartialGuildSettings = Partial // { // [key: string]: unknown // } export type GuildSettings = { - [key: string]: object, - locale: {language: string}, + [key: string]: GuildSettingTypes, + locale: LocaleSettings, textcommands: TextCommands, wordfilter: WordFilter moderation: ModerationSettings, diff --git a/src/@types/Infractions.ts b/src/@types/Infractions.d.ts similarity index 94% rename from src/@types/Infractions.ts rename to src/@types/Infractions.d.ts index 88f56da..5413512 100644 --- a/src/@types/Infractions.ts +++ b/src/@types/Infractions.d.ts @@ -1,7 +1,7 @@ import { GuildTextBasedChannel } from 'discord.js'; -import { MemberWrapper } from '../client/components/wrappers/index.js'; -import { BaseInfractionData } from './Client.js'; -import { Infraction } from '../client/interfaces/index.js'; +import { MemberWrapper } from '../client/components/wrappers/index.ts'; +import { BaseInfractionData } from './Client.ts'; +import { Infraction } from '../client/interfaces/index.ts'; export type ResolveResult = { result?: void, diff --git a/src/@types/Moderation.ts b/src/@types/Moderation.d.ts similarity index 94% rename from src/@types/Moderation.ts rename to src/@types/Moderation.d.ts index 38e5d6d..e3914ec 100644 --- a/src/@types/Moderation.ts +++ b/src/@types/Moderation.d.ts @@ -1,7 +1,7 @@ import { GuildBasedChannel, Message, TextChannel } from 'discord.js'; -import { GuildWrapper, InvokerWrapper, MemberWrapper, UserWrapper } from '../client/components/wrappers/index.js'; -import { CommandOption, Infraction } from '../client/interfaces/index.js'; -import { FormatParams, InfractionType } from './Client.js'; +import { GuildWrapper, InvokerWrapper, MemberWrapper, UserWrapper } from '../client/components/wrappers/index.ts'; +import { CommandOption, Infraction } from '../client/interfaces/index.ts'; +import { FormatParams, InfractionType } from './Client.ts'; export type ModerationTarget = UserWrapper | MemberWrapper | TextChannel export type ModerationTargets = ModerationTarget[] diff --git a/src/@types/Settings.ts b/src/@types/Settings.d.ts similarity index 87% rename from src/@types/Settings.ts rename to src/@types/Settings.d.ts index a31c33c..e29914d 100644 --- a/src/@types/Settings.ts +++ b/src/@types/Settings.d.ts @@ -1,6 +1,6 @@ import { Snowflake } from 'discord.js'; -import { InfractionType } from './Client.js'; -import { AutomodAction } from './Moderation.js'; +import { InfractionType } from './Client.ts'; +import { AutomodAction } from './Moderation.ts'; export type UserSettings = { prefix?: string, @@ -30,9 +30,10 @@ export type WordFilter = { } & FilterSetting export type ModerationSettings = { - channel: Snowflake, + channel: Snowflake | null, infractions: InfractionType[], anonymous: boolean, + enabled: boolean } export type DMInfractionSettings = { @@ -65,8 +66,9 @@ export type AutomodSettings = { }, } & Setting +export type PermissionType = 'discord' | 'grant' | 'both'; export type PermissionSettings = { - type: 'discord' | 'grant' | 'both' + type: PermissionType } export type GrantableSettings = { @@ -92,12 +94,14 @@ export type SilenceSettings = { } & Setting export type IgnoreSettings = { + [key: string] channels: Snowflake[], bypass: Snowflake[] } export type CommandSettings = { - disabled: string[] + disabled: string[], + custom: object } export type IndexingSettings = { @@ -110,10 +114,12 @@ export type ErrorLogSettings = { } export type MessagesSettings = { + [key: string] channel: string | null, bypass: string[], ignore: string[], - attachments: boolean + attachments: boolean, + webhook: string | null, } & Setting export type StaffSettings = { @@ -126,6 +132,7 @@ export type VoiceSettings = { } & Setting export type SelfroleSettings = { + text: string | null, roles: string[], message: string | null, channel: string | null, @@ -175,4 +182,8 @@ export type InviteFilterSettings = { export type MentionFilterSettings = { unique: boolean, limit: number -} & FilterSetting \ No newline at end of file +} & FilterSetting + +export type LocaleSettings = { + language: string +} \ No newline at end of file diff --git a/src/@types/Shard.ts b/src/@types/Shard.d.ts similarity index 93% rename from src/@types/Shard.ts rename to src/@types/Shard.d.ts index b196457..7197d01 100644 --- a/src/@types/Shard.ts +++ b/src/@types/Shard.d.ts @@ -1,4 +1,4 @@ -import { ClientOptions } from './Client.js'; +import { ClientOptions } from './Client.ts'; export type ShardingOptions = { shardList?: 'auto' | number[], diff --git a/src/@types/Shared.ts b/src/@types/Shared.d.ts similarity index 97% rename from src/@types/Shared.ts rename to src/@types/Shared.d.ts index dc61136..6e83438 100644 --- a/src/@types/Shared.ts +++ b/src/@types/Shared.d.ts @@ -1,5 +1,5 @@ import { WriteOptions, LoggerClient } from '@navy.gif/logger'; -import { ClientOptions } from './Client.js'; +import { ClientOptions } from './Client.ts'; export type CommandOption = { [key: string]: unknown diff --git a/src/@types/Storage.ts b/src/@types/Storage.d.ts similarity index 100% rename from src/@types/Storage.ts rename to src/@types/Storage.d.ts diff --git a/src/@types/Utils.ts b/src/@types/Utils.d.ts similarity index 100% rename from src/@types/Utils.ts rename to src/@types/Utils.d.ts diff --git a/src/@types/Wrappers.ts b/src/@types/Wrappers.d.ts similarity index 95% rename from src/@types/Wrappers.ts rename to src/@types/Wrappers.d.ts index f546ef2..067bc9e 100644 --- a/src/@types/Wrappers.ts +++ b/src/@types/Wrappers.d.ts @@ -1,5 +1,5 @@ import { APIEmbed, Attachment, AttachmentBuilder, AttachmentPayload, ChannelSelectMenuInteraction, EmbedBuilder, MentionableSelectMenuInteraction, MessageComponent, MessageComponentType, RoleSelectMenuInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction } from 'discord.js'; -import { FormatOpts, FormatParams } from './Client.js'; +import { FormatOpts, FormatParams } from './Client.ts'; export type MessageOptions = { index?: string, diff --git a/src/client/components/Registry.ts b/src/client/components/Registry.ts index 195b72f..555125d 100644 --- a/src/client/components/Registry.ts +++ b/src/client/components/Registry.ts @@ -36,15 +36,14 @@ class Registry * @return {Array} The loaded components * @memberof Registry */ - async loadComponents (dir: string, classToHandle: typeof Component) + async loadComponents (dir: string, classToHandle: typeof Component) { - const directory = path.join(process.cwd(), 'src/structure', dir); // Finds directory of component folder relative to current working directory. const files = Util.readdirRecursive(directory); // Loops through all folders in the directory and returns the files. const loaded = []; - for (const filePath of files) + for (const filePath of files) { const func = await import(`file://${filePath}`); if (typeof func !== 'function') @@ -63,11 +62,8 @@ class Registry } loaded.push(await this.loadComponent(component, filePath)); - } - return loaded; - } /** @@ -79,9 +75,9 @@ class Registry * @return {Component} * @memberof Registry */ - async loadComponent (component: Component, directory?: string, silent = false) + async loadComponent (component: T, directory?: string, silent = false): Promise { - if (!(component instanceof Component)) + if (!(component instanceof Component)) throw new Error('Attempted to load an invalid component.'); if (this.#components.has(component.resolveable)) @@ -89,14 +85,14 @@ class Registry if (directory) component.directory = directory; - if (component.module && typeof component.module === 'string') + if (component.moduleName) { // Sets modules or "groups" for each component, specified by their properties. - let module = this.#components.get(`module:${component.module}`) as Module; - if (!module) - module = await this.loadComponent(new Module(this.#client, { name: component.module })) as Module; + let module = this.#components.get(`module:${component.moduleName}`) as Module; + if (!module) + module = await this.loadComponent(new Module(this.#client, { name: component.moduleName })); this.#components.set(module.resolveable, module); - component.module = module as Module; + component.module = module; module.components.set(component.resolveable, component); } @@ -112,14 +108,12 @@ class Registry async unloadComponent (component: Component) { - if (component.module instanceof Module) - { + if (component.module instanceof Module) component.module.components.delete(component.resolveable); - } this.#components.delete(component.resolveable); } - filter (f: (c: T) => boolean) + filter (f: (c: T) => boolean) { const coll = new Collection(); for (const component of this.#components.values()) diff --git a/src/client/components/Resolver.ts b/src/client/components/Resolver.ts index 5e82a4c..0faa42b 100644 --- a/src/client/components/Resolver.ts +++ b/src/client/components/Resolver.ts @@ -489,7 +489,7 @@ class Resolver } async resolveChannel ( - resolveable: ChannelResolveable, strict?: boolean, + resolveable?: ChannelResolveable | null, strict?: boolean, guild?: GuildWrapper | null, filter?: (channel: Channel) => boolean ) { diff --git a/src/client/components/commands/administration/Permissions.ts b/src/client/components/commands/administration/Permissions.ts index 4fd9e1b..c31daae 100644 --- a/src/client/components/commands/administration/Permissions.ts +++ b/src/client/components/commands/administration/Permissions.ts @@ -157,14 +157,14 @@ class PermissionsCommand extends SlashCommand for (const component of module.components.values()) if (component.type === 'command') { - if ((component.module as Module).id === 'administration') + if (component.module.id === 'administration') adminWarning = true; parsed.push(component.resolveable); } } else { - if ((result.module as Module).id === 'administration') + if (result.module.id === 'administration') adminWarning = true; parsed.push(result.resolveable); } diff --git a/src/client/components/commands/administration/Settings.ts b/src/client/components/commands/administration/Settings.ts index de1c36b..869e49b 100644 --- a/src/client/components/commands/administration/Settings.ts +++ b/src/client/components/commands/administration/Settings.ts @@ -1,7 +1,6 @@ import { APIEmbed, APIEmbedField } from 'discord.js'; import { CommandOptionType } from '../../../../@types/Client.js'; import DiscordClient from '../../../DiscordClient.js'; -import Module from '../../../interfaces/Module.js'; import SlashCommand from '../../../interfaces/commands/SlashCommand.js'; import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; import Util from '../../../../utilities/Util.js'; @@ -45,7 +44,7 @@ class SettingsCommand extends SlashCommand const { settings } = this.client.registry; const modules = settings.reduce((acc, setting) => { - const { name } = setting.module as Module; + const { name } = setting.module; if (!acc[name]) acc[name] = []; acc[name].push(setting.display); diff --git a/src/client/components/commands/information/Commands.ts b/src/client/components/commands/information/Commands.ts index 288c6c4..3e49b96 100644 --- a/src/client/components/commands/information/Commands.ts +++ b/src/client/components/commands/information/Commands.ts @@ -46,7 +46,7 @@ class Commands extends SlashCommand { if (command.restricted) continue; - const _module = (command.module as Module).name; + const _module = command.module.name; if (!modules[_module]) modules[_module] = []; const emoji = disabled?.includes(command.resolveable) ? Emojis.failure : Emojis.success; diff --git a/src/client/components/observers/CommandHandler.ts b/src/client/components/observers/CommandHandler.ts index 096951b..1e06529 100644 --- a/src/client/components/observers/CommandHandler.ts +++ b/src/client/components/observers/CommandHandler.ts @@ -848,7 +848,6 @@ class CommandHandler extends Observer _generateError (invoker: InvokerWrapper, info: ErrorParams) { - const messages: {[key: string]: () => object} = { command: () => { diff --git a/src/client/components/settings/administration/Commands.ts b/src/client/components/settings/administration/Commands.ts index c13032e..26ffe19 100644 --- a/src/client/components/settings/administration/Commands.ts +++ b/src/client/components/settings/administration/Commands.ts @@ -1,7 +1,12 @@ -class CommandsSetting extends Setting -{ +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { CommandSettings } from '../../../../@types/Settings.js'; +import DiscordClient from '../../../DiscordClient.js'; +import Setting from '../../../interfaces/Setting.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; - constructor (client) +class CommandsSetting extends Setting +{ + constructor (client: DiscordClient) { super(client, { name: 'commands', @@ -16,69 +21,66 @@ class CommandsSetting extends Setting disabled: { ARRAY: 'COMMAND' }, custom: { OBJECT: 'CUSTOM_COMMAND' } }, - commandType: 'SUB_COMMAND_GROUP', + commandType: CommandOptionType.SUB_COMMAND_GROUP, commandOptions: [{ name: [ 'enable', 'disable' ], description: [ 'Enable commands', 'Disable commands' ], - type: 'SUB_COMMAND', + type: CommandOptionType.SUB_COMMAND, options: [{ name: 'commands', description: 'The command to enable/disable', - type: 'COMMANDS', + type: CommandOptionType.COMMANDS, required: true }] }, { name: 'list', description: 'List disabled commands', - type: 'SUB_COMMAND' + type: CommandOptionType.SUB_COMMAND }] }); } - async execute (invoker, { commands }, setting) + async execute (invoker: InvokerWrapper, { commands }: CommandParams, setting: CommandSettings) { - const { subcommand, guild } = invoker; let warning = null; - if (subcommand.name === 'enable') + if (subcommand!.name === 'enable') { - if (!setting.disabled.length) + if (!setting.disabled.length) return { error: true, index: 'SETTING_COMMANDS_NONE' }; - for (const command of commands.value) + for (const command of commands!.asCommands) { const index = setting.disabled.indexOf(command.resolveable); - if (index >= 0) + if (index >= 0) setting.disabled.splice(index, 1); } } - else if (subcommand.name === 'disable') + else if (subcommand!.name === 'disable') { - for (const command of commands.value) + for (const command of commands!.asCommands) { - if (command.module.name === 'administration') + if (command.module.name === 'administration') { warning = guild.format('SETTING_COMMAND_WARNING'); continue; } - if (!setting.disabled.includes(command.resolveable)) + if (!setting.disabled.includes(command.resolveable)) setting.disabled.push(command.resolveable); } - if (warning) + if (warning) return { content: warning }; } - else if (subcommand.name === 'list') + else if (subcommand!.name === 'list') { let content = null; - if (setting.disabled.length) + if (setting.disabled.length) content = setting.disabled.join(', '); - else + else content = guild.format('SETTING_COMMANDS_NONE'); return { content }; } return { index: 'SETTING_SUCCESS_ALT' }; - } - } export default CommandsSetting; \ No newline at end of file diff --git a/src/client/components/settings/administration/IgnoreChannels.js b/src/client/components/settings/administration/IgnoreChannels.ts similarity index 54% rename from src/client/components/settings/administration/IgnoreChannels.js rename to src/client/components/settings/administration/IgnoreChannels.ts index bfa1665..d34014a 100644 --- a/src/client/components/settings/administration/IgnoreChannels.js +++ b/src/client/components/settings/administration/IgnoreChannels.ts @@ -1,12 +1,15 @@ -const { Util } = require('../../../../utilities'); -const { Setting } = require('../../../interfaces'); +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { IgnoreSettings } from '../../../../@types/Settings.js'; +import Util from '../../../../utilities/Util.js'; +import DiscordClient from '../../../DiscordClient.js'; +import Setting from '../../../interfaces/Setting.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; -class IgnoreSetting extends Setting +class IgnoreSetting extends Setting { - - constructor (client) + constructor (client: DiscordClient) { - super(client, { name: 'ignore', module: 'administration', @@ -24,7 +27,7 @@ class IgnoreSetting extends Setting { name: 'list', description: 'List to act on', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'channels', value: 'channels' }, { name: 'bypass', value: 'bypass' } @@ -34,7 +37,7 @@ class IgnoreSetting extends Setting { name: 'method', description: 'Method of modifying', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'add', value: 'add' }, { name: 'remove', value: 'remove' }, @@ -46,48 +49,42 @@ class IgnoreSetting extends Setting } ] }); - } - async execute (interaction, opts, setting) + async execute (invoker: InvokerWrapper, opts: CommandParams, setting: IgnoreSettings) { - const { list, method } = opts; - - if (method.value === 'list') - return this.list(setting[list.value], interaction, list.value); + if (method?.value === 'list') + return this.list(setting[list!.asString], invoker, list?.asString); - const response = await this._prompt(interaction, { - index: `SETTING_PROMPT_${method.value.toUpperCase()}`, - params: { list: list.value } + const promptResponse = await this._prompt(invoker, { + index: `SETTING_PROMPT_${method!.asString.toUpperCase()}`, + params: { list: list?.asString } }); - if (response.error) - return response; - if (method.value === 'reset' && !this.client.resolver.resolveBoolean(response)) - { + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; + if (method?.asString === 'reset' && !this.client.resolver.resolveBoolean(content)) return { index: 'SETTING_NO_CHANGE' }; - } - const params = Util.parseQuotes(response).map(([ param ]) => param); + const params = Util.parseQuotes(content).map(([ param ]) => param); let values = []; - const { guild } = interaction; - if (list.value === 'bypass') + const { guild } = invoker; + if (list?.asString === 'bypass') values = await guild.resolveRoles(params); - else + else values = await guild.resolveChannels(params); - const { modified } = this[method.value](setting[list.value], values.map((o) => o.id)); - if (!modified.length) + const { modified } = this[method!.asString](setting[list!.asString], values.map((o) => o.id)); + if (!modified.length) return { index: 'SETTING_NO_CHANGE' }; - return { index: `SETTING_SUCCESS_${method.value.toUpperCase()}`, params: { updated: list.value, modified: values.filter((o) => modified.includes(o.id)).map((o) => o.name).join('__, __') } }; - + return { index: `SETTING_SUCCESS_${method?.asString.toUpperCase()}`, params: { updated: list!.asString, modified: values.filter((o) => modified.includes(o.id)).map((o) => o.name).join('__, __') } }; } - fields (guild) + fields (guild: GuildWrapper) { - - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as IgnoreSettings; return [ { name: 'GENERAL_CHANNELS', diff --git a/src/client/components/settings/administration/Indexing.js b/src/client/components/settings/administration/Indexing.ts similarity index 61% rename from src/client/components/settings/administration/Indexing.js rename to src/client/components/settings/administration/Indexing.ts index c47c819..b8a4188 100644 --- a/src/client/components/settings/administration/Indexing.js +++ b/src/client/components/settings/administration/Indexing.ts @@ -1,12 +1,15 @@ -const { Setting } = require('../../../interfaces'); +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { IndexingSettings } from '../../../../@types/Settings.js'; +import DiscordClient from '../../../DiscordClient.js'; +import Setting from '../../../interfaces/Setting.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; // setting for guild indexing and description -class IndexSetting extends Setting +class IndexSetting extends Setting { - - constructor (client) + constructor (client: DiscordClient) { - super(client, { name: 'indexing', module: 'administration', @@ -20,7 +23,7 @@ class IndexSetting extends Setting { name: 'enabled', description: 'Toggle enable state', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -31,23 +34,20 @@ class IndexSetting extends Setting } ] }); - } - async execute (invoker, { enabled, description }, setting) + async execute (_invoker: InvokerWrapper, { enabled, description }: CommandParams, setting: IndexingSettings) { - if (enabled) - setting.enabled = enabled.value; + setting.enabled = enabled.asBool; if (description) - setting.description = description.value; + setting.description = description.asString; return { index: 'SETTING_SUCCESS_ALT' }; - } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as IndexingSettings; return [ { name: 'GENERAL_STATUS', @@ -62,7 +62,6 @@ class IndexSetting extends Setting } ]; } - } export default IndexSetting; \ No newline at end of file diff --git a/src/client/components/settings/administration/PermissionType.js b/src/client/components/settings/administration/PermissionType.ts similarity index 58% rename from src/client/components/settings/administration/PermissionType.js rename to src/client/components/settings/administration/PermissionType.ts index 7e5825e..37e3bbf 100644 --- a/src/client/components/settings/administration/PermissionType.js +++ b/src/client/components/settings/administration/PermissionType.ts @@ -1,11 +1,14 @@ -const { Setting } = require('../../../interfaces/'); +import { CommandParams } from '../../../../@types/Client.js'; +import { PermissionSettings, PermissionType } from '../../../../@types/Settings.js'; +import DiscordClient from '../../../DiscordClient.js'; +import Setting from '../../../interfaces/Setting.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; -class PermissionType extends Setting +class Permissions extends Setting { - - constructor (client) + constructor (client: DiscordClient) { - super(client, { name: 'permissions', description: 'Change between discord and bot based permissions', @@ -19,7 +22,6 @@ class PermissionType extends Setting }, commandOptions: [ { - type: 'STRING', name: 'type', description: 'Where to read permissions from', choices: [ @@ -30,21 +32,18 @@ class PermissionType extends Setting } ] }); - } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: PermissionSettings) { - const { type } = opts; - setting.type = type.value; + setting.type = type!.asString as PermissionType; return { index: 'SETTING_SUCCESS_ALT' }; - } - async fields (guild) + async fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as PermissionSettings; return [ { name: 'SETTING_PERMISSION_TYPE_FIELD', @@ -52,7 +51,6 @@ class PermissionType extends Setting } ]; } - } -export default PermissionType; \ No newline at end of file +export default Permissions; \ No newline at end of file diff --git a/src/client/components/settings/administration/Protection.js b/src/client/components/settings/administration/Protection.ts similarity index 93% rename from src/client/components/settings/administration/Protection.js rename to src/client/components/settings/administration/Protection.ts index f38ba85..d8b5f45 100644 --- a/src/client/components/settings/administration/Protection.js +++ b/src/client/components/settings/administration/Protection.ts @@ -1,12 +1,10 @@ -const { Util } = require('../../../../utilities'); -const { Setting } = require('../../../interfaces'); +import DiscordClient from '../../../DiscordClient.js'; +import Setting from '../../../interfaces/Setting.js'; -class ProtectionSetting extends Setting +class ProtectionSetting extends Setting { - - constructor (client) + constructor (client: DiscordClient) { - super(client, { name: 'protection', description: 'Select which roles are immune from moderation', @@ -24,7 +22,6 @@ class ProtectionSetting extends Setting }, commandOptions: [ { - type: 'STRING', name: 'type', description: 'Select protection type', choices: [ @@ -34,7 +31,6 @@ class ProtectionSetting extends Setting dependsOn: [] }, { - type: 'STRING', name: 'roles', description: 'Method of modifying', choices: [ @@ -47,7 +43,6 @@ class ProtectionSetting extends Setting dependsOn: [] }, { - type: 'BOOLEAN', name: 'enabled', description: 'Whether setting is active or not' } diff --git a/src/client/components/settings/administration/Silent.js b/src/client/components/settings/administration/Silent.js index 374f2d0..fd4a085 100644 --- a/src/client/components/settings/administration/Silent.js +++ b/src/client/components/settings/administration/Silent.js @@ -1,6 +1,3 @@ -const { Setting } = require('../../../interfaces'); - - class SilentSetting extends Setting { diff --git a/src/client/components/settings/administration/TextCommands.js b/src/client/components/settings/administration/TextCommands.js index cb27ae8..838bc0d 100644 --- a/src/client/components/settings/administration/TextCommands.js +++ b/src/client/components/settings/administration/TextCommands.js @@ -1,5 +1,3 @@ -const { Setting } = require('../../../interfaces'); - class TextCommands extends Setting { diff --git a/src/client/components/settings/logging/DmInfraction.js b/src/client/components/settings/logging/DmInfraction.ts similarity index 73% rename from src/client/components/settings/logging/DmInfraction.js rename to src/client/components/settings/logging/DmInfraction.ts index c0c76c5..6e638c3 100644 --- a/src/client/components/settings/logging/DmInfraction.js +++ b/src/client/components/settings/logging/DmInfraction.ts @@ -1,4 +1,10 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams, FormatParams, InfractionType } from '../../../../@types/Client.js'; +import { DMInfractionSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; + const Infractions = [ 'NOTE', 'WARN', @@ -16,12 +22,11 @@ const Infractions = [ 'REMOVEROLE', 'ADDROLE', 'NICKNAME' -]; +] satisfies InfractionType[]; class DmInfraction extends Setting { - - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'dminfraction', @@ -43,7 +48,7 @@ class DmInfraction extends Setting 'VCKICK', 'VCBAN', 'VCUNBAN' - ], + ] satisfies InfractionType[], messages: { default: 'You were **{infraction}** {from|on} the server `{server}`, your infraction details are below.' }, @@ -62,13 +67,13 @@ class DmInfraction extends Setting { name: 'message', description: 'Set the message for an infraction type', - type: 'STRING', + type: CommandOptionType.STRING, dependsOn: [ 'infraction' ] }, { name: 'infraction', description: 'Choose the infraction for which to modify the message', - type: 'STRING', + type: CommandOptionType.STRING, choices: Infractions.map((inf) => { return { name: inf, value: inf }; @@ -78,7 +83,7 @@ class DmInfraction extends Setting { name: 'infractions', description: 'Modify the list of infractions that are sent', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'add', value: 'add' }, { name: 'remove', value: 'remove' }, @@ -89,14 +94,14 @@ class DmInfraction extends Setting { name: 'enabled', description: 'Enable or disable the sending of infractions in DMs', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true }, { name: 'anonymous', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true, @@ -106,31 +111,32 @@ class DmInfraction extends Setting }); } - async execute (interaction, opts, setting) + async execute (invoker: InvokerWrapper, opts: CommandParams, setting: DMInfractionSettings) { const { enabled, infractions, infraction, message, anonymous } = opts, - langParams = {}; + langParams: FormatParams = {}; let index = 'SETTING_SUCCESS_ALT'; if (enabled) - setting.enabled = enabled.value; + setting.enabled = enabled.asBool; if (anonymous) - setting.anonymous = anonymous.value; + setting.anonymous = anonymous.asBool; if (infractions) { - const extra = `\n\n${interaction.format('SETTING_DMINFRACTION_VALID', { + const extra = `\n\n${invoker.format('SETTING_DMINFRACTION_VALID', { valid: Infractions.join(', ') })}`; - const reset = infractions.value === 'reset'; - const response = await this._prompt(interaction, { - message: `${interaction.format(`SETTING_PROMPT_${infractions.value.toUpperCase()}`, { + const reset = infractions.asString === 'reset'; + const promptResponse = await this._prompt(invoker, { + message: `${invoker.format(`SETTING_PROMPT_${infractions.asString.toUpperCase()}`, { list: 'infractions' })}${reset ? '' : extra}` }); - if (response.error) - return response; + if (promptResponse.error) + return promptResponse; + const response = promptResponse.content!; if (reset) { if (this.client.resolver.resolveBoolean(response)) @@ -144,10 +150,10 @@ class DmInfraction extends Setting const infs = response.split(' ') .map((inf) => this.client.resolver.resolveInfraction(inf)) .filter((inf) => inf !== null); - const { modified } = this[infractions.value](setting.infractions, infs); + const { modified } = this[infractions.asString](setting.infractions, infs); if (modified.length) { - index = `SETTING_SUCCESS_${infractions.value.toUpperCase()}`; + index = `SETTING_SUCCESS_${infractions.asString.toUpperCase()}`; langParams.modified = modified.join('__, __'); langParams.updated = 'infractions'; } @@ -156,17 +162,17 @@ class DmInfraction extends Setting if (message && infraction) { - setting.messages[infraction.value] = message.value; + setting.messages[infraction.asString] = message.asString; } return { index, params: langParams }; } - async fields (guild) + async fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as DMInfractionSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Errors.js b/src/client/components/settings/logging/Errors.ts similarity index 67% rename from src/client/components/settings/logging/Errors.js rename to src/client/components/settings/logging/Errors.ts index e4e57d7..7b3446f 100644 --- a/src/client/components/settings/logging/Errors.js +++ b/src/client/components/settings/logging/Errors.ts @@ -1,9 +1,14 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { ErrorLogSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; class MessageLog extends Setting { - constructor (client) + constructor (client: DiscordClient) { // Guild based logging of errors, typically configuration errors. @@ -27,12 +32,12 @@ class MessageLog extends Setting { name: 'channel', description: 'Channel in which to output logs', - type: 'TEXT_CHANNEL' + type: CommandOptionType.TEXT_CHANNEL }, { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -42,23 +47,23 @@ class MessageLog extends Setting } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: ErrorLogSettings) { - if (opts.enabled?.value === false) + if (opts.enabled?.asBool === false) setting.channel = null; if (opts.channel) { - const channel = opts.channel.value; - const perms = channel.permissionsFor(this.client.user); - const missingPerms = perms.missing([ 'ViewChannel', 'EmbedLinks', 'SendMessages' ]); - if (missingPerms.length) + const channel = opts.channel.asChannel; + const perms = channel.permissionsFor(this.client.user!); + const missingPerms = perms?.missing([ 'ViewChannel', 'EmbedLinks', 'SendMessages' ]); + if (!missingPerms || missingPerms.length) return { error: true, index: 'ERR_CHANNEL_PERMS', - params: { channel: channel.name, perms: missingPerms.join(', ') } + params: { channel: channel.name, perms: missingPerms?.join(', ') } }; setting.channel = channel.id; @@ -69,9 +74,9 @@ class MessageLog extends Setting } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as ErrorLogSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Members.js b/src/client/components/settings/logging/Members.ts similarity index 69% rename from src/client/components/settings/logging/Members.js rename to src/client/components/settings/logging/Members.ts index 7065dbe..8c617eb 100644 --- a/src/client/components/settings/logging/Members.js +++ b/src/client/components/settings/logging/Members.ts @@ -1,9 +1,14 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { MemberLogSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; class MemberLog extends Setting { - constructor (client) + constructor (client: DiscordClient) { super(client, { @@ -26,7 +31,7 @@ class MemberLog extends Setting { name: 'enabled', description: 'Enable/disable member logs', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -34,18 +39,18 @@ class MemberLog extends Setting { name: 'channel', description: 'Select the log output channel', - type: 'TEXT_CHANNEL' + type: CommandOptionType.TEXT_CHANNEL }, { name: 'join', description: 'Set the join message', - type: 'STRING', + type: CommandOptionType.STRING, flag: true }, { name: 'leave', description: 'Set the leave message', - type: 'STRING', + type: CommandOptionType.STRING, flag: true } ] @@ -53,26 +58,26 @@ class MemberLog extends Setting } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: MemberLogSettings) { if (opts.join) - setting.join = opts.join.value; + setting.join = opts.join.asString; if (opts.leave) - setting.leave = opts.leave.value; + setting.leave = opts.leave.asString; if (opts.channel) - setting.channel = opts.channel.value.id; + setting.channel = opts.channel.asChannel.id; if (opts.enabled) - setting.enabled = opts.enabled.value; + setting.enabled = opts.enabled.asBool; return { index: 'SETTING_SUCCESS_ALT' }; } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as MemberLogSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Messages.js b/src/client/components/settings/logging/Messages.ts similarity index 67% rename from src/client/components/settings/logging/Messages.js rename to src/client/components/settings/logging/Messages.ts index f7db56b..ccf444a 100644 --- a/src/client/components/settings/logging/Messages.js +++ b/src/client/components/settings/logging/Messages.ts @@ -1,12 +1,17 @@ -const { Setting } = require('../../../interfaces'); -const { Util } = require('../../../../utilities'); +import { CommandError, Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams, FormatParams } from '../../../../@types/Client.js'; +import { MessagesSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import Util from '../../../../utilities/Util.js'; +import { Role, TextChannel } from 'discord.js'; +import { SettingModifyResult } from '../../../../@types/Commands/Settings.js'; class MessageLog extends Setting { - - constructor (client) + constructor (client: DiscordClient) { - super(client, { name: 'messages', module: 'logging', @@ -31,12 +36,12 @@ class MessageLog extends Setting { name: 'channel', description: 'Channel in which to output logs', - type: 'TEXT_CHANNEL' + type: CommandOptionType.TEXT_CHANNEL }, { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -44,7 +49,7 @@ class MessageLog extends Setting { name: 'attachments', description: 'Whether to log attachments. PREMIUM TIER 1', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -52,7 +57,7 @@ class MessageLog extends Setting { name: 'list', description: 'Select which list to modify', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'bypass', value: 'bypass' }, { name: 'ignore', value: 'ignore' }, @@ -62,7 +67,7 @@ class MessageLog extends Setting { name: 'method', description: 'Select which modification method to use', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'add', value: 'add' }, { name: 'remove', value: 'remove' }, @@ -77,30 +82,30 @@ class MessageLog extends Setting } - async execute (interaction, opts, setting) + async execute (invoker: InvokerWrapper, opts: CommandParams, setting: MessagesSettings) { - - const method = opts.method?.value; - const list = opts.list?.value; - const { guild } = interaction; + const method = opts.method?.asString; + const list = opts.list?.asString; + const guild = invoker.guild!; let index = 'SETTING_SUCCESS_ALT'; - const langParams = {}; + const langParams: FormatParams = {}; if (opts.enabled) - setting.enabled = opts.enabled.value; + setting.enabled = opts.enabled.asBool; - if (opts.channel) + if (opts.channel) { - - const channel = opts.channel.value; - const perms = channel.permissionsFor(this.client.user); - const missingPerms = perms.missing([ 'ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks' ]); - if (missingPerms.length) + const channel = opts.channel.asChannel; + if (!(channel instanceof TextChannel)) + throw new CommandError(invoker, { index: 'ERR_INVALID_CHANNEL_TYPE' }); + const perms = channel.permissionsFor(this.client.user!); + const missingPerms = perms?.missing([ 'ViewChannel', 'EmbedLinks', 'SendMessages', 'ManageWebhooks' ]); + if (!missingPerms || missingPerms.length) return { error: true, index: 'ERR_CHANNEL_PERMS', - params: { channel: channel.name, perms: missingPerms.join(', ') } + params: { channel: channel.name, perms: missingPerms?.join(', ') ?? 'ALL' } }; let hook = await guild.getWebhook(this.name); @@ -108,9 +113,11 @@ class MessageLog extends Setting if (hook) hook = await this.client.fetchWebhook(hook.id).catch(() => null); - if (hook) - await hook.edit({ channel }); - else + if (hook) + { + await hook.edit({ channel }); + } + else { hook = await channel.createWebhook({ name: 'Galactic Bot message logs', @@ -119,59 +126,53 @@ class MessageLog extends Setting await guild.updateWebhook(this.name, hook); setting.webhook = hook.id; } - setting.channel = channel.id; - } - if (method && list) + if (method && list) { - - if (method === 'list') - return this.list(setting[list], interaction, list); + if (method === 'list') + return this.list(setting[list], invoker, list); const time = 120; - const content = await this._prompt(interaction, { + const promptResponse = await this._prompt(invoker, { message: guild.format( `SETTING_PROMPT_${method.toUpperCase()}`, { method, list } ) + '\n' + guild.format('TIMEOUT_IN', { time }), time }); - if (content.error) - return content; + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; if (method === 'reset' && !this.client.resolver.resolveBoolean(content)) { return { index: 'SETTING_NO_CHANGE' }; } const words = Util.parseQuotes(content).map(([ word ]) => word); - - let values = []; - if (list === 'bypass') + let values: TextChannel[] | Role[] = []; + if (list === 'bypass') values = await guild.resolveRoles(words); - else if (list === 'ignore') - values = await guild.resolveChannels(words); + else if (list === 'ignore') + values = await guild.resolveChannels(words); - if (!values.length) + if (!values.length) return { error: true, index: 'RESOLVE_FAIL', params: { type: list === 'bypass' ? 'roles' : 'channels' } }; - const { modified } = this[method](setting[list], values.map((o) => o.id)); - if (modified.length) + const { modified } = this[method](setting[list], values.map((o) => o.id)) as SettingModifyResult; + if (modified.length) { index = `SETTING_SUCCESS_${method.toUpperCase()}`; langParams.updated = list; - langParams.modified = modified.map((o) => o.name).join('__, __'); + langParams.modified = values.filter(o => modified.some(e => e === o.id)).map((o) => o.name).join('__, __'); } - } - return { index, params: langParams }; - } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as MessagesSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Moderation.js b/src/client/components/settings/logging/Moderation.ts similarity index 68% rename from src/client/components/settings/logging/Moderation.js rename to src/client/components/settings/logging/Moderation.ts index 4b9b727..260587e 100644 --- a/src/client/components/settings/logging/Moderation.js +++ b/src/client/components/settings/logging/Moderation.ts @@ -1,5 +1,10 @@ -const { Infractions } = require('../../../../constants/Constants'); -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams, FormatParams } from '../../../../@types/Client.js'; +import { ModerationSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import { Infractions } from '../../../../constants/Constants.js'; // [ // 'NOTE', @@ -26,8 +31,7 @@ const { Setting } = require('../../../interfaces'); class ModerationLog extends Setting { - - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'moderation', @@ -37,7 +41,8 @@ class ModerationLog extends Setting default: { channel: null, infractions: Infractions, - anonymous: false + anonymous: false, + enabled: false }, definitions: { channel: 'GUILD_TEXT', @@ -49,7 +54,7 @@ class ModerationLog extends Setting { name: 'enabled', description: 'Enable/disable member logs', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true @@ -57,12 +62,12 @@ class ModerationLog extends Setting { name: 'channel', description: 'Logging channel', - type: 'TEXT_CHANNEL' + type: CommandOptionType.TEXT_CHANNEL }, { name: 'infractions', description: 'Modify the list of infractions that are sent', - type: 'STRING', + type: CommandOptionType.STRING, choices: [ { name: 'add', value: 'add' }, { name: 'remove', value: 'remove' }, @@ -72,7 +77,7 @@ class ModerationLog extends Setting }, { name: 'anonymous', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true, @@ -82,33 +87,34 @@ class ModerationLog extends Setting }); } - async execute (interaction, opts, setting) + async execute (invoker: InvokerWrapper, opts: CommandParams, setting: ModerationSettings) { const { channel, infractions, anonymous, enabled } = opts, - langParams = {}; + langParams: FormatParams = {}; let index = 'SETTING_SUCCESS_ALT'; if (anonymous) - setting.anonymous = anonymous.value; + setting.anonymous = anonymous.asBool; if (channel) - setting.channel = channel.value.id; + setting.channel = channel.asChannel.id; if (enabled) - setting.enabled = enabled.value; + setting.enabled = enabled.asBool; if (infractions) { - const extra = `\n\n${interaction.format('SETTING_DMINFRACTION_VALID', { + const extra = `\n\n${invoker.format('SETTING_DMINFRACTION_VALID', { valid: Infractions.join(', ') })}`; - const reset = infractions.value === 'reset'; - const response = await this._prompt(interaction, { - message: `${interaction.format(`SETTING_PROMPT_${infractions.value.toUpperCase()}`, { + const reset = infractions.asString === 'reset'; + const promptResponse = await this._prompt(invoker, { + message: `${invoker.format(`SETTING_PROMPT_${infractions.asString.toUpperCase()}`, { list: 'infractions' })}${reset ? '' : extra}` }); - if (response.error) - return response; + if (promptResponse.error) + return promptResponse; + const response = promptResponse.content!; if (reset) { if (this.client.resolver.resolveBoolean(response)) @@ -122,10 +128,10 @@ class ModerationLog extends Setting const infs = response.split(' ') .map((inf) => this.client.resolver.resolveInfraction(inf)) .filter((inf) => inf !== null); - const { modified } = this[infractions.value](setting.infractions, infs); + const { modified } = this[infractions.asString](setting.infractions, infs); if (modified.length) { - index = `SETTING_SUCCESS_${infractions.value.toUpperCase()}`; + index = `SETTING_SUCCESS_${infractions.asString.toUpperCase()}`; langParams.modified = modified.join('__, __'); langParams.updated = 'infractions'; } @@ -135,9 +141,9 @@ class ModerationLog extends Setting } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as ModerationSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Nicknames.js b/src/client/components/settings/logging/Nicknames.ts similarity index 61% rename from src/client/components/settings/logging/Nicknames.js rename to src/client/components/settings/logging/Nicknames.ts index b6ee87a..01c543e 100644 --- a/src/client/components/settings/logging/Nicknames.js +++ b/src/client/components/settings/logging/Nicknames.ts @@ -1,9 +1,13 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { NicknameLogSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; -class Nicknames extends Setting +class Nicknames extends Setting { - - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'nicknames', @@ -21,35 +25,32 @@ class Nicknames extends Setting { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true }, { name: 'channel', - type: 'TEXT_CHANNEL', + type: CommandOptionType.TEXT_CHANNEL, description: 'Set the channel for nickname logging' } ] }); } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: NicknameLogSettings) { - if (opts.enabled) - setting.enabled = opts.enabled.value; + setting.enabled = opts.enabled.asBool; if (opts.channel) - setting.channel = opts.channel.value.id; - + setting.channel = opts.channel.asChannel.id; return { index: 'SETTING_SUCCESS_ALT' }; - } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as NicknameLogSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/logging/Voice.js b/src/client/components/settings/logging/Voice.ts similarity index 60% rename from src/client/components/settings/logging/Voice.js rename to src/client/components/settings/logging/Voice.ts index 3241dfe..7457cdf 100644 --- a/src/client/components/settings/logging/Voice.js +++ b/src/client/components/settings/logging/Voice.ts @@ -1,9 +1,13 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { VoiceSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; class Voice extends Setting { - - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'voice', @@ -11,7 +15,8 @@ class Voice extends Setting display: 'Voice Channel Logging', module: 'logging', default: { - channel: null + channel: null, + enabled: false }, definitions: { channel: 'GUILD_TEXT' @@ -20,35 +25,32 @@ class Voice extends Setting { name: 'enabled', description: 'Toggle logging on or off', - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true }, { name: 'channel', - type: 'TEXT_CHANNEL', + type: CommandOptionType.TEXT_CHANNEL, description: 'Set the channel for voice join/leave logging' } ] }); } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: VoiceSettings) { - if (opts.enabled) - setting.enabled = opts.enabled.value; + setting.enabled = opts.enabled.asBool; if (opts.channel) - setting.channel = opts.channel.value.id; - + setting.channel = opts.channel.asChannel.id; return { index: 'SETTING_SUCCESS_ALT' }; - } - fields (guild) + fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as VoiceSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/utility/Autorole.ts b/src/client/components/settings/utility/Autorole.ts index 04bffc8..f5c3d52 100644 --- a/src/client/components/settings/utility/Autorole.ts +++ b/src/client/components/settings/utility/Autorole.ts @@ -64,23 +64,22 @@ class Autorole extends Setting { const guild = invoker.guild!; const time = 120; - const content = await this._prompt(invoker, { + const promptResponse = await this._prompt(invoker, { message: guild.format( `SETTING_PROMPT_${method.toUpperCase()}`, { method, list: 'roles' } ) + '\n' + guild.format('TIMEOUT_IN', { time }), time }); - if (content.error) - return content; - if (method === 'reset' && !this.client.resolver.resolveBoolean(content)) - { + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; + if (method === 'reset' && !this.client.resolver.resolveBoolean(content)) return { index: 'SETTING_NO_CHANGE' }; - } const words = Util.parseQuotes(content).map(([ word ]) => word); const params = (await guild.resolveRoles(words)).filter((r) => !r.managed); - if (!params.length) + if (!params.length) return { error: true, index: 'RESOLVE_FAIL', params: { type: 'roles' } }; const { modified } = this[method](setting.roles, params.map((o) => o.id)); @@ -92,16 +91,13 @@ class Autorole extends Setting modified: params.filter((o) => modified.includes(o.id)).map((o) => o.name).join('__, __') } }; - } - return { index: 'SETTING_SUCCESS_ALT' }; - } async fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as AutoroleSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/utility/Selfrole.ts b/src/client/components/settings/utility/Selfrole.ts index 1950a47..e9951d4 100644 --- a/src/client/components/settings/utility/Selfrole.ts +++ b/src/client/components/settings/utility/Selfrole.ts @@ -1,10 +1,11 @@ -import { Setting } from '../../../interfaces/index.js'; +import { CommandError, Setting } from '../../../interfaces/index.js'; import DiscordClient from '../../../DiscordClient.js'; import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; import Util from '../../../../utilities/Util.js'; import { SelfroleSettings } from '../../../../@types/Settings.js'; -import GuildWrapper from '../../wrappers/GuildWrapper.js'; +import { ButtonComponentData, ButtonStyle, ComponentType, MessageCreateOptions, MessageEditOptions, StringSelectMenuComponentData, TextChannel } from 'discord.js'; +import Emojis from '../../../../constants/Emojis.js'; class SelfroleSetting extends Setting { @@ -48,20 +49,20 @@ class SelfroleSetting extends Setting if (!roles && !channel && !text) return { error: true, index: 'SETTING_MISSING_ARG' }; const guild = invoker.guild!; - const invokerChannel = invoker.channel!; if (roles?.asString === 'list') return this.list(setting.roles, invoker, 'roles'); if (roles) { - const response = await this._prompt(invoker, { + const promptResponse = await this._prompt(invoker, { index: `SETTING_PROMPT_${roles.asString.toUpperCase()}`, params: { list: 'roles' } }); - if (response.error) - return response; - if (roles.asString === 'reset' && !this.client.resolver.resolveBoolean(response)) + if (promptResponse.error) + return promptResponse; + const response = promptResponse.content!; + if (roles.value === 'reset' && !this.client.resolver.resolveBoolean(response)) { return { index: 'SETTING_NO_CHANGE' }; } @@ -70,15 +71,17 @@ class SelfroleSetting extends Setting const values = (await guild.resolveRoles(params)).filter((r) => !r.managed).map((r) => r.id); this[roles.asString](setting.roles, values); if (setting.roles.length >= 25 && (setting.channel || channel)) - await channel.send(guild.format('SETTING_SELFROLE_WARNING')); + await (invoker.channel!).send(guild.format('SETTING_SELFROLE_WARNING')); } // old channel for deleting old message if one exists - const oldChannel = await guild.resolveChannel(setting.channel); - const newChannel = channel?.asString || oldChannel; - if (channel) - setting.channel = channel.asString.id; // Set the new channel if one is given - if (text) + const oldChannel = await guild.resolveChannel(setting.channel); + const newChannel = channel?.asChannel || oldChannel; + if (!(newChannel instanceof TextChannel)) + throw new CommandError(invoker, { index: 'ERR_INVALID_CHANNEL_TYPE' }); + if (channel) + setting.channel = channel.asChannel.id; // Set the new channel if one is given + if (text) setting.text = text.asString; // If an old channel exists, and a message exists, and either all roles were removed or the channel changed, delete the old message @@ -94,34 +97,36 @@ class SelfroleSetting extends Setting if (setting.roles.length && setting.channel && setting.roles.length <= 25) { - const roles = await guild.resolveRoles(setting.roles); + const resolvedRoles = await guild.resolveRoles(setting.roles); + const selectMenu: StringSelectMenuComponentData = { + type: ComponentType.SelectMenu, + customId: 'selfrole-select', + maxValues: resolvedRoles.length, + options: resolvedRoles.map((r) => + { + return { label: r.name, value: r.id }; + }) + }; + const button: ButtonComponentData = { + type: ComponentType.Button, + customId: 'selfrole-clear', + label: 'Clear', + style: ButtonStyle.Primary, + emoji: Emojis.failure + }; const components = [{ type: ComponentType.ActionRow, - components: [{ - type: ComponentType.SelectMenu, - customId: 'selfrole-select', - maxValues: roles.length, - options: roles.map((r) => - { - return { label: r.name, value: r.id }; - }) - }] + components: [ selectMenu ] }, { type: ComponentType.ActionRow, - components: [{ - type: ComponentType.Button, - customId: 'selfrole-clear', - label: 'Clear', - style: ButtonStyle.Primary, - emoji: Emojis.failure - }] + components: [ button ] }]; - const payload = { + const payload: MessageCreateOptions & MessageEditOptions = { content: setting.text || guild.format('SETTING_SELFROLE_SELECT'), components }; - if (newChannel === oldChannel) + if (newChannel === oldChannel && oldChannel && setting.message) { const msg = await oldChannel.messages.fetch(setting.message).catch(() => null); if (msg) diff --git a/src/client/components/settings/utility/StickyRole.js b/src/client/components/settings/utility/StickyRole.ts similarity index 71% rename from src/client/components/settings/utility/StickyRole.js rename to src/client/components/settings/utility/StickyRole.ts index 7b899cb..ef809ae 100644 --- a/src/client/components/settings/utility/StickyRole.js +++ b/src/client/components/settings/utility/StickyRole.ts @@ -1,10 +1,15 @@ -const { Setting } = require('../../../interfaces'); -const { Util } = require('../../../../utilities'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import Util from '../../../../utilities/Util.js'; +import { StickyRoleSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; class Autorole extends Setting { - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'stickyrole', @@ -21,7 +26,7 @@ class Autorole extends Setting }, commandOptions: [ { - type: 'STRING', + type: CommandOptionType.STRING, name: 'roles', description: 'Modification method for roles', choices: [ @@ -34,7 +39,7 @@ class Autorole extends Setting ] }, { - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true, @@ -46,31 +51,32 @@ class Autorole extends Setting }); } - async execute (interaction, opts, setting) + async execute (invoker: InvokerWrapper, opts: CommandParams, setting: StickyRoleSettings) { - const enabled = opts.enabled?.value; - const method = opts.roles?.value; + const enabled = opts.enabled?.asBool; + const method = opts.roles?.asString; if (typeof enabled === 'boolean') setting.enabled = enabled; if (method === 'list') - return this.list(setting.roles, interaction, 'roles'); + return this.list(setting.roles, invoker, 'roles'); if (method) { - const { guild } = interaction; + const guild = invoker.guild!; const time = 120; - const content = await this._prompt(interaction, { + const promptResponse = await this._prompt(invoker, { message: guild.format( `SETTING_PROMPT_${method.toUpperCase()}`, { method, list: 'roles' } ) + '\n' + guild.format('TIMEOUT_IN', { time }), time }); - if (content.error) - return content; - if (method.value === 'reset' && !this.client.resolver.resolveBoolean(content)) + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; + if (method === 'reset' && !this.client.resolver.resolveBoolean(content)) { return { index: 'SETTING_NO_CHANGE' }; } @@ -96,9 +102,9 @@ class Autorole extends Setting } - async fields (guild) + async fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as StickyRoleSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/settings/utility/Welcomer.js b/src/client/components/settings/utility/Welcomer.ts similarity index 65% rename from src/client/components/settings/utility/Welcomer.js rename to src/client/components/settings/utility/Welcomer.ts index f9d5dd5..f98173a 100644 --- a/src/client/components/settings/utility/Welcomer.js +++ b/src/client/components/settings/utility/Welcomer.ts @@ -1,9 +1,14 @@ -const { Setting } = require('../../../interfaces'); +import { Setting } from '../../../interfaces/index.js'; +import DiscordClient from '../../../DiscordClient.js'; +import InvokerWrapper from '../../wrappers/InvokerWrapper.js'; +import { CommandOptionType, CommandParams } from '../../../../@types/Client.js'; +import { WelcomerSettings } from '../../../../@types/Settings.js'; +import GuildWrapper from '../../wrappers/GuildWrapper.js'; class Autorole extends Setting { - constructor (client) + constructor (client: DiscordClient) { super(client, { name: 'welcomer', @@ -20,12 +25,12 @@ class Autorole extends Setting }, commandOptions: [ { - type: 'STRING', + type: CommandOptionType.STRING, name: 'message', description: 'Set the welcome message' }, { - type: 'BOOLEAN', + type: CommandOptionType.BOOLEAN, flag: true, valueOptional: true, defaultValue: true, @@ -36,23 +41,21 @@ class Autorole extends Setting }); } - async execute (interaction, opts, setting) + async execute (_invoker: InvokerWrapper, opts: CommandParams, setting: WelcomerSettings) { - - const enabled = opts.enabled?.value; - const message = opts.message?.value; + const enabled = opts.enabled?.asBool; + const message = opts.message?.asString; if (typeof enabled === 'boolean') setting.enabled = enabled; if (typeof message === 'string') setting.message = message; return { index: 'SETTING_SUCCESS_ALT' }; - } - async fields (guild) + async fields (guild: GuildWrapper) { - const setting = guild._settings[this.name]; + const setting = guild._settings[this.name] as WelcomerSettings; return [ { name: 'GENERAL_STATUS', diff --git a/src/client/components/wrappers/GuildWrapper.ts b/src/client/components/wrappers/GuildWrapper.ts index 1abcfe8..30b09c4 100644 --- a/src/client/components/wrappers/GuildWrapper.ts +++ b/src/client/components/wrappers/GuildWrapper.ts @@ -542,7 +542,7 @@ class GuildWrapper return this.#client.resolver.resolveChannels(resolveables, strict, this, filter); } - resolveChannel (resolveable: ChannelResolveable, strict = false, filter?: (channel: Channel) => boolean) + resolveChannel (resolveable?: ChannelResolveable | null, strict = false, filter?: (channel: Channel) => boolean) { return this.#client.resolver.resolveChannel(resolveable, strict, this, filter); } diff --git a/src/client/interfaces/CommandOption.ts b/src/client/interfaces/CommandOption.ts index f55cb71..3ca3cb6 100644 --- a/src/client/interfaces/CommandOption.ts +++ b/src/client/interfaces/CommandOption.ts @@ -913,6 +913,11 @@ class CommandOption return this.value as GuildBasedChannel; } + get asCommands () + { + return this.value as Command[]; + } + get asCommand () { return this.value as Command; diff --git a/src/client/interfaces/Component.ts b/src/client/interfaces/Component.ts index 72e80fc..296e201 100644 --- a/src/client/interfaces/Component.ts +++ b/src/client/interfaces/Component.ts @@ -10,7 +10,8 @@ abstract class Component #client: DiscordClient; #id: string; #type: ComponentType; - #module: Module | string | null; + #module!: Module; + #moduleName: string | null; #directory: string | null; #guarded: boolean; #disabled: boolean; @@ -36,7 +37,8 @@ abstract class Component this.#id = options.id; // Name of the component this.#type = options.type; // Type of component (command, observer, etc.) - this.#module = options.module ?? null; // Group/module the component belongs to. + // this.#module = null; + this.#moduleName = options.module ?? null; // Group/module the component belongs to. this.#directory = null; // File directory to component. @@ -60,12 +62,17 @@ abstract class Component return this.#type; } - get module (): Module | string + get moduleName () { - return this.#module ?? 'MODULE'; + return this.#moduleName; } - set module (mod: Module) + get module () + { + return this.#module; + } + + set module (mod: Module) { this.#module = mod; } diff --git a/src/client/interfaces/FilterSetting.ts b/src/client/interfaces/FilterSetting.ts index 8f081eb..a28578b 100644 --- a/src/client/interfaces/FilterSetting.ts +++ b/src/client/interfaces/FilterSetting.ts @@ -12,7 +12,7 @@ import { FilterSettingHelperResponse, SettingAction, SettingActionType, SettingM const validInfractions = [ 'WARN', 'MUTE', 'KICK', 'BAN', 'SOFTBAN' ]; -abstract class FilterSetting extends Setting +abstract class FilterSetting extends Setting { protected async _action ( invoker: InvokerWrapper, method: SettingMethod, actions: SettingAction[], @@ -138,9 +138,10 @@ abstract class FilterSetting extends Setting if (settings!.modpoints?.enabled) { // Points - const content = await this._prompt(invoker, { index: 'SETTING_FILTER_ACTION_ADD_POINTS' }); - if (typeof content !== 'string') - return content; + const promptResponse = await this._prompt(invoker, { index: 'SETTING_FILTER_ACTION_ADD_POINTS' }); + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; this.client.logger.debug(`Points: ${content}`); if (![ 'no', 'n' ].includes(content)) @@ -372,10 +373,11 @@ abstract class FilterSetting extends Setting content: invoker.format('SETTING_FILTER_ACTION_EDIT_POINTS_DISABLED') }; - const content = await this._prompt(invoker, { index: 'SETTING_FILTER_ACTION_EDIT_POINTS', time: 60 }); - if (typeof content !== 'string') - return content; + const promptResponse = await this._prompt(invoker, { index: 'SETTING_FILTER_ACTION_EDIT_POINTS', time: 60 }); + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; const reg = /(\d{1,3})\s?(points?|pts?|p)?/iu; if (!reg.test(content)) return { @@ -401,15 +403,16 @@ abstract class FilterSetting extends Setting async _editType (invoker: InvokerWrapper, actions: SettingAction[], action: SettingAction) : Promise { - const content = await this._prompt(invoker, { + const promptResponse = await this._prompt(invoker, { index: 'SETTING_FILTER_ACTION_EDIT_TYPE', params: { valid: validInfractions.join('`, `') }, time: 120 }); - if (typeof content !== 'string') - return content; + if (promptResponse.error) + return promptResponse; + const content = promptResponse.content!; if (!validInfractions.includes(content.toUpperCase())) return { error: true, diff --git a/src/client/interfaces/Setting.ts b/src/client/interfaces/Setting.ts index bd9cd25..42bacd2 100644 --- a/src/client/interfaces/Setting.ts +++ b/src/client/interfaces/Setting.ts @@ -19,6 +19,8 @@ import { } from '../../@types/Client.js'; import CommandOption from './CommandOption.js'; import Module from './Module.js'; +import { GuildSettingTypes as GuildSettingType } from '../../@types/Guild.js'; +import { SettingModifyResult, SettingResult } from '../../@types/Commands/Settings.js'; // const EMOJIS = { // 'GUILD_TEXT': { @@ -43,11 +45,21 @@ import Module from './Module.js'; // } // }; +type HelperResult = { + modified: string[] +} + +// declare interface Setting extends Component { +// add: () => HelperResult; +// remove: () => HelperResult; +// set: () => HelperResult; +// } + /** * @class Setting * @extends {Component} */ -abstract class Setting extends Component +abstract class Setting extends Component { #name: string; #resolve: SettingTypeResolve; @@ -55,7 +67,7 @@ abstract class Setting extends Component #description: string; #display: string; #emoji: SettingEmojiOption; - #default: { [key: string]: any; }; + #default: { [key: string]: GuildSettingType; }; #definitions: SettingApiDefinitions; #commandOptions: CommandOptionParams[] | CommandOption[]; #commandType: CommandOptionType; @@ -64,7 +76,9 @@ abstract class Setting extends Component #memberPermissions: PermissionsString[]; #premium: number; // onCollect: any; - + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: (() => HelperResult) | any; /** * Creates an instance of Setting. @@ -97,7 +111,7 @@ abstract class Setting extends Component this.#display = options.display || options.name; this.#emoji = options.emoji || {}; - this.#default = { [this.#name]: options.default || {} }; + this.#default = { [this.#name]: options.default }; this.#definitions = options.definitions || {}; // Used for the API for field definitions this.#commandOptions = options.commandOptions || []; @@ -181,7 +195,7 @@ abstract class Setting extends Component * @param {object} hook * @memberof Setting */ - async execute (invoker: InvokerWrapper, params: CommandParams) // wrapper: InteractionWrapper, selectMenu, hook + async execute (_invoker: InvokerWrapper, _params: CommandParams, _setting: GuildSettingType): Promise // wrapper: InteractionWrapper, selectMenu, hook { throw new Error('Setting missing implementation'); // hook.collector = await this.collector(wrapper, selectMenu); @@ -189,9 +203,8 @@ abstract class Setting extends Component // , verbose = false -- unsure if this is a necessary argument anymore // eslint-disable-next-line @typescript-eslint/no-unused-vars - usageEmbed (guild: GuildWrapper, _verbose = false, subcommand: CommandOption | null = null) + usageEmbed (guild: GuildWrapper, _verbose: boolean | null = false, subcommand: CommandOption | null = null) { - const fields = []; if (this.#commandOptions.some(opt => !(opt instanceof CommandOption))) throw new Error('Command options not initialised'); @@ -227,7 +240,7 @@ abstract class Setting extends Component return new EmbedBuilder({ author: { - name: `${this.#display} [module:${(this.module as Module).name}]` + name: `${this.#display} [module:${this.module.name}]` }, description: guild.format(`SETTING_${this.#name.toUpperCase()}_HELP`), fields @@ -246,7 +259,7 @@ abstract class Setting extends Component * @return {Array} * @memberof Setting */ - fields (_guild: GuildWrapper): Promise + fields (_guild: GuildWrapper): Promise | APIEmbedField[] { return Promise.resolve([]); } @@ -286,9 +299,8 @@ abstract class Setting extends Component // Helper function for prompting for user response by editing the interaction response async _prompt (invoker: InvokerWrapper, { message = '', params = {}, embed, index, time = 120 }: { message?: string, params?: FormatParams, embed?: APIEmbed | null, index?: string, time?: number }) - : Promise + : Promise { - if (!message.length && !index && !embed) throw new Error('Must declare either message, index or embeds'); const response = await invoker.promptMessage( @@ -310,23 +322,22 @@ abstract class Setting extends Component else if (!content.length) return { error: true, index: 'SETTING_NOCONTENT' }; - return content; - + return { content }; } // Manipulator functions - add T}> (list: T[] = [], params: T[] = [], caseSensitive = false) + add T}> (list: T[] = [], params: T[] = [], caseSensitive = false): SettingModifyResult { const modified = [], skipped = []; - for (const param of params) + for (const param of params) { - if (list.includes(param)) + if (list.includes(param)) { skipped.push(param); } - else + else { list.push(caseSensitive ? param.toLowerCase() : param); modified.push(param); @@ -336,12 +347,12 @@ abstract class Setting extends Component return { list, modified, skipped }; } - remove T}> (list: T[] = [], params: T[] = [], caseSensitive = false) + remove T}> (list: T[] = [], params: T[] = [], caseSensitive = false): SettingModifyResult { const modified = [], skipped = []; - for (const param of params) + for (const param of params) { if (list.includes(param)) { @@ -355,7 +366,7 @@ abstract class Setting extends Component return { list, modified, skipped }; } - set (list: T[], params: T[] = []) + set (list: T[], params: T[] = []): SettingModifyResult { const modified = [ ...new Set(params) ]; list.splice(0, list.length); @@ -363,7 +374,7 @@ abstract class Setting extends Component return { list, modified }; } - reset (list: unknown[]) + reset (list: unknown[]): SettingModifyResult { const modified = list.splice(0, list.length); return { list, modified }; @@ -371,7 +382,6 @@ abstract class Setting extends Component async list (stuff: (object | string)[], invoker: InvokerWrapper, type = 'text') { - const items = stuff.map((i) => i.toString()); const strings = []; let string = ''; @@ -390,41 +400,37 @@ abstract class Setting extends Component channels: resolver.resolveChannel.bind(resolver) }; - for (let item of items) + for (let item of items) { - - if (resolvers[type]) + if (resolvers[type]) { const resolved = await resolvers[type](item, true, invoker.guild); if (resolved) item = resolved.toString(); } - if (string.length + item.length > 2000 && string.length) + if (string.length + item.length > 2000 && string.length) { strings.push(string); string = ''; } string += `${item}, `; - } - if (string.length) + if (string.length) strings.push(string.substring(0, string.length -2)); // await invoker.reply({ content: `Ok.`, emoji: 'success' }); - for (const str of strings) + for (const str of strings) { - if (invoker.replied) + if (invoker.replied) await invoker.channel?.send(str); - else + else await invoker.reply(str); } - return null; - } // Functions for message component based settings diff --git a/src/client/interfaces/commands/Command.ts b/src/client/interfaces/commands/Command.ts index a6014d5..fe276fb 100644 --- a/src/client/interfaces/commands/Command.ts +++ b/src/client/interfaces/commands/Command.ts @@ -287,7 +287,7 @@ abstract class Command extends Component return new EmbedBuilder({ author: { - name: `${this.name} [module:${(this.module as Module)?.name}]` + name: `${this.name} [module:${this.module?.name}]` }, description: format(`COMMAND_${this.name.toUpperCase()}_HELP`), fields diff --git a/src/client/interfaces/commands/SettingsCommand.ts b/src/client/interfaces/commands/SettingsCommand.ts index ed3929a..b8a8f71 100644 --- a/src/client/interfaces/commands/SettingsCommand.ts +++ b/src/client/interfaces/commands/SettingsCommand.ts @@ -9,6 +9,7 @@ import Module from '../Module.js'; import { SettingsCommandOptions } from '../../../@types/Commands/Settings.js'; import { CommandOptionType, CommandParams } from '../../../@types/Client.js'; import InvokerWrapper from '../../components/wrappers/InvokerWrapper.js'; +import { APIEmbed } from 'discord.js'; class SettingsCommand extends SlashCommand { @@ -34,7 +35,7 @@ class SettingsCommand extends SlashCommand build () { const settings = this.client.registry - .filter((c) => c.type === 'setting' && (c.module as Module).name === this.name); + .filter((c) => c.type === 'setting' && c.module.name === this.name); // const allSettings = this.client.registry.components.filter((c) => c._type ==='setting'); // Organise modules into subcommand groups @@ -119,27 +120,28 @@ class SettingsCommand extends SlashCommand // Pass setting values copy so the changes don't persist unless successful and actually saved const _setting = { ...settings[setting.name] }; const result = await setting.execute(invoker, opts, _setting); - if (result) + if (result) { const obj = { components: [], params: {}, ...result }; obj.params.setting = setting.name; - if (!result.error) + if (result.error) + { + await invoker.reply({ ...obj, emoji: 'failure', edit: invoker.replied }); + } + else { settings[setting.name] = _setting; await guild.updateSettings(settings); const emoji = result.emoji ? result.emoji : 'success'; - await invoker.reply({ ...obj, emoji, _edit: invoker.replied }); - } - else - { - await invoker.reply({ ...obj, emoji: 'failure', _edit: invoker.replied }); + await invoker.reply({ ...obj, emoji, edit: invoker.replied }); } } } - catch (err) + catch (err) { - this.client.logger.error(`Error during setting execution:\n${err.stack || err}`); + const error = err as Error; + this.client.logger.error(`Error during setting execution:\n${error.stack || err}`); await invoker.editReply({ index: 'SETTINGS_ERROR', params: { resolveable: setting.resolveable }, @@ -147,17 +149,15 @@ class SettingsCommand extends SlashCommand }); } - if (!invoker.replied) + if (!invoker.replied) await invoker.reply({ index: 'SETTINGS_NO_REPLY', params: { resolveable: setting.resolveable } }); - } - async _listSettings (invoker) + async _listSettings (invoker: InvokerWrapper) { - const { settings } = this.client.registry; const modules = settings.filter((setting) => setting.module.name === this.name) .reduce((acc, setting) => @@ -167,31 +167,29 @@ class SettingsCommand extends SlashCommand acc[name] = []; acc[name].push(setting.display); return acc; - }, {}); + }, {} as {[key: string]: string[]}); const { guild } = invoker; - const embed = { + const embed: APIEmbed = { title: 'Settings', description: guild.format('SETTINGS_LIST'), fields: [] }; const entries = Object.entries(modules); for (const [ module, values ] of entries) - embed.fields.push({ + embed.fields!.push({ name: Util.capitalise(module), value: `\`${values.join('`\n`')}\``, inline: true }); return invoker.reply({ embeds: [ embed ] }); - } - async _showSetting (invoker, setting) + async _showSetting (invoker: InvokerWrapper, setting: Setting) { - const { guild, subcommand } = invoker; - const embed = setting.usageEmbed(guild, null, this.subcommand(subcommand.name)); + const embed = setting.usageEmbed(guild, null, this.subcommand(subcommand!.name)); const dataFields = await setting.fields(guild); // eslint-disable-next-line no-return-assign dataFields.forEach((field, index) => diff --git a/src/constants/Constants.ts b/src/constants/Constants.ts index 67fc159..0ba6e8c 100644 --- a/src/constants/Constants.ts +++ b/src/constants/Constants.ts @@ -80,7 +80,7 @@ const InfractionResolves: {[key in InfractionType]: string[]} = { DELETE: ['delete', 'del'] }; -const Infractions = Object.keys(InfractionResolves); +const Infractions = Object.keys(InfractionResolves) as InfractionType[]; const InfractionTargetTypes = [ 'USER',