mirror of
https://github.com/jaronpate/unicord.git
synced 2026-10-18 21:47:40 +00:00
refactor: update command and event handling, improve logging, and enhance argument validation, add client inheritance
This commit is contained in:
+91
-53
@@ -10,13 +10,14 @@ import { UnicordCommandContext } from './context';
|
||||
import type { UnicordEventProcessor } from './eventProcessor';
|
||||
import { Embed } from './types';
|
||||
import { UnicordArgumentType, UnicordEventType } from './types/common';
|
||||
import { log } from './utils';
|
||||
|
||||
type UnicordCommandDefinition<Options extends UnicordCommandOptions> = {
|
||||
options: Options;
|
||||
handler: UnicordHandler<Options>;
|
||||
handler: UnicordCommandHandler<Options>;
|
||||
};
|
||||
|
||||
export const OptionConstructorMap = {
|
||||
export const ArgumentConstructorMap = {
|
||||
[UnicordArgumentType.String]: String,
|
||||
[UnicordArgumentType.Integer]: Number,
|
||||
[UnicordArgumentType.Boolean]: Boolean,
|
||||
@@ -32,8 +33,14 @@ export const OptionConstructorMap = {
|
||||
|
||||
export class UnicordCommandManager {
|
||||
private handlers = {
|
||||
[UnicordEventType.ChatCommands]: new Map<string, UnicordCommandDefinition<any>[]>(),
|
||||
[UnicordEventType.ApplicationCommands]: new Map<string, UnicordCommandDefinition<any>[]>(),
|
||||
[UnicordEventType.ChatCommands]: new Map<
|
||||
string,
|
||||
UnicordCommandDefinition<any> & { wrappedHandler: UnicordCommandHandler<any> }
|
||||
>(),
|
||||
[UnicordEventType.ApplicationCommands]: new Map<
|
||||
string,
|
||||
UnicordCommandDefinition<any> & { wrappedHandler: UnicordCommandHandler<any> }
|
||||
>(),
|
||||
};
|
||||
|
||||
constructor(private readonly self: Unicord, private readonly eventProcessor: UnicordEventProcessor) {
|
||||
@@ -42,53 +49,13 @@ export class UnicordCommandManager {
|
||||
[UnicordEventType.ApplicationCommands]: new Map(),
|
||||
};
|
||||
|
||||
// Register the default help command
|
||||
this.registerChatCommand(
|
||||
'help',
|
||||
async (context) => {
|
||||
// Just list chat commands for now
|
||||
const reply = new Embed().setTitle('Available Commands');
|
||||
|
||||
for (const [command, defs] of this.handlers[UnicordEventType.ChatCommands]) {
|
||||
for (const def of defs) {
|
||||
let fieldValue = def.options.description ?? 'No description';
|
||||
fieldValue += '\n';
|
||||
|
||||
if (def.options.args.length > 0) {
|
||||
fieldValue += `\n\`${command}`;
|
||||
for (const arg of def.options.args) {
|
||||
fieldValue += arg.required ? ` <${arg.name}>` : ` <?${arg.name}>`;
|
||||
}
|
||||
fieldValue += '`\n';
|
||||
for (const arg of def.options.args) {
|
||||
fieldValue += `- \`${arg.name}\` (${UnicordArgumentType[arg.type]}${
|
||||
arg.required ? ', required' : ''
|
||||
}): ${arg.description ?? 'No description'}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
fieldValue += '\n';
|
||||
|
||||
reply.fields.push({
|
||||
name: command,
|
||||
value: fieldValue,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await context.reply(reply.toMessage());
|
||||
},
|
||||
{
|
||||
description: 'Lists all available commands',
|
||||
args: [],
|
||||
},
|
||||
);
|
||||
this.registerDefaultHelpCommand();
|
||||
}
|
||||
|
||||
private validateAndResolveArgs = async <const Options extends UnicordCommandOptions>(
|
||||
private async validateAndResolveArgs<const Options extends UnicordCommandOptions>(
|
||||
args: any[],
|
||||
definitions: Options['args'],
|
||||
): Promise<ArgumentTypeFromOptions<Options['args']>> => {
|
||||
): Promise<ArgumentTypeFromOptions<Options['args']>> {
|
||||
// Zip the args and definition together
|
||||
const zipped: [UnicordArgumentDefinition, any][] = args.map((arg, i) => [definitions[i], arg]);
|
||||
// Validate the args
|
||||
@@ -102,7 +69,78 @@ export class UnicordCommandManager {
|
||||
}
|
||||
// TODO: Fix types so we don't need to cast here
|
||||
return Promise.resolve(validated as ArgumentTypeFromOptions<Options['args']>);
|
||||
};
|
||||
}
|
||||
|
||||
registerDefaultHelpCommand() {
|
||||
this.registerChatCommand(
|
||||
'help',
|
||||
async (context) => {
|
||||
// Just list chat commands for now
|
||||
const reply = new Embed().setTitle('Available Commands');
|
||||
|
||||
for (const [command, def] of this.handlers[UnicordEventType.ChatCommands]) {
|
||||
let fieldValue = def.options.description ?? 'No description';
|
||||
fieldValue += '\n';
|
||||
|
||||
if (def.options.args.length > 0) {
|
||||
fieldValue += `\n\`${command}`;
|
||||
for (const arg of def.options.args) {
|
||||
fieldValue += arg.required ? ` <${arg.name}>` : ` <?${arg.name}>`;
|
||||
}
|
||||
fieldValue += '`\n';
|
||||
for (const arg of def.options.args) {
|
||||
fieldValue += `- \`${arg.name}\` (${UnicordArgumentType[arg.type]}${
|
||||
arg.required ? ', required' : ''
|
||||
}): ${arg.description ?? 'No description'}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
fieldValue += '\n';
|
||||
|
||||
reply.fields.push({
|
||||
name: command,
|
||||
value: fieldValue,
|
||||
});
|
||||
}
|
||||
|
||||
await context.reply(reply.toMessage());
|
||||
},
|
||||
{
|
||||
description: 'Lists all available commands',
|
||||
args: [],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
all() {
|
||||
return this.handlers;
|
||||
}
|
||||
|
||||
has(type: UnicordEventType.ChatCommands | UnicordEventType.ApplicationCommands, event: string) {
|
||||
return this.handlers[type].has(event);
|
||||
}
|
||||
|
||||
merge(other: UnicordCommandManager) {
|
||||
const handlers = other.all();
|
||||
|
||||
for (const type in handlers) {
|
||||
const typeKey = type as UnicordEventType.ChatCommands | UnicordEventType.ApplicationCommands;
|
||||
for (const [event, definition] of handlers[typeKey]) {
|
||||
this.register(typeKey, event, definition.handler, definition.options);
|
||||
}
|
||||
}
|
||||
|
||||
this.registerDefaultHelpCommand();
|
||||
}
|
||||
|
||||
unregister(type: UnicordEventType.ChatCommands | UnicordEventType.ApplicationCommands, event: string) {
|
||||
const handler = this.handlers[type].get(event);
|
||||
if (handler) {
|
||||
this.eventProcessor.unregister(`${type}:${event}`, handler.wrappedHandler);
|
||||
this.handlers[type].delete(event);
|
||||
}
|
||||
return this.self;
|
||||
}
|
||||
|
||||
// TODO: Register application commands with the DiscordAPI
|
||||
register<const Options extends UnicordCommandOptions>(
|
||||
@@ -111,19 +149,19 @@ export class UnicordCommandManager {
|
||||
handler: UnicordCommandHandler<Options>,
|
||||
options: Options = { args: [] } as unknown as Options,
|
||||
) {
|
||||
if (this.handlers[type].has(event) === false) {
|
||||
this.handlers[type].set(event, []);
|
||||
if (this.has(type, event)) {
|
||||
this.unregister(type, event);
|
||||
}
|
||||
|
||||
this.handlers[type].get(event)!.push({ options, handler });
|
||||
|
||||
// Generate a closure to wrap the handler
|
||||
const wrappedHandler = (async (context: UnicordCommandContext, args: any[]) => {
|
||||
const resolvedArgs = await this.validateAndResolveArgs<Options>(args, options.args);
|
||||
return handler(context, resolvedArgs);
|
||||
}) as UnicordCommandHandler<Options>;
|
||||
|
||||
this.handlers[type].set(event, { options, handler, wrappedHandler });
|
||||
|
||||
this.eventProcessor.register(`${type}:${event}`, wrappedHandler);
|
||||
|
||||
return this.self;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user