mirror of
https://github.com/sern-handler/handler
synced 2026-06-22 07:42:14 +00:00
initplugins inject deps, inconspicuos
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { CommandType, EventType, PluginType } from './structures/enums';
|
||||
import type { Plugin, PluginResult, EventArgs, CommandArgs, InitArgs } from '../types/core-plugin';
|
||||
import type { ClientEvents } from 'discord.js';
|
||||
import { CommandType, PluginType } from './structures/enums';
|
||||
import type { Plugin, PluginResult, CommandArgs, InitArgs } from '../types/core-plugin';
|
||||
import { err, ok } from './functions';
|
||||
|
||||
export function makePlugin<V extends unknown[]>(
|
||||
@@ -31,27 +30,7 @@ export function CommandControlPlugin<I extends CommandType>(
|
||||
) {
|
||||
return makePlugin(PluginType.Control, execute);
|
||||
}
|
||||
/**
|
||||
* @since 2.5.0
|
||||
*/
|
||||
export function EventControlPlugin<I extends EventType>(
|
||||
execute: (...args: EventArgs<I>) => PluginResult,
|
||||
) {
|
||||
return makePlugin(PluginType.Control, execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.5.0
|
||||
* @Experimental
|
||||
* A specialized function for creating control plugins with discord.js ClientEvents.
|
||||
* Will probably be moved one day!
|
||||
*/
|
||||
export function DiscordEventControlPlugin<T extends keyof ClientEvents>(
|
||||
name: T,
|
||||
execute: (...args: ClientEvents[T]) => PluginResult,
|
||||
) {
|
||||
return makePlugin(PluginType.Control, execute);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.0.0
|
||||
|
||||
@@ -46,37 +46,31 @@ export function treeSearch(
|
||||
while (_options.length > 0) {
|
||||
const cur = _options.pop()!;
|
||||
switch (cur.type) {
|
||||
case ApplicationCommandOptionType.Subcommand:
|
||||
{
|
||||
case ApplicationCommandOptionType.Subcommand: {
|
||||
subcommands.add(cur.name);
|
||||
for (const option of cur.options ?? []) _options.push(option);
|
||||
}
|
||||
break;
|
||||
case ApplicationCommandOptionType.SubcommandGroup:
|
||||
{
|
||||
} break;
|
||||
case ApplicationCommandOptionType.SubcommandGroup: {
|
||||
for (const command of cur.options ?? []) _options.push(command);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if ('autocomplete' in cur && cur.autocomplete) {
|
||||
const choice = iAutocomplete.options.getFocused(true);
|
||||
assert( 'command' in cur, 'No `command` property found for option ' + cur.name);
|
||||
if (subcommands.size > 0) {
|
||||
const parent = iAutocomplete.options.getSubcommand();
|
||||
const parentAndOptionMatches =
|
||||
subcommands.has(parent) && cur.name === choice.name;
|
||||
if (parentAndOptionMatches) {
|
||||
return { ...cur, parent };
|
||||
}
|
||||
} else {
|
||||
if (cur.name === choice.name) {
|
||||
return { ...cur, parent: undefined };
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
if ('autocomplete' in cur && cur.autocomplete) {
|
||||
const choice = iAutocomplete.options.getFocused(true);
|
||||
assert( 'command' in cur, 'No `command` property found for option ' + cur.name);
|
||||
if (subcommands.size > 0) {
|
||||
const parent = iAutocomplete.options.getSubcommand();
|
||||
const parentAndOptionMatches =
|
||||
subcommands.has(parent) && cur.name === choice.name;
|
||||
if (parentAndOptionMatches) {
|
||||
return { ...cur, parent };
|
||||
}
|
||||
} else {
|
||||
if (cur.name === choice.name) {
|
||||
return { ...cur, parent: undefined };
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ClientEvents } from 'discord.js';
|
||||
import { EventType } from '../core/structures/enums';
|
||||
import type { AnyEventPlugin, } from '../types/core-plugin';
|
||||
import type {
|
||||
InputCommand,
|
||||
InputEvent,
|
||||
@@ -21,18 +20,14 @@ export function commandModule(mod: InputCommand): Module {
|
||||
plugins,
|
||||
} as Module;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.0.0
|
||||
* The wrapper function to define event modules for sern
|
||||
* @param mod
|
||||
*/
|
||||
export function eventModule(mod: InputEvent): Module {
|
||||
const [onEvent, plugins] = partitionPlugins(mod.plugins);
|
||||
return {
|
||||
...mod,
|
||||
plugins,
|
||||
onEvent,
|
||||
} as Module;
|
||||
return mod as Module;
|
||||
}
|
||||
|
||||
/** Create event modules from discord.js client events,
|
||||
@@ -43,7 +38,6 @@ export function eventModule(mod: InputEvent): Module {
|
||||
*/
|
||||
export function discordEvent<T extends keyof ClientEvents>(mod: {
|
||||
name: T;
|
||||
plugins?: AnyEventPlugin[];
|
||||
execute: (...args: ClientEvents[T]) => Awaitable<unknown>;
|
||||
}) {
|
||||
return eventModule({ type: EventType.Discord, ...mod, });
|
||||
|
||||
@@ -24,25 +24,26 @@ function fmt(msg: string, prefix?: string): string[] {
|
||||
* Message and ChatInputCommandInteraction
|
||||
*/
|
||||
export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
|
||||
prefix: string|undefined;
|
||||
|
||||
get options() {
|
||||
return this.interaction.options;
|
||||
}
|
||||
|
||||
args() {
|
||||
return {
|
||||
message: <T = string[]>() => {
|
||||
args(type: 'message'|'interaction', parser?: Function) {
|
||||
switch(type) {
|
||||
case 'message': {
|
||||
const [, ...rest] = fmt(this.message.content, this.prefix);
|
||||
return rest as T;
|
||||
},
|
||||
interaction: () => this.interaction.options
|
||||
return rest;
|
||||
};
|
||||
case 'interaction': {
|
||||
return this.interaction.options;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected constructor(protected ctx: Result<Message, ChatInputCommandInteraction>, prefix?: string) {
|
||||
protected constructor(protected ctx: Result<Message, ChatInputCommandInteraction>,
|
||||
public prefix?: string) {
|
||||
super(ctx);
|
||||
this.prefix = prefix
|
||||
}
|
||||
|
||||
public get id(): Snowflake {
|
||||
@@ -52,9 +53,7 @@ export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
|
||||
}
|
||||
|
||||
public get channel() {
|
||||
return safeUnwrap(this.ctx
|
||||
.map(m => m.channel)
|
||||
.mapErr(i => i.channel));
|
||||
return safeUnwrap(this.ctx.map(m => m.channel).mapErr(i => i.channel));
|
||||
}
|
||||
|
||||
public get channelId(): Snowflake {
|
||||
|
||||
@@ -17,7 +17,7 @@ import * as Id from '../core/id'
|
||||
import type { Emitter } from '../core/interfaces';
|
||||
import { PayloadType, SernError } from '../core/structures/enums'
|
||||
import { Err, Ok, Result } from 'ts-results-es';
|
||||
import type { UnpackedDependencies, VoidResult } from '../types/utility';
|
||||
import type { UnpackedDependencies } from '../types/utility';
|
||||
import type { CommandModule, Module, Processed } from '../types/core-modules';
|
||||
import * as assert from 'node:assert';
|
||||
import { Context } from '../core/structures/context';
|
||||
@@ -36,15 +36,9 @@ interface ExecutePayload {
|
||||
|
||||
function intoPayload(module: Module, deps: Dependencies) {
|
||||
return pipe(map(arrayifySource),
|
||||
map(args => ({ module, args, deps })));
|
||||
map(args => ({ module, args, deps })),
|
||||
map(p => p.args));
|
||||
}
|
||||
const createResult = (deps: Dependencies) =>
|
||||
createResultResolver<unknown[]>({
|
||||
onNext: (p) => p.args,
|
||||
onStop: (module) => {
|
||||
//maybe do something when plugins fail?
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Creates an observable from { source }
|
||||
* @param module
|
||||
@@ -60,7 +54,6 @@ export function eventDispatcher(deps: Dependencies, module: Module, source: unkn
|
||||
//@ts-ignore
|
||||
return fromEvent(source, module.name!)
|
||||
.pipe(intoPayload(module, deps),
|
||||
concatMap(createResult(deps)),
|
||||
execute);
|
||||
}
|
||||
|
||||
@@ -218,7 +211,23 @@ export function createResultResolver<Output>(config: {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export async function callInitPlugins(module: Module, deps: Dependencies, sEmitter?: Emitter) {
|
||||
for(const plugin of module.plugins) {
|
||||
const res = await plugin.execute({
|
||||
module,
|
||||
absPath: module.meta.absPath ,
|
||||
updateModule: (partial: Partial<Module>) => {
|
||||
module = { ...module, ...partial };
|
||||
return module;
|
||||
},
|
||||
deps
|
||||
});
|
||||
if(res.isErr()) {
|
||||
sEmitter?.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
throw Error("Plugin failed with controller.stop()");
|
||||
}
|
||||
}
|
||||
}
|
||||
async function callPlugins({ args, module, deps }: ExecutePayload) {
|
||||
let state = {};
|
||||
for(const plugin of module.onEvent) {
|
||||
|
||||
@@ -17,7 +17,7 @@ function isNonBot(prefix: string) {
|
||||
|
||||
function hasPrefix(prefix: string, content: string) {
|
||||
const prefixInContent = content.slice(0, prefix.length);
|
||||
return (prefixInContent.localeCompare(prefix, undefined, { sensitivity: 'accent' }) === 0);
|
||||
return prefixInContent.localeCompare(prefix, undefined, { sensitivity: 'accent' }) === 0;
|
||||
}
|
||||
|
||||
export default function (
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PayloadType } from '..';
|
||||
import { CommandType, SernError } from '../core/structures/enums';
|
||||
import { Module } from '../types/core-modules';
|
||||
import { UnpackedDependencies } from '../types/utility';
|
||||
import { callInitPlugins } from './event-utils';
|
||||
|
||||
export default async function(dir: string, deps : UnpackedDependencies) {
|
||||
const { '@sern/client': client,
|
||||
@@ -23,20 +24,7 @@ export default async function(dir: string, deps : UnpackedDependencies) {
|
||||
if(!validType) {
|
||||
throw Error(`Found ${module.name} at ${module.meta.absPath}, which has incorrect \`type\``);
|
||||
}
|
||||
for(const plugin of module.plugins) {
|
||||
const res = await plugin.execute({
|
||||
module,
|
||||
absPath: module.meta.absPath ,
|
||||
updateModule: (partial: Partial<Module>) => {
|
||||
module = { ...module, ...partial };
|
||||
return module;
|
||||
}
|
||||
});
|
||||
if(res.isErr()) {
|
||||
sEmitter.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
throw Error("Plugin failed with controller.stop()");
|
||||
}
|
||||
}
|
||||
await callInitPlugins(module, deps, sEmitter);
|
||||
// FREEZE! no more writing!!
|
||||
commands.set(module.meta.id, Object.freeze(module));
|
||||
sEmitter.emit('module.register', resultPayload(PayloadType.Success, module));
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { EventType, PayloadType, SernError } from '../core/structures/enums';
|
||||
import { eventDispatcher, handleCrash } from './event-utils'
|
||||
import { EventType, SernError } from '../core/structures/enums';
|
||||
import { callInitPlugins, eventDispatcher, handleCrash } from './event-utils'
|
||||
import { EventModule, Module } from '../types/core-modules';
|
||||
import * as Files from '../core/module-loading'
|
||||
import type { UnpackedDependencies } from '../types/utility';
|
||||
import { resultPayload } from '../core/functions';
|
||||
import { from, map, mergeAll } from 'rxjs';
|
||||
|
||||
const intoDispatcher = (deps: UnpackedDependencies) =>
|
||||
@@ -29,20 +28,7 @@ export default async function(deps: UnpackedDependencies, eventDir: string) {
|
||||
const eventModules: EventModule[] = [];
|
||||
for await (const path of Files.readRecursive(eventDir)) {
|
||||
let { module } = await Files.importModule<Module>(path);
|
||||
for(const plugin of module.plugins) {
|
||||
const res = await plugin.execute({
|
||||
module,
|
||||
absPath: module.meta.absPath,
|
||||
updateModule: (partial: Partial<Module>) => {
|
||||
module = { ...module, ...partial };
|
||||
return module;
|
||||
}
|
||||
});
|
||||
if(res.isErr()) {
|
||||
deps['@sern/emitter'].emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
throw Error("Plugin failed with controller.stop()");
|
||||
}
|
||||
}
|
||||
await callInitPlugins(module, deps)
|
||||
eventModules.push(module as EventModule);
|
||||
}
|
||||
from(eventModules)
|
||||
|
||||
@@ -32,8 +32,7 @@ export type {
|
||||
InitPlugin,
|
||||
ControlPlugin,
|
||||
Plugin,
|
||||
AnyEventPlugin,
|
||||
AnyCommandPlugin,
|
||||
AnyPlugin,
|
||||
} from './types/core-plugin';
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import type {
|
||||
} from 'discord.js';
|
||||
import type { CommandType, EventType } from '../core/structures/enums';
|
||||
import { Context } from '../core/structures/context'
|
||||
import { AnyCommandPlugin, AnyEventPlugin, ControlPlugin, InitPlugin } from './core-plugin';
|
||||
import { AnyPlugin, ControlPlugin, InitPlugin } from './core-plugin';
|
||||
import { Awaitable, SernEventsMapping } from './utility';
|
||||
|
||||
type ToBeDecided = {
|
||||
@@ -193,12 +193,12 @@ type EventModulesNoPlugins = {
|
||||
};
|
||||
|
||||
export type InputEvent = {
|
||||
[T in EventType]: EventModulesNoPlugins[T] & { plugins?: AnyEventPlugin[] };
|
||||
[T in EventType]: EventModulesNoPlugins[T];
|
||||
}[EventType];
|
||||
|
||||
export type InputCommand = {
|
||||
[T in CommandType]: CommandModuleNoPlugins[T] & {
|
||||
plugins?: AnyCommandPlugin[];
|
||||
plugins?: AnyPlugin[];
|
||||
};
|
||||
}[CommandType];
|
||||
|
||||
|
||||
@@ -16,13 +16,12 @@ import type {
|
||||
Module,
|
||||
Processed,
|
||||
} from './core-modules';
|
||||
import type { Awaitable, Payload } from './utility';
|
||||
import type { CommandType, EventType, PluginType } from '../core/structures/enums'
|
||||
import type { Awaitable } from './utility';
|
||||
import type { CommandType, PluginType } from '../core/structures/enums'
|
||||
import type { Context } from '../core/structures/context'
|
||||
import type {
|
||||
ButtonInteraction,
|
||||
ChannelSelectMenuInteraction,
|
||||
ClientEvents,
|
||||
MentionableSelectMenuInteraction,
|
||||
MessageContextMenuCommandInteraction,
|
||||
ModalSubmitInteraction,
|
||||
@@ -37,6 +36,7 @@ export type PluginResult = Awaitable<Result<unknown, unknown>>;
|
||||
export interface InitArgs<T extends Processed<Module> = Processed<Module>> {
|
||||
module: T;
|
||||
absPath: string;
|
||||
deps: Dependencies
|
||||
updateModule: (module: Partial<T>) => T
|
||||
}
|
||||
export interface Controller {
|
||||
@@ -57,11 +57,9 @@ export interface ControlPlugin<Args extends any[] = any[]> {
|
||||
execute: (...args: Args) => PluginResult;
|
||||
}
|
||||
|
||||
export type AnyCommandPlugin = ControlPlugin | InitPlugin<[InitArgs<Processed<Module>>]>;
|
||||
export type AnyEventPlugin = ControlPlugin | InitPlugin<[InitArgs<Processed<Module>>]>;
|
||||
export type AnyPlugin = ControlPlugin | InitPlugin<[InitArgs<Processed<Module>>]>;
|
||||
|
||||
export type CommandArgs<I extends CommandType = CommandType > = CommandArgsMatrix[I]
|
||||
export type EventArgs<I extends EventType = EventType> = EventArgsMatrix[I]
|
||||
|
||||
interface CommandArgsMatrix {
|
||||
[CommandType.Text]: [Context];
|
||||
@@ -77,10 +75,3 @@ interface CommandArgsMatrix {
|
||||
[CommandType.UserSelect]: [UserSelectMenuInteraction];
|
||||
[CommandType.Modal]: [ModalSubmitInteraction];
|
||||
}
|
||||
|
||||
interface EventArgsMatrix {
|
||||
[EventType.Discord]: ClientEvents[keyof ClientEvents];
|
||||
[EventType.Sern]: [Payload];
|
||||
[EventType.External]: unknown[];
|
||||
[EventType.Cron]: unknown[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user