Bugfixes to eventhooker and infraction dms

This commit is contained in:
Erik 2023-12-05 23:25:12 +02:00
parent f65604ec2b
commit 9d88266f41
12 changed files with 1205 additions and 1215 deletions

View File

@ -76,7 +76,7 @@ class EventHooker
const eventArgs = [];
for (const arg of args)
{
if (arg && typeof arg === 'object' && 'guild' in arg)
if (arg && typeof arg === 'object' && 'guild' in arg && arg.guild)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
arg.guildWrapper = this.#target.getGuildWrapper(arg.guild!.id);

View File

@ -1,51 +1,51 @@
import DiscordClient from '../DiscordClient.js';
import SlashCommand from '../interfaces/commands/SlashCommand.js';
class Intercom
{
#client: DiscordClient;
constructor (client: DiscordClient)
{
this.#client = client;
if (client.singleton || client!.shard?.ids[0] === 0)
{
this.#client.eventHooker.hook('built', () =>
{
this._transportCommands();
});
}
}
send (type: string, message = {})
{
if (typeof message !== 'object')
throw new Error('Invalid message object');
if (!process.send)
return; // Nowhere to send, the client was not spawned as a shard
return process.send({
[`_${type}`]: true,
...message
});
}
_transportCommands ()
{
if (!this.#client.application)
throw new Error('Missing client application');
const clientId = this.#client.application.id;
const commands = this.#client.registry
.filter((c: SlashCommand) => c.type === 'command' && c.slash)
.map((c) => c.shape);
// console.log(inspect(commands, { depth: 25 }));
if (process.env.NODE_ENV === 'development')
return this.send('commands', { type: 'guild', commands, clientId });
this.send('commands', { type: 'global', commands, clientId });
// this.send('commands', { type: 'guild', commands, clientId });
}
}
import DiscordClient from '../DiscordClient.js';
import SlashCommand from '../interfaces/commands/SlashCommand.js';
class Intercom
{
#client: DiscordClient;
constructor (client: DiscordClient)
{
this.#client = client;
if (client.singleton || client!.shard?.ids[0] === 0)
{
this.#client.eventHooker.hook('built', () =>
{
this._transportCommands();
});
}
}
send (type: string, message = {})
{
if (typeof message !== 'object')
throw new Error('Invalid message object');
if (!process.send)
return; // Nowhere to send, the client was not spawned as a shard
return process.send({
[`_${type}`]: true,
...message
});
}
_transportCommands ()
{
if (!this.#client.application)
throw new Error('Missing client application');
const clientId = this.#client.application.id;
const commands = this.#client.registry
.filter((c: SlashCommand) => c.type === 'command' && c.slash)
.map((c) => c.shape);
// console.log(inspect(commands, { depth: 25 }));
if (process.env.NODE_ENV === 'development')
return this.send('commands', { type: 'guild', commands, clientId });
this.send('commands', { type: 'global', commands, clientId });
// this.send('commands', { type: 'guild', commands, clientId });
}
}
export default Intercom;

View File

@ -626,7 +626,7 @@ class ModerationManager implements Initialisable
if (updateCase)
await this.#client.storage.mongodb.infractions.updateOne(
{ id: infraction.id },
{ _callbacked: true }
{ $set: { _callbacked: true } }
).catch((e) =>
{
this.#logger.error(`Error during update of infraction:\n${e.stack || e}`);

View File

@ -132,7 +132,7 @@ class ImportCommand extends SlashCommand
for (const log of existingLogs)
{
log.case += highestOldId;
await this.client.mongodb.infractions.updateOne({ _id: log._id }, { case: log.case });
await this.client.mongodb.infractions.updateOne({ _id: log._id }, { $set: { case: log.case } });
}
await this.client.mongodb.infractions.insertMany(imported);
if (!guild.data.caseId)
@ -193,12 +193,12 @@ class ImportCommand extends SlashCommand
else if (version === '3')
{
delete webhook.feature;
await this.client.storageManager.mongodb.webhooks.updateOne({ feature: 'messages', guild: guild.id }, webhook);
await this.client.storageManager.mongodb.webhooks.updateOne({ feature: 'messages', guild: guild.id }, { $set: webhook });
}
}
if (permissions)
await this.client.storageManager.mongodb.permissions.updateOne({ guildId: guild.id }, permissions);
await this.client.storageManager.mongodb.permissions.updateOne({ guildId: guild.id }, { $set: permissions });
const { premium } = imported.settings;
delete imported.settings.premium;

View File

@ -48,9 +48,9 @@ class UtilityHook extends Observer
await this.client.storageManager.mongodb.roleCache.updateOne({
member: member.id, guild: guild.id
}, {
}, { $set: {
roles: storeThese, timestamp: Date.now()
});
} });
}
async automute (member: ExtendedGuildMember)

View File

@ -1,4 +1,4 @@
import { CommandParams, FormatParams } from '../../../../../@types/Client.js';
import { CommandOptionType, CommandParams, FormatParams } from '../../../../../@types/Client.js';
import { ProtectionSettings, ProtectionType } from '../../../../../@types/Settings.js';
import Util from '../../../../utilities/Util.js';
import DiscordClient from '../../../DiscordClient.js';
@ -49,11 +49,11 @@ class ProtectionSetting extends Setting
},
{
name: 'enabled',
description: 'Whether setting is active or not'
description: 'Whether setting is active or not',
type: CommandOptionType.BOOLEAN
}
]
});
}
async execute (invoker: InvokerWrapper<true>, opts: CommandParams, setting: ProtectionSettings)

File diff suppressed because it is too large Load Diff

View File

@ -152,7 +152,7 @@ class UserWrapper
{
await this.#client.mongodb.users.updateOne(
{ guildId: this.id },
data
{ $set: data }
);
this.#settings = {
...this.#settings,

View File

@ -112,7 +112,7 @@ class UnlockdownInfraction extends Infraction
if (callback)
await this.client.moderation.removeCallback(callback.infraction, true);
else
await this.client.mongodb.infractions.updateOne({ id: latest.id }, { _callbacked: true });
await this.client.mongodb.infractions.updateOne({ id: latest.id }, { $set: { _callbacked: true } });
}
await this.handle();

View File

@ -166,7 +166,6 @@ class Infraction
async handle ()
{
// Infraction was fetched from database, i.e. was already executed previously
if (this.#fetched)
throw new Error('Cannot handle a fetched Infraction');
@ -186,7 +185,7 @@ class Infraction
});
/* Logging */
if (moderation.channel)
if (moderation.channel)
{
if (moderation.infractions.includes(this.#type!))
{
@ -197,7 +196,6 @@ class Infraction
this.#dmLogMessage = await this.#moderationLog.send({ embeds: [ await this.#embed() ] }).catch(null);
this.#modLogMessageId = this.#dmLogMessage?.id || null;
}
}
else
{
@ -205,7 +203,7 @@ class Infraction
}
}
if (dminfraction.enabled && !this.#silent)
if (dminfraction.enabled && !this.#silent)
{
if (this.#targetType === 'USER' && dminfraction.infractions.includes(this.#type!))
{
@ -218,30 +216,25 @@ class Infraction
.replace(/\{infraction\}/ugim, this.dictionary.past)
.replace(/\{from\|on\}/ugim, Constants.RemovedInfractions.includes(this.#type!) ? 'from' : 'on'); // add more if you want i should probably add a better system for this...
if (Util.isSendable(this.#target))
if (Util.isSendable(this.#target))
{
const logMessage = await this.#target.send({
content: message,
embeds: [ await this.#embed(true) ]
}).catch(null);
this.#dmLogMessageId = logMessage.id;
}).catch(() => null);
this.#dmLogMessageId = logMessage?.id ?? null;
}
}
}
if (this.#duration)
{
if (this.#duration)
await this.#client.moderation.handleCallbacks([ this.json ]);
}
/* LMAOOOO PLEASE DONT JUDGE ME */
if (this.#data.roles)
delete this.#data.roles;
return this.save();
}
execute (): Promise<InfractionSuccess | InfractionFail>
@ -254,7 +247,7 @@ class Infraction
const filter: {id: string, _id?: ObjectId} = { id: this.id };
if (this.#mongoId)
filter._id = this.#mongoId;
return this.#client.mongodb.infractions.updateOne(filter, this.json)
return this.#client.mongodb.infractions.updateOne(filter, { $set: this.json })
.catch((error: Error) =>
{
this.#logger.error(`There was an issue saving infraction data to the database.\n${error.stack || error}\nInfraction data:\n${inspect(this.json)}`);
@ -321,7 +314,6 @@ class Infraction
// Function implemented in subclasses for additional case data
if (this.description && this.description instanceof Function)
description += this.description(dm);
if (this.#resolved)
{
@ -345,14 +337,12 @@ class Infraction
}
embed.description = description;
return embed;
}
description (_dm: boolean): string
{
throw new Error('Description is to be implemented by a subclass');
return '';
}
protected get client ()

View File

@ -1,402 +1,402 @@
import { LoggerClient } from '@navy.gif/logger';
import { EmbedBuilder, Message, PermissionsString, Snowflake } from 'discord.js';
import Component from '../Component.js';
import CommandOption from '../CommandOption.js';
import { Util } from '../../../utilities/index.js';
import DiscordClient from '../../DiscordClient.js';
import { InvokerWrapper } from '../../components/wrappers/index.js';
import { CommandOptionParams, CommandOptionType, CommandOptions, CommandParams } from '../../../../@types/Client.js';
import { ReplyOptions } from '../../../../@types/Wrappers.js';
type CommandUsageLimits = {
usages: number,
duration: number
}
type CommandThrottle = {
usages: number,
start: number,
timeout: NodeJS.Timeout
}
// declare interface Command extends Component
// {
// get type(): 'command'
// }
abstract class Command extends Component
{
#logger: LoggerClient;
#name: string;
#description: string;
#tags: string[];
#aliases: string[];
#restricted: boolean;
#showUsage: boolean;
#guildOnly: boolean;
#archivable: boolean;
#slash: boolean;
#clientPermissions: PermissionsString[];
#memberPermissions: PermissionsString[];
#invokes: {
success: number,
successTime: number,
fail: number,
failTime: number
};
#options: CommandOption[];
#throttling?: CommandUsageLimits;
#throttles: Map<Snowflake, CommandThrottle>;
/**
* Creates an instance of Command.
* @param {DiscordClient} client
* @param {Object} [options={}]
* @memberof Command
*/
constructor (client: DiscordClient, options: CommandOptions)
{
if (!options)
throw Util.fatal(new Error('Missing command options'));
if (!options.name)
throw Util.fatal(new Error('Missing name'));
super(client, {
id: options.name,
type: 'command',
disabled: options.disabled,
guarded: options.guarded,
moduleName: options.moduleName
});
this.#name = options.name;
this.#logger = client.createLogger(this);
if (!options.moduleName)
this.logger.warn(`Command ${this.#name} is missing module information.`);
this.#description = options.description || '';
this.#tags = options.tags || [];
this.#aliases = options.aliases || [];
this.#restricted = Boolean(options?.restricted);
this.#showUsage = Boolean(options.showUsage);
this.#guildOnly = Boolean(options?.guildOnly);
this.#archivable = typeof options.archivable === 'undefined' ? true : Boolean(options.archivable);
this.#slash = Boolean(options.slash);
// Convers permissions to PascalCase from snake case bc for some reason d.js decided it was a good change
this.#clientPermissions = [ ...new Set<PermissionsString>([ 'SendMessages', ...options.clientPermissions || [] ]) ]; // .map(Util.pascalConverter);
this.#memberPermissions = options.memberPermissions || []; // .map(Util.pascalConverter);
this.#invokes = {
success: 0,
successTime: 0,
fail: 0,
failTime: 0
};
this.#options = [];
if (options.options)
this.#parseOptions(options.options);
this.#options.sort((a, b) =>
{
if (a.required)
return -1;
if (b.required)
return 1;
return 0;
});
this.#throttles = new Map();
}
get name ()
{
return this.#name;
}
get aliases ()
{
return this.#aliases;
}
get description ()
{
return this.#description;
}
get tags ()
{
return this.#tags;
}
get restricted ()
{
return this.#restricted;
}
get showUsage ()
{
return this.#showUsage;
}
get archivable ()
{
return this.#archivable;
}
get slash ()
{
return this.#slash;
}
get memberPermissions ()
{
return this.#memberPermissions;
}
get clientPermissions ()
{
return this.#clientPermissions;
}
get options ()
{
return this.#options;
}
get guildOnly ()
{
return this.#guildOnly;
}
protected get logger ()
{
return this.#logger;
}
get throttling ()
{
return this.#throttling;
}
get throttles ()
{
return this.#throttles;
}
get invokes ()
{
return this.#invokes;
}
abstract execute(invoker: InvokerWrapper, options: CommandParams):
Promise<string | Message | null | ReplyOptions | void | EmbedBuilder>;
// {
// throw new Error(`${this.resolveable} is missing an execute function.`);
// }
success (when: number)
{
const now = Date.now();
const execTime = now - when;
// Calculate new average
if (this.#invokes.successTime)
{
this.#invokes.successTime = (this.#invokes.successTime * this.#invokes.success + execTime) / ++this.#invokes.success;
}
else
{
this.#invokes.successTime = execTime;
this.#invokes.success++;
}
}
error (when: number)
{
const now = Date.now();
const execTime = now - when;
// Calculate new average
if (this.#invokes.failTime)
{
this.#invokes.failTime = (this.#invokes.failTime * this.#invokes.fail + execTime) / ++this.#invokes.fail;
}
else
{
this.#invokes.failTime = execTime;
this.#invokes.fail++;
}
}
async usageEmbed (invoker: InvokerWrapper, verbose = false)
{
const fields = [];
const { guild, subcommand, subcommandGroup } = invoker;
let type = null;
const format = (index: string) => guild
? guild.format(index)
: this.client.format(index);
if (guild)
({ permissions: { type } } = await guild.settings());
if (this.#options.length)
{
if (verbose)
fields.push(...this.#options.map((opt) => opt.usage(guild)));
else if (subcommand)
{
const opt = this.subcommand(subcommand.name) as CommandOption;
fields.push(opt.usage(guild));
}
else if (subcommandGroup)
{
const opt = this.subcommandGroup(subcommandGroup.name) as CommandOption;
fields.push(opt.usage(guild));
}
}
if (this.memberPermissions.length)
{
let required = [];
if (type === 'discord')
required = this.memberPermissions;
else if (type === 'grant')
required = [ this.resolveable ];
else
required = [ this.resolveable, ...this.memberPermissions ];
fields.push({
name: `${format('GENERAL_PERMISSIONS')}`,
value: `\`${required.join('`, `')}\``
});
}
return new EmbedBuilder({
author: {
name: `${this.name} [module:${this.module?.name}]`
},
description: format(`COMMAND_${this.name.toUpperCase()}_HELP`),
fields
});
}
subcommandGroup (name: string)
{
if (!name)
return null;
name = name.toLowerCase();
return this.subcommandGroups.find((group) => group.name === name) ?? null;
}
get subcommandGroups ()
{
return this.#options.filter((opt) => opt.type === CommandOptionType.SUB_COMMAND_GROUP);
}
subcommand (name?: string)
{
if (!name)
return null;
name = name.toLowerCase();
return this.subcommands.find((cmd) => cmd.name === name) ?? null;
}
get subcommands ()
{
return this.#subcommands(this.#options);
}
/**
* @private
*/
#subcommands (opts: CommandOption[]): CommandOption[]
{
const subcommands = [];
for (const opt of opts)
{
if (opt.type === CommandOptionType.SUB_COMMAND)
subcommands.push(opt);
else if (opt.type === CommandOptionType.SUB_COMMAND_GROUP)
subcommands.push(...this.#subcommands(opt.options));
}
return subcommands;
}
// probably not a final name -- flattenedOptions maybe?
get actualOptions ()
{
return this.#actualOptions(this.#options);
}
/**
* @private
*/
#actualOptions (opts: CommandOption[]): CommandOption[]
{
const options: CommandOption[] = [];
for (const opt of opts)
{
if ([ CommandOptionType.SUB_COMMAND_GROUP, CommandOptionType.SUB_COMMAND ].includes(opt.type))
options.push(...this.#actualOptions(opt.options));
else
options.push(opt);
}
return options;
}
#parseOptions (options: CommandOptionParams[])
{
for (const opt of options)
{
if (opt instanceof CommandOption)
{
opt.client = this.client;
this.#options.push(opt);
continue;
}
if (!(opt.name instanceof Array))
{
this.#options.push(new CommandOption({ ...opt, client: this.client }));
continue;
}
// Allows easy templating of subcommands that share arguments
const { name: names, description, type, ...opts } = opt;
for (const name of names)
{
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)
{
_type = CommandOptionType.STRING;
this.logger.warn(`Missing option type for ${this.resolveable}.${name}, defaulting to string`);
}
// throw new Error(`Missing type for option ${name} in command ${this.name}`);
this.#options.push(new CommandOption({
...opts,
name,
type: _type,
description: desc,
client: this.client
}));
}
}
}
}
import { LoggerClient } from '@navy.gif/logger';
import { EmbedBuilder, Message, PermissionsString, Snowflake } from 'discord.js';
import Component from '../Component.js';
import CommandOption from '../CommandOption.js';
import { Util } from '../../../utilities/index.js';
import DiscordClient from '../../DiscordClient.js';
import { InvokerWrapper } from '../../components/wrappers/index.js';
import { CommandOptionParams, CommandOptionType, CommandOptions, CommandParams } from '../../../../@types/Client.js';
import { ReplyOptions } from '../../../../@types/Wrappers.js';
type CommandUsageLimits = {
usages: number,
duration: number
}
type CommandThrottle = {
usages: number,
start: number,
timeout: NodeJS.Timeout
}
// declare interface Command extends Component
// {
// get type(): 'command'
// }
abstract class Command extends Component
{
#logger: LoggerClient;
#name: string;
#description: string;
#tags: string[];
#aliases: string[];
#restricted: boolean;
#showUsage: boolean;
#guildOnly: boolean;
#archivable: boolean;
#slash: boolean;
#clientPermissions: PermissionsString[];
#memberPermissions: PermissionsString[];
#invokes: {
success: number,
successTime: number,
fail: number,
failTime: number
};
#options: CommandOption[];
#throttling?: CommandUsageLimits;
#throttles: Map<Snowflake, CommandThrottle>;
/**
* Creates an instance of Command.
* @param {DiscordClient} client
* @param {Object} [options={}]
* @memberof Command
*/
constructor (client: DiscordClient, options: CommandOptions)
{
if (!options)
throw Util.fatal(new Error('Missing command options'));
if (!options.name)
throw Util.fatal(new Error('Missing name'));
super(client, {
id: options.name,
type: 'command',
disabled: options.disabled,
guarded: options.guarded,
moduleName: options.moduleName
});
this.#name = options.name;
this.#logger = client.createLogger(this);
if (!options.moduleName)
this.logger.warn(`Command ${this.#name} is missing module information.`);
this.#description = options.description || '';
this.#tags = options.tags || [];
this.#aliases = options.aliases || [];
this.#restricted = Boolean(options?.restricted);
this.#showUsage = Boolean(options.showUsage);
this.#guildOnly = Boolean(options?.guildOnly);
this.#archivable = typeof options.archivable === 'undefined' ? true : Boolean(options.archivable);
this.#slash = Boolean(options.slash);
// Convers permissions to PascalCase from snake case bc for some reason d.js decided it was a good change
this.#clientPermissions = [ ...new Set<PermissionsString>([ 'SendMessages', ...options.clientPermissions || [] ]) ]; // .map(Util.pascalConverter);
this.#memberPermissions = options.memberPermissions || []; // .map(Util.pascalConverter);
this.#invokes = {
success: 0,
successTime: 0,
fail: 0,
failTime: 0
};
this.#options = [];
if (options.options)
this.#parseOptions(options.options);
this.#options.sort((a, b) =>
{
if (a.required)
return -1;
if (b.required)
return 1;
return 0;
});
this.#throttles = new Map();
}
get name ()
{
return this.#name;
}
get aliases ()
{
return this.#aliases;
}
get description ()
{
return this.#description;
}
get tags ()
{
return this.#tags;
}
get restricted ()
{
return this.#restricted;
}
get showUsage ()
{
return this.#showUsage;
}
get archivable ()
{
return this.#archivable;
}
get slash ()
{
return this.#slash;
}
get memberPermissions ()
{
return this.#memberPermissions;
}
get clientPermissions ()
{
return this.#clientPermissions;
}
get options ()
{
return this.#options;
}
get guildOnly ()
{
return this.#guildOnly;
}
protected get logger ()
{
return this.#logger;
}
get throttling ()
{
return this.#throttling;
}
get throttles ()
{
return this.#throttles;
}
get invokes ()
{
return this.#invokes;
}
abstract execute(invoker: InvokerWrapper, options: CommandParams):
Promise<string | Message | null | ReplyOptions | void | EmbedBuilder>;
// {
// throw new Error(`${this.resolveable} is missing an execute function.`);
// }
success (when: number)
{
const now = Date.now();
const execTime = now - when;
// Calculate new average
if (this.#invokes.successTime)
{
this.#invokes.successTime = (this.#invokes.successTime * this.#invokes.success + execTime) / ++this.#invokes.success;
}
else
{
this.#invokes.successTime = execTime;
this.#invokes.success++;
}
}
error (when: number)
{
const now = Date.now();
const execTime = now - when;
// Calculate new average
if (this.#invokes.failTime)
{
this.#invokes.failTime = (this.#invokes.failTime * this.#invokes.fail + execTime) / ++this.#invokes.fail;
}
else
{
this.#invokes.failTime = execTime;
this.#invokes.fail++;
}
}
async usageEmbed (invoker: InvokerWrapper, verbose = false)
{
const fields = [];
const { guild, subcommand, subcommandGroup } = invoker;
let type = null;
const format = (index: string) => guild
? guild.format(index)
: this.client.format(index);
if (guild)
({ permissions: { type } } = await guild.settings());
if (this.#options.length)
{
if (verbose)
fields.push(...this.#options.map((opt) => opt.usage(guild)));
else if (subcommand)
{
const opt = this.subcommand(subcommand.name) as CommandOption;
fields.push(opt.usage(guild));
}
else if (subcommandGroup)
{
const opt = this.subcommandGroup(subcommandGroup.name) as CommandOption;
fields.push(opt.usage(guild));
}
}
if (this.memberPermissions.length)
{
let required = [];
if (type === 'discord')
required = this.memberPermissions;
else if (type === 'grant')
required = [ this.resolveable ];
else
required = [ this.resolveable, ...this.memberPermissions ];
fields.push({
name: `${format('GENERAL_PERMISSIONS')}`,
value: `\`${required.join('`, `')}\``
});
}
return new EmbedBuilder({
author: {
name: `${this.name} [module:${this.module?.name}]`
},
description: format(`COMMAND_${this.name.toUpperCase()}_HELP`),
fields
});
}
subcommandGroup (name: string)
{
if (!name)
return null;
name = name.toLowerCase();
return this.subcommandGroups.find((group) => group.name === name) ?? null;
}
get subcommandGroups ()
{
return this.#options.filter((opt) => opt.type === CommandOptionType.SUB_COMMAND_GROUP);
}
subcommand (name?: string)
{
if (!name)
return null;
name = name.toLowerCase();
return this.subcommands.find((cmd) => cmd.name === name) ?? null;
}
get subcommands ()
{
return this.#subcommands(this.#options);
}
/**
* @private
*/
#subcommands (opts: CommandOption[]): CommandOption[]
{
const subcommands = [];
for (const opt of opts)
{
if (opt.type === CommandOptionType.SUB_COMMAND)
subcommands.push(opt);
else if (opt.type === CommandOptionType.SUB_COMMAND_GROUP)
subcommands.push(...this.#subcommands(opt.options));
}
return subcommands;
}
// probably not a final name -- flattenedOptions maybe?
get actualOptions ()
{
return this.#actualOptions(this.#options);
}
/**
* @private
*/
#actualOptions (opts: CommandOption[]): CommandOption[]
{
const options: CommandOption[] = [];
for (const opt of opts)
{
if ([ CommandOptionType.SUB_COMMAND_GROUP, CommandOptionType.SUB_COMMAND ].includes(opt.type))
options.push(...this.#actualOptions(opt.options));
else
options.push(opt);
}
return options;
}
#parseOptions (options: CommandOptionParams[])
{
for (const opt of options)
{
if (opt instanceof CommandOption)
{
opt.client = this.client;
this.#options.push(opt);
continue;
}
if (!(opt.name instanceof Array))
{
this.#options.push(new CommandOption({ ...opt, client: this.client }));
continue;
}
// Allows easy templating of subcommands that share arguments
const { name: names, description, type, ...opts } = opt;
for (const name of names)
{
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)
{
_type = CommandOptionType.STRING;
this.logger.warn(`Missing option type for ${this.resolveable}.${name}, defaulting to string`);
}
// throw new Error(`Missing type for option ${name} in command ${this.name}`);
this.#options.push(new CommandOption({
...opts,
name,
type: _type,
description: desc,
client: this.client
}));
}
}
}
}
export default Command;

View File

@ -72,7 +72,7 @@ class Util
static has (o: unknown, k: string)
{
return Object.prototype.hasOwnProperty.call(o, k);
return Object.prototype.hasOwnProperty.call(o, k);
}
static hasId (obj: unknown): obj is DiscordStruct
@ -124,7 +124,7 @@ class Util
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static isSendable (obj: any): obj is { send: () => Promise<Message>}
{
return Util.has(obj, 'send') && typeof obj.send === 'function';
return typeof obj.send === 'function';
}
// static hasProperty<T> (obj: any, name: string): obj is { [key in typeof name]: T }