mirror of
https://github.com/sern-handler/handler
synced 2026-06-20 23:02:15 +00:00
Matched the case of Utilities
caused import error
This commit is contained in:
@@ -1,23 +1,12 @@
|
||||
import * as Files from './utilities/readFile';
|
||||
import type * as Utils from './utilities/preprocessors/args';
|
||||
import * as Files from './Utilities/readFile';
|
||||
import type * as Utils from './Utilities/Preprocessors/args';
|
||||
|
||||
import type {
|
||||
Arg,
|
||||
Context,
|
||||
Visibility,
|
||||
possibleOutput
|
||||
} from '../types/handler';
|
||||
import type { Arg, Context, Visibility, possibleOutput } from '../types/handler';
|
||||
|
||||
import type {
|
||||
ApplicationCommandOptionData,
|
||||
Awaitable,
|
||||
Client,
|
||||
CommandInteraction,
|
||||
Message
|
||||
} from 'discord.js';
|
||||
import type { ApplicationCommandOptionData, Awaitable, Client, CommandInteraction, Message } from 'discord.js';
|
||||
|
||||
import { Ok, Result, None, Some } from 'ts-results';
|
||||
import { isBot, hasPrefix, fmt } from './utilities/messageHelpers';
|
||||
import { isBot, hasPrefix, fmt } from './Utilities/messageHelpers';
|
||||
import Logger from './logger';
|
||||
|
||||
/**
|
||||
@@ -25,221 +14,226 @@ import Logger from './logger';
|
||||
*/
|
||||
|
||||
export class Handler {
|
||||
private wrapper: Wrapper;
|
||||
private wrapper: Wrapper;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Wrapper} wrapper The data that is required to run sern handler
|
||||
*/
|
||||
|
||||
constructor(
|
||||
wrapper: Wrapper,
|
||||
) {
|
||||
this.wrapper = wrapper;
|
||||
this.client
|
||||
|
||||
/**
|
||||
* On ready, builds command data and registers them all
|
||||
* from command directory
|
||||
**/
|
||||
|
||||
.on('ready', async () => {
|
||||
Files.buildData(this)
|
||||
.then(data => this.registerModules(data))
|
||||
if (wrapper.init !== undefined) wrapper.init(this);
|
||||
new Logger().tableRam()
|
||||
})
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Wrapper} wrapper The data that is required to run sern handler
|
||||
*/
|
||||
|
||||
.on('messageCreate', async (message: Message) => {
|
||||
if (isBot(message) || !hasPrefix(message, this.prefix)) return;
|
||||
if (message.channel.type === 'DM') return; // TODO: Handle dms
|
||||
constructor(wrapper: Wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
this.client
|
||||
|
||||
const tryFmt = fmt(message, this.prefix)
|
||||
const commandName = tryFmt.shift()!;
|
||||
const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName)
|
||||
if (module === undefined) {
|
||||
message.channel.send('Unknown legacy command')
|
||||
return;
|
||||
}
|
||||
const cmdResult = (await this.commandResult(module, message, tryFmt.join(' ')))
|
||||
if (cmdResult === undefined) return;
|
||||
/**
|
||||
* On ready, builds command data and registers them all
|
||||
* from command directory
|
||||
**/
|
||||
|
||||
message.channel.send(cmdResult);
|
||||
.on('ready', async () => {
|
||||
Files.buildData(this).then((data) => this.registerModules(data));
|
||||
if (wrapper.init !== undefined) wrapper.init(this);
|
||||
new Logger().tableRam();
|
||||
})
|
||||
|
||||
})
|
||||
.on('messageCreate', async (message: Message) => {
|
||||
if (isBot(message) || !hasPrefix(message, this.prefix)) return;
|
||||
if (message.channel.type === 'DM') return; // TODO: Handle dms
|
||||
|
||||
.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isCommand()) return;
|
||||
const module = Files.Commands.get(interaction.commandName);
|
||||
const res = await this.interactionResult(module, interaction);
|
||||
if (res === undefined) return;
|
||||
await interaction.reply(res);
|
||||
});
|
||||
const tryFmt = fmt(message, this.prefix);
|
||||
const commandName = tryFmt.shift()!;
|
||||
const module = Files.Commands.get(commandName) ?? Files.Alias.get(commandName);
|
||||
if (module === undefined) {
|
||||
message.channel.send('Unknown legacy command');
|
||||
return;
|
||||
}
|
||||
const cmdResult = await this.commandResult(module, message, tryFmt.join(' '));
|
||||
if (cmdResult === undefined) return;
|
||||
|
||||
message.channel.send(cmdResult);
|
||||
})
|
||||
|
||||
.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isCommand()) return;
|
||||
const module = Files.Commands.get(interaction.commandName);
|
||||
const res = await this.interactionResult(module, interaction);
|
||||
if (res === undefined) return;
|
||||
await interaction.reply(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Files.CommandVal | undefined} 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 | undefined,
|
||||
interaction: CommandInteraction,
|
||||
): Promise<possibleOutput | undefined> {
|
||||
if (module === undefined) return 'Unknown slash command!';
|
||||
const name = Array.from(Files.Commands.keys()).find((it) => it === interaction.commandName);
|
||||
if (name === undefined) return `Could not find ${interaction.commandName} command!`;
|
||||
|
||||
if (module.mod.type < CommandType.SLASH) return 'This is not a slash command';
|
||||
|
||||
const context = { message: None, interaction: Some(interaction) };
|
||||
const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok('');
|
||||
|
||||
if (parsedArgs.err) return parsedArgs.val;
|
||||
|
||||
return (await module.mod.delegate(context, parsedArgs))?.val;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Files.CommandVal | undefined} 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 | undefined,
|
||||
message: Message,
|
||||
args: string,
|
||||
): Promise<possibleOutput | undefined> {
|
||||
if (module?.mod === undefined) return 'Unknown legacy command';
|
||||
if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command`;
|
||||
if (module.mod.visibility === 'private') {
|
||||
const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test;
|
||||
if (checkIsTestServer === undefined)
|
||||
return 'This command has the private modifier but is not registered under Handler#privateServers';
|
||||
if (checkIsTestServer !== module.mod.test) {
|
||||
const msg = `This command is only available on test servers.`; // TODO: Customizable private message
|
||||
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
const context = {
|
||||
message: Some(message),
|
||||
interaction: None,
|
||||
};
|
||||
const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok('');
|
||||
if (parsedArgs.err) return parsedArgs.val;
|
||||
return (await module.mod.delegate(context, parsedArgs))?.val;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Files.CommandVal | undefined} 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 | undefined,
|
||||
interaction: CommandInteraction): Promise<possibleOutput | undefined> {
|
||||
/**
|
||||
* This function chains `Files.buildData`
|
||||
* @param {{name: string, mod: Module<unknown>, absPath: string}} modArr module information
|
||||
*/
|
||||
|
||||
if (module === undefined) return 'Unknown slash command!';
|
||||
const name = Array.from(Files.Commands.keys()).find(it => it === interaction.commandName);
|
||||
if (name === undefined) return `Could not find ${interaction.commandName} command!`;
|
||||
|
||||
if (module.mod.type < CommandType.SLASH) return 'This is not a slash command';
|
||||
|
||||
const context = { message: None, interaction: Some(interaction) }
|
||||
const parsedArgs = module.mod.parse?.(context, ['slash', interaction.options]) ?? Ok('');
|
||||
|
||||
if (parsedArgs.err) return parsedArgs.val;
|
||||
|
||||
return (await module.mod.delegate(context, parsedArgs))?.val;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Files.CommandVal | undefined} 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 | undefined, message: Message, args: string): Promise<possibleOutput | undefined> {
|
||||
if (module?.mod === undefined) return 'Unknown legacy command';
|
||||
if (module.mod.type === CommandType.SLASH) return `This may be a slash command and not a legacy command`
|
||||
if (module.mod.visibility === 'private') {
|
||||
const checkIsTestServer = this.privateServers.find(({ id }) => id === message.guildId!)?.test;
|
||||
if (checkIsTestServer === undefined) return 'This command has the private modifier but is not registered under Handler#privateServers';
|
||||
if (checkIsTestServer !== module.mod.test) {
|
||||
const msg = `This command is only available on test servers.`; // TODO: Customizable private message
|
||||
|
||||
return msg;
|
||||
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, options: [] });
|
||||
break;
|
||||
case 2:
|
||||
case 1 | 2:
|
||||
{
|
||||
const options = (await import(absPath)).options as ApplicationCommandOptionData[];
|
||||
Files.Commands.set(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, options: [] });
|
||||
}
|
||||
const context = {
|
||||
message: Some(message),
|
||||
interaction: None
|
||||
}
|
||||
const parsedArgs = module.mod.parse?.(context, ['text', args]) ?? Ok('');
|
||||
if (parsedArgs.err) return parsedArgs.val;
|
||||
return (await module.mod.delegate(context, parsedArgs))?.val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, options: [] }); break;
|
||||
case 2:
|
||||
case (1 | 2): {
|
||||
const options = ((await import(absPath)).options as ApplicationCommandOptionData[])
|
||||
Files.Commands.set(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.`);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {string} cmdName name of command
|
||||
* @param {string} description description of command
|
||||
* @param {ApplicationCommandOptionData[]} options any options for the slash command
|
||||
*/
|
||||
|
||||
if (mod.alias.length > 0) {
|
||||
for (const alias of mod.alias) {
|
||||
Files.Alias.set(alias, { mod, options: [] })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
/**
|
||||
*
|
||||
* @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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
guild.commands.create({
|
||||
name: cmdName,
|
||||
description,
|
||||
options
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @readonly
|
||||
* @returns {string} The prefix used for legacy commands
|
||||
*/
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @returns {string} The prefix used for legacy commands
|
||||
*/
|
||||
|
||||
get prefix(): string {
|
||||
return this.wrapper.prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @returns {string} Directory of the commands folder
|
||||
*/
|
||||
|
||||
get commandDir(): string {
|
||||
return this.wrapper.commands;
|
||||
}
|
||||
get prefix(): string {
|
||||
return this.wrapper.prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object to be passed into Sern.Handler constructor.
|
||||
* An object to be passed into Sern.Handler constructor.
|
||||
* @typedef {object} Wrapper
|
||||
* @property {readonly Client} client
|
||||
* @property {readonly string} prefix
|
||||
@@ -248,15 +242,15 @@ export class Handler {
|
||||
* @property {readonly {test: boolean, id: string}[]} privateServers
|
||||
*/
|
||||
export interface Wrapper {
|
||||
readonly client: Client,
|
||||
readonly prefix: string,
|
||||
readonly commands: string
|
||||
init?: (handler: Handler) => void,
|
||||
readonly privateServers: { test: boolean, id: string }[],
|
||||
readonly client: Client;
|
||||
readonly prefix: string;
|
||||
readonly commands: string;
|
||||
init?: (handler: Handler) => void;
|
||||
readonly privateServers: { test: boolean; id: string }[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that gets imported and acts as a command.
|
||||
* An object that gets imported and acts as a command.
|
||||
* @typedef {object} Module<T=string>
|
||||
* @property {string} desc
|
||||
* @property {Visibility} visibility
|
||||
@@ -266,13 +260,13 @@ export interface Wrapper {
|
||||
*/
|
||||
|
||||
export interface Module<T = string> {
|
||||
alias: string[],
|
||||
desc: string,
|
||||
visibility: Visibility,
|
||||
type: CommandType,
|
||||
test : boolean,
|
||||
delegate: (eventParams: Context, args: Ok<T>) => Awaitable<Result<possibleOutput, string> | void>
|
||||
parse?: (ctx: Context, args: Arg) => Utils.ArgType<T>
|
||||
alias: string[];
|
||||
desc: string;
|
||||
visibility: Visibility;
|
||||
type: CommandType;
|
||||
test: boolean;
|
||||
delegate: (eventParams: Context, args: Ok<T>) => Awaitable<Result<possibleOutput, string> | void>;
|
||||
parse?: (ctx: Context, args: Arg) => Utils.ArgType<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user