feat(handler) finish loading command functionality, adjust properties

per cmd type
This commit is contained in:
Jacob Nguyen
2022-03-11 17:31:44 -06:00
parent f1ffff48c8
commit ecf7ecc92c
4 changed files with 58 additions and 176 deletions

View File

@@ -1,7 +1,9 @@
import { concatMap, first, fromEvent, pipe, tap } from "rxjs";
import { concatMap, first, from, fromEvent, map, pipe, tap } from "rxjs";
import { basename } from 'path';
import * as Files from '../utilities/readFile';
import type Wrapper from '../structures/wrapper';
import type { Command } from "../structures/commands/command";
import type { ApplicationCommandOptionData } from "discord.js";
export const onReady = ( wrapper : Wrapper ) => {
const { client, init, commands, } = wrapper;
fromEvent(client, 'ready')
@@ -11,11 +13,24 @@ export const onReady = ( wrapper : Wrapper ) => {
concatMap(
pipe(
() => Files.buildData(commands),
( createCommandCache )
)
),
)
.subscribe();
.subscribe( () => console.log(Files.Commands));
}
async function createCommandCache( ) {
function setCommands ( { mod, absPath } : { mod : Command, absPath : string } ) {
const options = mod.options ?? [] as ApplicationCommandOptionData[];
const name = mod.name ?? Files.fmtFileName(basename(absPath));
mod.alias?.forEach( n => Files.Alias.set( n, { mod, options } ));
Files.Commands.set(name, { mod, options });
}
async function createCommandCache(
arr: Promise<{mod: Command, absPath: string}[]>
) {
from(await arr).subscribe ( setCommands );
}

View File

@@ -1,4 +1,3 @@
import * as Files from './utilities/readFile';
import type {
DiscordEvent,
possibleOutput,
@@ -48,10 +47,9 @@ export class Handler {
*/
constructor(wrapper: Wrapper) {
this.wrapper = wrapper;
this.client
}
/**
.on('messageCreate', async (message: Message) => {
const module = this.findModuleFrom(message);
if (module === undefined) {
@@ -86,12 +84,7 @@ export class Handler {
});
}
/**
*
* @param {Files.CommandVal} module Command file information
* @param {CommandInteraction} interaction The Discord.js command interaction (DiscordJS#CommandInteraction))
* @returns {possibleOutput | undefined} Takes return value and replies it, if possible input
*/
private async interactionResult(
module: Files.CommandVal,
@@ -109,13 +102,7 @@ export class Handler {
return (module.mod.execute?.(context, parsedArgs) as possibleOutput | undefined);
}
/**
*
* @param {Files.CommandVal} module Command file information
* @param {Message} message The message object
* @param {string} args Anything after the command
* @returns Takes return value and replies it, if possible input
*/
private async commandResult(
module: Files.CommandVal,
@@ -129,159 +116,22 @@ export class Handler {
);
return;
}
if (module.mod.visibility === 'private') {
const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test;
if (checkIsTestServer === undefined) {
this.defaultLogger.log(
sEvent.MISUSE_CMD,
message.guildId!,
`The text command ${module.mod.name} has private modifier but is not registered in private server config.`
);
return;
}
if (checkIsTestServer !== module.mod.test) {
this.defaultLogger.log(
sEvent.MISUSE_CMD,
message.guildId!,
`The command ${module.mod.name} is only available on test servers.`
);
return;
}
}
const context = new Context ( Some(message), None );
const args = message.content.slice(this.prefix.length).trim().split(/s+/g);
const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok(args);
if (parsedArgs.err) return parsedArgs.val;
return (module.mod.execute?.(context, parsedArgs) as possibleOutput | undefined);
}
/**
* This function chains `Files.buildData`
* @param {{name: string, mod: Module<unknown>, absPath: string}} modArr module information
*/
private async registerModules(
modArr: {
name: string;
mod: Module<unknown>;
absPath: string;
}[],
) {
for await (const { name, mod, absPath } of modArr) {
const cmdName = Files.fmtFileName(name);
switch (mod.type) {
case 1:
Files.Commands.set(cmdName, { mod: { name: cmdName, ...mod }, options: [] });
break;
case 2:
case 1 | 2:
{
const options = (await import(absPath)).options as ApplicationCommandOptionData[];
Files.Commands.set(cmdName, { mod: { name: cmdName, ...mod }, options: options ?? [] });
switch (mod.visibility) {
case 'private': {
// Reloading guild slash commands
await this.reloadSlash(cmdName, mod.desc, options);
}
case 'public': {
// Creating global commands
await this.client.application!.commands.create({
name: cmdName,
description: mod.desc,
options,
});
}
}
}
break;
default:
throw Error(`SernHandlerError: ${name} with ${mod.visibility} is not a valid module type.`);
}
if (mod.alias.length > 0) {
for (const alias of mod.alias) {
Files.Alias.set(alias, { mod: { name: cmdName, ...mod }, options: [] });
}
}
}
}
/**
*
* @param {T extends Message | CommandInteraction} ctx name of possible command
* @returns {Files.CommandVal | undefined}
*/
private findModuleFrom<T extends Message | CommandInteraction>(ctx: T): Files.CommandVal | undefined {
const name = ctx.applicationId === null ? fmt(ctx as Message, this.prefix).shift()! : (ctx as CommandInteraction).commandName;
return Files.Commands.get(name) ?? Files.Alias.get(name);
}
/**
*
* @param {string} cmdName name of command
* @param {string} description description of command
* @param {ApplicationCommandOptionData[]} options any options for the slash command
*/
private async reloadSlash(
cmdName: string,
description: string,
options: ApplicationCommandOptionData[],
): Promise<void> {
for (const { id } of this.privateServers) {
const guild = await this.client.guilds.fetch(id);
guild.commands.create({
name: cmdName,
description,
options,
});
}
}
/**
* @readonly
* @returns {string} The prefix used for legacy commands
*/
get prefix(): string {
return this.wrapper.defaultPrefix;
}
/**
* @readonly
* @returns {string} Directory of the commands folder
*/
get commandDir(): string {
return this.wrapper.commands;
}
/**
* @readonly
* @returns {Client<boolean>} the discord.js client (DiscordJS#Client));
*/
get client(): Client<boolean> {
return this.wrapper.client;
}
/**
* @readonly
* @returns {{test: boolean, id: string}[]} Private server ID for testing or personal use
*/
get privateServers(): { test: boolean; id: string }[] {
return this.wrapper.privateServers;
}
}
};
*/
}
/**
* @enum { number };
*/
export enum CommandType {
TEXT = 1,
SLASH = 2,
TEXT = 0b0001,
SLASH = 0b0010,
BOTH = 0b0011
}

View File

@@ -1,26 +1,44 @@
import type { Awaitable } from "discord.js";
import type { ApplicationCommandOptionData, Awaitable } from "discord.js";
import type { possibleOutput, Arg } from "../../../types/handler";
import Context from "../context";
import type * as Utils from '../../utilities/preprocessors/args';
import { None, Ok } from "ts-results";
import type { CommandType } from "../../sern";
import { CommandType } from "../../sern";
export abstract class Command {
protected name : string | undefined;
protected _name? : string | undefined;
protected _ctx : Context = new Context( None, None );
protected commandType : CommandType;
protected _commandType : CommandType;
protected _options : ApplicationCommandOptionData[] | undefined;
protected _alias : string[] | undefined;
protected constructor (
name : string | undefined,
commandType : CommandType
commandType : CommandType,
options?: ApplicationCommandOptionData[],
alias? : string[],
name? : string | undefined
) {
this.name = name;
this.commandType = commandType;
this._name = name;
this._commandType = commandType;
switch ( commandType ) {
case CommandType.TEXT : {
this._alias = alias;
this._options = undefined;
} break;
case CommandType.SLASH : case CommandType.BOTH : {
this._alias = undefined
this._options = options;
} break;
}
}
abstract execute<T> ( args: Ok<T> ) : Awaitable<possibleOutput | void>;
abstract parse?<T> (args: Arg) : Utils.ArgType<T>;
private set ctx ( context: Context ) { this._ctx = context; }
abstract parse?<T> ( args: Arg ) : Utils.ArgType<T>;
public set ctx ( context: Context ) { this._ctx = context; }
public get name () { return this._name; }
public get commandType () { return this._commandType; }
public get options() { return this._options; }
public get alias() { return this._alias }
}

View File

@@ -1,5 +1,4 @@
import type { ApplicationCommandOptionData } from 'discord.js';
import type Module from '../structures/module';
import { readdirSync, statSync } from 'fs';
import { join } from 'path';