mirror of
https://github.com/sern-handler/handler
synced 2026-06-10 09:52:14 +00:00
feat(handler) more plugin work, refactoring rxjs pipes
This commit is contained in:
@@ -62,7 +62,7 @@ export const onInteractionCreate = ( wrapper : Wrapper ) => {
|
||||
},
|
||||
next(command) {
|
||||
//log on each command emitted
|
||||
console.log(command?.name);
|
||||
console.log(command);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,22 +5,24 @@ import { Observable, throwError } from 'rxjs';
|
||||
import type { ModuleDefs } from '../structures/modules/commands/moduleHandler';
|
||||
import { SernError } from '../structures/errors';
|
||||
import { isNotFromBot } from '../utilities/messageHelpers';
|
||||
import type { PluggedModule } from '../structures/modules/module';
|
||||
import type { SernPlugin } from '../plugins/plugin';
|
||||
|
||||
export function match(mod: Module | undefined, type : CommandType) : boolean {
|
||||
return mod !== undefined && (mod.type & type) != 0;
|
||||
export function match(plug: PluggedModule | undefined, type : CommandType) : boolean {
|
||||
return plug !== undefined && (plug.mod.type & type) != 0;
|
||||
}
|
||||
export function filterTap<T extends keyof ModuleDefs>(
|
||||
cmdType : T,
|
||||
tap: (mod : ModuleDefs[T]) => Awaitable<void>
|
||||
tap: (mod : ModuleDefs[T], plugins : SernPlugin[]) => Awaitable<void>
|
||||
) {
|
||||
return (src : Observable<Module|undefined>) =>
|
||||
new Observable<Module|undefined>( subscriber => {
|
||||
return (src : Observable<PluggedModule|undefined>) =>
|
||||
new Observable<PluggedModule|undefined>( subscriber => {
|
||||
return src.subscribe({
|
||||
next(modul) {
|
||||
if(match(modul, cmdType)) {
|
||||
const asModT = <ModuleDefs[T]> modul;
|
||||
tap(asModT);
|
||||
subscriber.next(asModT);
|
||||
const asModT = <ModuleDefs[T]> modul!.mod;
|
||||
tap(asModT, modul!.plugins);
|
||||
subscriber.next(modul);
|
||||
} else {
|
||||
if (modul === undefined) {
|
||||
return throwError(() => SernError.UndefinedModule);
|
||||
|
||||
@@ -1,102 +1,84 @@
|
||||
import { concatMap, first, from, fromEvent, map, tap } from 'rxjs';
|
||||
import { basename } from 'path';
|
||||
import { from, fromEvent, map, take,concat, concatAll, mergeMap, skip} from 'rxjs'; import { basename } from 'path';
|
||||
import * as Files from '../utilities/readFile';
|
||||
import type Wrapper from '../structures/wrapper';
|
||||
import type { HandlerCallback, ModuleHandlers, ModuleStates, ModuleType } from '../structures/modules/commands/moduleHandler';
|
||||
import { CommandType } from '../sern';
|
||||
import type { PluggedModule } from '../structures/modules/module';
|
||||
import type { CommandPlugin, SernPlugin } from '../plugins/plugin';
|
||||
import { partition } from './observableHandling';
|
||||
import type { Client } from 'discord.js';
|
||||
import { Err, Ok } from 'ts-results';
|
||||
import type { PluggedModule } from '../structures/modules/module';
|
||||
|
||||
export const onReady = ( wrapper : Wrapper ) => {
|
||||
const { client, commands } = wrapper;
|
||||
fromEvent(client, 'ready')
|
||||
.pipe(
|
||||
take(1),
|
||||
concatMap ( _ => {
|
||||
|
||||
|
||||
|
||||
})
|
||||
)
|
||||
.subscribe(() => {
|
||||
Files.buildData( commands )
|
||||
.then( deployCommands(client) );
|
||||
})
|
||||
};
|
||||
const ready$ = fromEvent(client, 'ready').pipe(take(1));
|
||||
const processCommandFiles$ = from(Files.buildData(commands)).pipe(
|
||||
concatAll(),
|
||||
map(({plugged, absPath}) => {
|
||||
const name = plugged.mod.name ?? Files.fmtFileName(basename(absPath));
|
||||
if (plugged.mod.name === undefined ) {
|
||||
return { mod: { name, ...plugged.mod }, plugins : plugged.plugins };
|
||||
}
|
||||
return plugged;
|
||||
}),
|
||||
mergeMap(({ mod, plugins: allPlugins }) => {
|
||||
const [ cmdPlugins, plugins ] = partition(allPlugins, isCmdPlugin);
|
||||
return cmdPlugins.map(pl => {
|
||||
const res = pl.execute(client, mod, {
|
||||
next: () => Ok.EMPTY,
|
||||
stop: () => Err.EMPTY
|
||||
})
|
||||
return { res, plugged : <PluggedModule>{ mod, plugins } }
|
||||
})
|
||||
}),
|
||||
);
|
||||
concat(ready$.pipe(skip(1)),processCommandFiles$)
|
||||
.subscribe( _ => {
|
||||
if(a.ok) {
|
||||
registerModule(mod.name!, mod, plugins)
|
||||
} else {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Refactor : ? Possibly repetitive and verbose.
|
||||
const handler = ( name : string ) =>
|
||||
({
|
||||
[CommandType.Text] : mod => {
|
||||
mod.alias.forEach ( a => Files.Alias.set(a,mod));
|
||||
Files.Commands.set( name, mod );
|
||||
[CommandType.Text] : (mod, plugins) => {
|
||||
mod.alias.forEach ( a => Files.Alias.set(a,{ mod, plugins}));
|
||||
Files.Commands.set( name, { mod, plugins } );
|
||||
},
|
||||
[CommandType.Slash]: mod => {
|
||||
Files.Commands.set( name , mod);
|
||||
[CommandType.Slash]: (mod, plugins) => {
|
||||
Files.Commands.set( name , { mod, plugins });
|
||||
},
|
||||
[CommandType.Both] : mod => {
|
||||
Files.Commands.set ( name, mod);
|
||||
mod.alias.forEach (a => Files.Alias.set(a, mod));
|
||||
[CommandType.Both] :( mod, plugins )=> {
|
||||
Files.Commands.set ( name,{ mod, plugins});
|
||||
mod.alias.forEach (a => Files.Alias.set(a, {mod,plugins}));
|
||||
},
|
||||
[CommandType.MenuUser] : mod => {
|
||||
Files.ContextMenuUser.set ( name, mod );
|
||||
[CommandType.MenuUser] : (mod, plugins) => {
|
||||
Files.ContextMenuUser.set ( name, {mod, plugins} );
|
||||
},
|
||||
[CommandType.MenuMsg] : mod => {
|
||||
Files.ContextMenuMsg.set (name, mod );
|
||||
[CommandType.MenuMsg] : (mod,plugins) => {
|
||||
Files.ContextMenuMsg.set (name, {mod, plugins} );
|
||||
},
|
||||
[CommandType.Button] : mod => {
|
||||
Files.Buttons.set(name, mod);
|
||||
[CommandType.Button] : (mod,plugins) => {
|
||||
Files.Buttons.set(name, {mod, plugins});
|
||||
},
|
||||
[CommandType.MenuSelect] : mod => {
|
||||
Files.SelectMenus.set(name, mod);
|
||||
[CommandType.MenuSelect] : ( mod, plugins ) => {
|
||||
Files.SelectMenus.set(name, { mod, plugins });
|
||||
},
|
||||
} as ModuleHandlers);
|
||||
|
||||
const registerModules = <T extends ModuleType >(name : string, mod : ModuleStates[T]) =>
|
||||
(<HandlerCallback<T>> handler(name)[mod.type])(mod);
|
||||
|
||||
function setCommands ( plugged : PluggedModule ) {
|
||||
registerModules(plugged.mod.name!, plugged.mod);
|
||||
}
|
||||
|
||||
function deployCommands (wrapper : Client) {
|
||||
|
||||
return function (arr : { plugged : PluggedModule, absPath : string}[]) {
|
||||
from(arr)
|
||||
.pipe(
|
||||
map (({plugged, absPath}) => {
|
||||
const name = plugged.mod.name ?? Files.fmtFileName(basename(absPath));
|
||||
if (plugged.mod.name === undefined ) {
|
||||
return { mod: { name, ...plugged.mod }, plugins : plugged.plugins };
|
||||
}
|
||||
return plugged;
|
||||
}),
|
||||
concatMap( ({ plugins, mod} ) => {
|
||||
const [ cmdPlugins, eventPlugins ] = partition(plugins, isCmdPlugin);
|
||||
|
||||
return from(cmdPlugins)
|
||||
.pipe(
|
||||
|
||||
|
||||
)
|
||||
}),
|
||||
tap (plug => deployPlugins(plug, wrapper)),
|
||||
tap ( setCommands ),
|
||||
).subscribe ( );
|
||||
}
|
||||
function registerModule <T extends ModuleType> (
|
||||
name : string,
|
||||
mod : ModuleStates[T],
|
||||
plugins : SernPlugin[]
|
||||
) {
|
||||
return (<HandlerCallback<T>> handler(name)[mod.type])(mod, plugins);
|
||||
}
|
||||
|
||||
function isCmdPlugin ( p : SernPlugin) : p is CommandPlugin {
|
||||
return (p.type & 0) !== 0;
|
||||
}
|
||||
|
||||
// 0b0
|
||||
// 0b0
|
||||
function deployPlugins(plugged: PluggedModule, client : Client) {
|
||||
const { plugins, mod } = plugged;
|
||||
const [ cmdPlugins, eventPlugins ] = partition(plugins, isCmdPlugin)
|
||||
|
||||
}
|
||||
|
||||
|
||||
23
src/handler/plugins/defaultPlugins.ts
Normal file
23
src/handler/plugins/defaultPlugins.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { Client } from "discord.js";
|
||||
import type { Module } from "../..";
|
||||
import { CommandPlugin, Controller, PluginType } from "./plugin";
|
||||
|
||||
export function reload(
|
||||
data : { guildId: string, applicationId: string }
|
||||
) : CommandPlugin {
|
||||
|
||||
return {
|
||||
type : PluginType.Command,
|
||||
name : 'Refresh',
|
||||
description : 'Will reload the command this plugin is applied to',
|
||||
async execute(client : Client, module: Module, controller : Controller ) {
|
||||
const curGuild = await client.guilds.fetch(data.guildId);
|
||||
await curGuild.commands.edit(data.applicationId, {
|
||||
name : module.name!,
|
||||
description: module.description,
|
||||
})
|
||||
return controller.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,19 +10,18 @@
|
||||
// categorizing commands, cooldowns, permissions, etc
|
||||
// Plugins are reminisce of middleware in express.
|
||||
//
|
||||
//
|
||||
|
||||
import type { Client } from "discord.js";
|
||||
import type { Awaitable, Client } from "discord.js";
|
||||
import type { Err, Ok, Result } from "ts-results";
|
||||
import type { Override, Wrapper } from "../..";
|
||||
import type { BaseModule } from "../structures/modules/module";
|
||||
import type { Module, Override, Wrapper } from "../..";
|
||||
import type { BaseModule, PluggedModule } from "../structures/modules/module";
|
||||
|
||||
export enum PluginType {
|
||||
Command = 0b00,
|
||||
Event = 0b01
|
||||
}
|
||||
|
||||
interface Controller {
|
||||
export interface Controller {
|
||||
next : () => Ok<void>,
|
||||
stop : () => Err<void>
|
||||
|
||||
@@ -37,7 +36,9 @@ interface BasePlugin extends Override<BaseModule, executeCmdPlugin>{
|
||||
export type CommandPlugin = {
|
||||
type : PluginType.Command
|
||||
} & Override<BasePlugin, {
|
||||
execute : (wrapper:Client, controller:Controller) => Result<void,void>
|
||||
execute : (
|
||||
wrapper:Client, module : Module, controller:Controller
|
||||
) => Awaitable<Result<void,void>>
|
||||
}>;
|
||||
|
||||
export type EventPlugin = {
|
||||
@@ -48,7 +49,25 @@ export type SernPlugin =
|
||||
CommandPlugin
|
||||
| EventPlugin;
|
||||
|
||||
|
||||
export function commmand(plug : CommandPlugin) {
|
||||
return plug;
|
||||
}
|
||||
|
||||
export function event(plug : EventPlugin) {
|
||||
return plug;
|
||||
}
|
||||
|
||||
export function apply(...plugins: SernPlugin[]) {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export function sernModule
|
||||
(plugins : SernPlugin[], mod : Module ) : PluggedModule {
|
||||
return {
|
||||
mod,
|
||||
plugins
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type { ApplicationCommandOptionData, AutocompleteInteraction, Awaitable, ButtonInteraction, ContextMenuCommandInteraction, MessageContextMenuCommandInteraction, SelectMenuInteraction } from 'discord.js';
|
||||
import type { ApplicationCommandOptionData, Awaitable, ButtonInteraction, ContextMenuCommandInteraction, MessageContextMenuCommandInteraction, SelectMenuInteraction } from 'discord.js';
|
||||
import type { Override } from '../../../../types/handler';
|
||||
import type { CommandType } from '../../../sern';
|
||||
import type { BaseModule } from '../module';
|
||||
|
||||
|
||||
type AutoComp = {
|
||||
update : (ctx : AutocompleteInteraction) => Awaitable<void>
|
||||
}
|
||||
|
||||
//possible refactoring to interfaces and not types
|
||||
export type TextCommand = {
|
||||
type : CommandType.Text;
|
||||
@@ -38,12 +36,12 @@ export type SelectMenuCommand = {
|
||||
} & Override<BaseModule, { execute : (ctx : SelectMenuInteraction ) => Awaitable<void> }>;
|
||||
|
||||
|
||||
export type Module =
|
||||
export type Module = (
|
||||
TextCommand
|
||||
| SlashCommand
|
||||
| BothCommand
|
||||
| ContextMenuUser
|
||||
| ContextMenuMsg
|
||||
| ButtonCommand
|
||||
| SelectMenuCommand;
|
||||
|
||||
| SelectMenuCommand
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { EventPlugin, SernPlugin } from '../../../plugins/plugin';
|
||||
import { CommandType } from '../../../sern';
|
||||
import type {
|
||||
TextCommand,
|
||||
@@ -9,24 +10,25 @@ import type {
|
||||
SelectMenuCommand
|
||||
} from './module';
|
||||
//https://stackoverflow.com/questions/64092736/alternative-to-switch-statement-for-typescript-discriminated-union
|
||||
|
||||
// Explicit Module Definitions for mapping
|
||||
export type ModuleDefs = {
|
||||
[CommandType.Text] : TextCommand,
|
||||
[CommandType.Slash] : SlashCommand,
|
||||
[CommandType.Both] : BothCommand,
|
||||
[CommandType.MenuMsg] : ContextMenuMsg,
|
||||
[CommandType.MenuUser] : ContextMenuUser,
|
||||
[CommandType.Button] : ButtonCommand,
|
||||
[CommandType.MenuSelect] : SelectMenuCommand,
|
||||
[CommandType.Text] : TextCommand;
|
||||
[CommandType.Slash] : SlashCommand;
|
||||
[CommandType.Both] : BothCommand;
|
||||
[CommandType.MenuMsg] : ContextMenuMsg;
|
||||
[CommandType.MenuUser] : ContextMenuUser;
|
||||
[CommandType.Button] : ButtonCommand;
|
||||
[CommandType.MenuSelect] : SelectMenuCommand;
|
||||
}
|
||||
|
||||
//Keys of ModuleDefs
|
||||
export type ModuleType = keyof ModuleDefs;
|
||||
// The keys mapped to a constructed union with its type
|
||||
export type ModuleStates = { [ K in ModuleType ] : { type : K } & ModuleDefs[K] };
|
||||
export type ModuleStates = {
|
||||
[ K in ModuleType ] : { type : K } & ModuleDefs[K] };
|
||||
// A handler callback that is called on each ModuleDef
|
||||
export type HandlerCallback<K extends ModuleType> = ( params : ModuleStates[K] ) => unknown;
|
||||
export type HandlerCallback<K extends ModuleType> =
|
||||
( mod: ModuleStates[K], plugins : SernPlugin[] ) => unknown;
|
||||
//An object that acts as the mapped object to handler
|
||||
export type ModuleHandlers = { [K in ModuleType] : HandlerCallback<K> };
|
||||
|
||||
|
||||
@@ -14,22 +14,4 @@ export interface PluggedModule {
|
||||
plugins : SernPlugin[];
|
||||
}
|
||||
|
||||
export function commmand ( plug : CommandPlugin ) {
|
||||
return plug;
|
||||
}
|
||||
|
||||
export function event( plug : EventPlugin ) {
|
||||
return plug;
|
||||
}
|
||||
|
||||
export function apply(...plugins: SernPlugin[]) {
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export function sernModule
|
||||
(plugins : SernPlugin[], mod : Module, ) : PluggedModule {
|
||||
return {
|
||||
mod,
|
||||
plugins
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import type { Module } from '../structures/modules/commands/module';
|
||||
import { SernError } from '../structures/errors';
|
||||
import type { PluggedModule } from '../structures/modules/module';
|
||||
|
||||
//We can look into lazily loading modules once everything is set
|
||||
export const ContextMenuUser = new Map<string, Module>();
|
||||
export const ContextMenuMsg = new Map<string, Module>();
|
||||
export const Commands = new Map<string, Module>();
|
||||
export const Alias = new Map<string, Module>();
|
||||
export const Buttons = new Map<string, Module>();
|
||||
export const SelectMenus = new Map<string, Module>();
|
||||
export const ContextMenuUser = new Map<string, PluggedModule>();
|
||||
export const ContextMenuMsg = new Map<string, PluggedModule>();
|
||||
export const Commands = new Map<string, PluggedModule>();
|
||||
export const Alias = new Map<string, PluggedModule>();
|
||||
export const Buttons = new Map<string, PluggedModule>();
|
||||
export const SelectMenus = new Map<string, PluggedModule>();
|
||||
|
||||
|
||||
// Courtesy @Townsy45
|
||||
@@ -40,8 +39,7 @@ export async function buildData(commandDir: string ): Promise<
|
||||
{
|
||||
plugged: PluggedModule;
|
||||
absPath: string;
|
||||
}[]
|
||||
> {
|
||||
}[]> {
|
||||
return Promise.all(
|
||||
getCommands(commandDir).map( async (absPath) => {
|
||||
const plugged = <PluggedModule> (await import(absPath)).module;
|
||||
|
||||
Reference in New Issue
Block a user