mirror of
https://github.com/sern-handler/handler
synced 2026-06-17 05:12:16 +00:00
feat(handler) finish loading command functionality, adjust properties
per cmd type
This commit is contained in:
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user