feat: broadening EventPlugin default generic type, reformat with prettier

This commit is contained in:
jacoobes
2022-05-13 14:33:17 -05:00
parent 6462b4ca60
commit 88dcdee818
9 changed files with 329 additions and 330 deletions

View File

@@ -3,5 +3,6 @@
"trailingComma": "all",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 4
"tabWidth": 4,
"arrowParens": "avoid"
}

View File

@@ -1,50 +1,78 @@
import type { ApplicationCommandType, ChatInputCommandInteraction, CommandInteraction, Interaction } from 'discord.js';
import { fromEvent, Observable, of, concatMap, map, throwError } from 'rxjs';
import type {
ChatInputCommandInteraction,
CommandInteraction,
Interaction,
MessageContextMenuCommandInteraction as MessageCtxInt,
UserContextMenuCommandInteraction as UserCtxInt,
} from 'discord.js';
import { concatMap, fromEvent, Observable, of, throwError } from 'rxjs';
import type Wrapper from '../structures/wrapper';
import * as Files from '../utilities/readFile';
import { isEventPlugin } from './readyEvent';
import { P, match } from 'ts-pattern';
import { match, P } from 'ts-pattern';
import { SernError } from '../structures/errors';
import { correctModuleType } from './observableHandling';
import Context from '../structures/context';
import type { Result } from 'ts-results';
import type { PluggedModule } from '../structures/modules/module';
import { CommandType, controller } from '../sern';
import type { EventPlugin } from '../plugins/plugin';
function applicationCommandHandler<
T extends CommandType.Both | CommandType.MenuUser | CommandType.MenuMsg | CommandType.Slash,
>(mod: PluggedModule | undefined, interaction: CommandInteraction) {
if (mod === undefined) {
return throwError(() => SernError.UndefinedModule);
}
const eventPlugins = mod.plugins.filter(isEventPlugin);
return match(interaction)
.when(
i => i.isChatInputCommand(),
(i: ChatInputCommandInteraction) => {
const ctx = Context.wrap(i);
const res = eventPlugins.map((e: EventPlugin) => {
if (![CommandType.Slash, CommandType.Both].includes(e.modType)) {
return throwError(() => SernError.NonValidModuleType);
}
return e.execute([ctx, ['slash', i.options]], controller);
}) as Awaited<Result<void, void>>[];
//Possible unsafe cast
// could result in the promises not being resolved
return of({ res, mod, ctx });
},
)
.when(
() => P._,
(i: MessageCtxInt | UserCtxInt) => {
// const res = eventPlugins.map(e => {
//
//
// });
return of({});
},
)
.run();
}
export const onInteractionCreate = (wrapper: Wrapper) => {
const { client } = wrapper;
export const onInteractionCreate = ( wrapper : Wrapper ) => {
const { client } = wrapper;
const interactionEvent$ = <Observable<Interaction>>fromEvent(client, 'interactionCreate');
const interactionEvent$ = (<Observable<Interaction>> fromEvent(client, 'interactionCreate'));
interactionEvent$
.pipe(
concatMap(interaction => {
if (interaction.isCommand()) {
const modul =
Files.ApplicationCommandStore[interaction.commandType].get(interaction.commandName) ??
Files.BothCommand.get(interaction.commandName);
return applicationCommandHandler(modul, interaction);
}
return of({});
}),
)
.subscribe(console.log);
interactionEvent$.pipe(
concatMap( interaction => {
if(interaction.isCommand()) {
const modul =
Files.ApplicationCommandStore[interaction.commandType].get(interaction.commandName)
?? Files.BothCommand.get(interaction.commandName);
return of(modul).pipe(
map ( plug => {
console.log('a');
if(plug === undefined) {
return throwError(() => SernError.UndefinedModule);
}
const eventPlugins = plug.plugins.filter(isEventPlugin);
match(interaction)
.when(i => i.isChatInputCommand(), (i : ChatInputCommandInteraction) => {
console.log('chatI', eventPlugins);
})
.when(() => P._, i => {
console.log('other I', eventPlugins);
});
})
);
}
return of(null);
})
).subscribe(console.log);
/** concatMap (async interaction => {
/** concatMap (async interaction => {
if (interaction.isChatInputCommand()) {
return of(Files.Commands.get(interaction.commandName))
.pipe(
@@ -89,7 +117,7 @@ export const onInteractionCreate = ( wrapper : Wrapper ) => {
else return of();
})
).subscribe({
).subscribe({
error(e){
throw e;
},
@@ -98,6 +126,5 @@ export const onInteractionCreate = ( wrapper : Wrapper ) => {
//console.log(command);
},
});
**/
**/
};

View File

@@ -1,64 +1,64 @@
import type { Message } from 'discord.js';
import { fromEvent, Observable, of, concatMap, map, from, filter, concatAll, tap } from 'rxjs';
import { Err, Ok } from 'ts-results';
import { concatMap, from, fromEvent, map, Observable, of } from 'rxjs';
import { Err } from 'ts-results';
import type { Args } from '../..';
import { CommandType } from '../sern';
import { CommandType, controller } from '../sern';
import Context from '../structures/context';
import type Wrapper from '../structures/wrapper';
import { fmt } from '../utilities/messageHelpers';
import * as Files from '../utilities/readFile';
import { filterCorrectModule, ignoreNonBot, correctModuleType } from './observableHandling';
import { filterCorrectModule, ignoreNonBot } from './observableHandling';
import { isEventPlugin } from './readyEvent';
export const onMessageCreate = (wrapper : Wrapper) => {
export const onMessageCreate = (wrapper: Wrapper) => {
const { client, defaultPrefix } = wrapper;
if (defaultPrefix === undefined) return;
const messageEvent$ = (<Observable<Message>> fromEvent( client, 'messageCreate'));
const messageEvent$ = <Observable<Message>>fromEvent(client, 'messageCreate');
const processMessage$ = messageEvent$.pipe(
ignoreNonBot(defaultPrefix),
map(message => {
const [prefix, ...rest] = fmt(message, defaultPrefix);
return {
ctx : Context.wrap(message), //TODO : check for BothCommand
args : <Args>['text', rest],
mod : Files.ApplicationCommandStore[1].get(prefix)
?? Files.BothCommand.get(prefix)
?? Files.TextCommandStore.aliases.get(prefix)
ctx: Context.wrap(message), //TODO : check for BothCommand
args: <Args>['text', rest],
mod:
Files.ApplicationCommandStore[1].get(prefix) ??
Files.BothCommand.get(prefix) ??
Files.TextCommandStore.aliases.get(prefix),
};
}));
}),
);
const ensureModuleType$ = processMessage$.pipe(
concatMap(payload => of(payload.mod)
.pipe(
concatMap(payload =>
of(payload.mod).pipe(
filterCorrectModule(CommandType.Text), // fix for BothCommand
map( textCommand => ({ ...payload, mod : textCommand }))
)));
map(textCommand => ({ ...payload, mod: textCommand })),
),
),
);
const processEventPlugins$ = ensureModuleType$.pipe(
concatMap( ({ctx, args, mod:plugged}) => {
const eventPlugins = plugged.plugins.filter(isEventPlugin);
const res = Promise.all(eventPlugins.map(ePlug => {
if((ePlug.modTy & plugged.mod.type) === 0) {
concatMap(({ ctx, args, mod: plugged }) => {
const eventPlugins = plugged.plugins.filter(isEventPlugin);
const res = Promise.all(
eventPlugins.map(ePlug => {
if ((ePlug.modType & plugged.mod.type) === 0) {
return Err.EMPTY;
}
return ePlug.execute([ctx, args], {
next : () => Ok.EMPTY,
stop : () => Err.EMPTY
});
}));
return from(res).pipe(map(res => ({ plugged, ctx, args, res })));
}));
processEventPlugins$.subscribe( ( { plugged, ctx, args, res } ) => {
if(res.every( pl => pl.ok)) {
Promise.resolve(plugged.mod.execute(ctx, args)).then(() =>
console.log(plugged)
return ePlug.execute([ctx, args], controller);
}),
);
}
else {
return from(res).pipe(map(res => ({ plugged, ctx, args, res })));
}),
);
processEventPlugins$.subscribe(({ plugged, ctx, args, res }) => {
if (res.every(pl => pl.ok)) {
Promise.resolve(plugged.mod.execute(ctx, args)).then(() => console.log(plugged));
} else {
console.log(plugged, 'failed');
}
});
};

View File

@@ -1,43 +1,42 @@
import type { Awaitable, InteractionType, Message } from 'discord.js';
import type { Message } from 'discord.js';
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 { EventPlugin, SernPlugin } from '../plugins/plugin';
import type { SernPlugin } from '../plugins/plugin';
export function correctModuleType<T extends keyof ModuleDefs>(
plug: PluggedModule | undefined, type : T
) : plug is { mod: ModuleDefs[T], plugins : SernPlugin[] } {
plug: PluggedModule | undefined,
type: T,
): plug is { mod: ModuleDefs[T]; plugins: SernPlugin[] } {
return plug !== undefined && plug.mod.type === type;
}
export function filterCorrectModule<T extends keyof ModuleDefs>(cmdType : T) {
return (src : Observable<PluggedModule|undefined>) =>
new Observable<{ mod : ModuleDefs[T], plugins : SernPlugin[] }>( subscriber => {
return src.subscribe({
export function filterCorrectModule<T extends keyof ModuleDefs>(cmdType: T) {
return (src: Observable<PluggedModule | undefined>) =>
new Observable<{ mod: ModuleDefs[T]; plugins: SernPlugin[] }>(subscriber => {
return src.subscribe({
next(plug) {
if(correctModuleType(plug, cmdType)) {
subscriber.next({ mod : plug.mod, plugins : plug.plugins });
if (correctModuleType(plug, cmdType)) {
subscriber.next({ mod: plug.mod, plugins: plug.plugins });
} else {
if (plug === undefined) {
return throwError(() => SernError.UndefinedModule);
}
return throwError(() => SernError.MismatchModule);
if (plug === undefined) {
return throwError(() => SernError.UndefinedModule);
}
return throwError(() => SernError.MismatchModule);
}
},
error: (e) => subscriber.error(e),
complete: () => subscriber.complete()
error: e => subscriber.error(e),
complete: () => subscriber.complete(),
});
});
});
}
/** export function filterTap<T extends keyof ModuleDefs>(
cmdType : T,
tap: (mod : ModuleDefs[T], plugins : EventPlugin[]) => Awaitable<void>
) {
cmdType : T,
tap: (mod : ModuleDefs[T], plugins : EventPlugin[]) => Awaitable<void>
) {
return (src : Observable<PluggedModule|undefined>) =>
new Observable<PluggedModule|undefined>( subscriber => {
return src.subscribe({
@@ -58,45 +57,35 @@ export function filterCorrectModule<T extends keyof ModuleDefs>(cmdType : T) {
});
});
}
**/
export function ignoreNonBot(prefix : string) {
return (src : Observable<Message>) =>
**/
export function ignoreNonBot(prefix: string) {
return (src: Observable<Message>) =>
new Observable<Message>(subscriber => {
return src.subscribe({
next(m) {
const passAll = [
isNotFromBot,
(m : Message) =>
m.content
.slice(0,prefix.length)
.localeCompare(prefix,
undefined, { sensitivity : 'accent' }
) === 0
].every( fn => fn(m));
return src.subscribe({
next(m) {
const passAll = [
isNotFromBot,
(m: Message) =>
m.content
.slice(0, prefix.length)
.localeCompare(prefix, undefined, { sensitivity: 'accent' }) === 0,
].every(fn => fn(m));
if (passAll) {
subscriber.next(m);
}
},
error: (e) => subscriber.error(e),
complete: () => subscriber.complete()
if (passAll) {
subscriber.next(m);
}
},
error: e => subscriber.error(e),
complete: () => subscriber.complete(),
});
});
});
}
export function partition<T,U extends T>(
condition : (el : T) => el is U,
array : T[]
) : [ U[], T[] ] {
const uArr : U[] = [];
const vArr : T[] = [];
for (const el of array ) {
export function partition<T, U extends T>(condition: (el: T) => el is U, array: T[]): [U[], T[]] {
const uArr: U[] = [];
const vArr: T[] = [];
for (const el of array) {
(condition(el) ? uArr : vArr).push(el);
}
return [ uArr, vArr ];
return [uArr, vArr];
}

View File

@@ -1,115 +1,115 @@
import {concatMap, from, fromEvent, map, take,concat, skip, Observable, of, } from 'rxjs';
import { concat, concatMap, from, fromEvent, map, Observable, of, skip, take } 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 type {
HandlerCallback,
ModuleHandlers,
ModuleStates,
ModuleType,
} from '../structures/modules/commands/moduleHandler';
import { CommandType } from '../sern';
import { CommandPlugin, EventPlugin, PluginType, SernPlugin } from '../plugins/plugin';
import { correctModuleType, partition } from './observableHandling';
import { partition } from './observableHandling';
import { Err, Ok, Result } from 'ts-results';
import type { PluggedModule } from '../structures/modules/module';
import type { Awaitable } from 'discord.js';
export const onReady = ( wrapper : Wrapper ) => {
export const onReady = (wrapper: Wrapper) => {
const { client, commands } = wrapper;
const ready$ = fromEvent(client, 'ready').pipe(take(1),skip(1));
const processCommandFiles$ = Files.buildData(commands).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;
}));
const processPlugins$ = processCommandFiles$.pipe(
concatMap( ({mod, plugins:allPlugins}) => {
const [ cmdPlugins, eventPlugins ] = partition(isCmdPlugin, allPlugins);
const cmdPluginsRes = cmdPlugins.map(plug => {
return {
...plug,
name: plug?.name ?? 'Unnamed Plugin',
execute : plug.execute(client, mod, {
next : () => Ok.EMPTY,
stop : () => Err.EMPTY
})
};
});
return of({ plugged : <PluggedModule>{ mod , plugins : eventPlugins }, cmdPluginsRes });
}),
);
const ready$ = fromEvent(client, 'ready').pipe(take(1), skip(1));
const processCommandFiles$ = Files.buildData(commands).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;
}),
);
const processPlugins$ = processCommandFiles$.pipe(
concatMap(({ mod, plugins: allPlugins }) => {
const [cmdPlugins, eventPlugins] = partition(isCmdPlugin, allPlugins);
const cmdPluginsRes = cmdPlugins.map(plug => {
return {
...plug,
name: plug?.name ?? 'Unnamed Plugin',
execute: plug.execute(client, mod, {
next: () => Ok.EMPTY,
stop: () => Err.EMPTY,
}),
};
});
return of({ plugged: <PluggedModule>{ mod, plugins: eventPlugins }, cmdPluginsRes });
}),
);
(concat(ready$,processPlugins$) as Observable<{
plugged: PluggedModule;
cmdPluginsRes: {
execute: Awaitable<Result<void, void>>;
type: PluginType.Command;
name: string;
description: string;
}[];
}>).pipe (
concatMap( pl =>
from(Promise.all(pl.cmdPluginsRes.map(async e=> ({...e, execute : await e.execute }))))
.pipe(
map(res => ({...pl, cmdPluginsRes : res })),
)
),
)
.subscribe(({ plugged, cmdPluginsRes }) => {
const loadedPluginsCorrectly = cmdPluginsRes.every(res => res.execute.ok);
const { mod, plugins } = plugged;
if(loadedPluginsCorrectly) {
registerModule(mod.name!, mod, plugins);
}
else {
console.log(`Failed to load command ${mod.name!}`);
console.log(mod);
}
});
(
concat(ready$, processPlugins$) as Observable<{
plugged: PluggedModule;
cmdPluginsRes: {
execute: Awaitable<Result<void, void>>;
type: PluginType.Command;
name: string;
description: string;
}[];
}>
)
.pipe(
concatMap(pl =>
from(Promise.all(pl.cmdPluginsRes.map(async e => ({ ...e, execute: await e.execute })))).pipe(
map(res => ({ ...pl, cmdPluginsRes: res })),
),
),
)
.subscribe(({ plugged, cmdPluginsRes }) => {
const loadedPluginsCorrectly = cmdPluginsRes.every(res => res.execute.ok);
const { mod, plugins } = plugged;
if (loadedPluginsCorrectly) {
registerModule(mod.name!, mod, plugins);
} else {
console.log(`Failed to load command ${mod.name!}`);
console.log(mod);
}
});
};
function handler( name : string ) : ModuleHandlers {
return {
[CommandType.Text] : (mod, plugins) => {
mod.alias.forEach ( a => Files.TextCommandStore.aliases.set(a,{ mod, plugins}));
Files.TextCommandStore.text.set( name, { mod, plugins } );
function handler(name: string): ModuleHandlers {
return {
[CommandType.Text]: (mod, plugins) => {
mod.alias.forEach(a => Files.TextCommandStore.aliases.set(a, { mod, plugins }));
Files.TextCommandStore.text.set(name, { mod, plugins });
},
[CommandType.Slash]: (mod, plugins) => {
Files.ApplicationCommandStore[1].set( name , { mod, plugins });
Files.ApplicationCommandStore[1].set(name, { mod, plugins });
},
[CommandType.Both] :( mod, plugins )=> {
Files.BothCommand.set ( name,{ mod, plugins});
mod.alias.forEach (a => Files.TextCommandStore.aliases.set(a, {mod,plugins}));
[CommandType.Both]: (mod, plugins) => {
Files.BothCommand.set(name, { mod, plugins });
mod.alias.forEach(a => Files.TextCommandStore.aliases.set(a, { mod, plugins }));
},
[CommandType.MenuUser] : (mod, plugins) => {
Files.ApplicationCommandStore[2].set ( name, {mod, plugins} );
[CommandType.MenuUser]: (mod, plugins) => {
Files.ApplicationCommandStore[2].set(name, { mod, plugins });
},
[CommandType.MenuMsg] : (mod,plugins) => {
Files.ApplicationCommandStore[3].set (name, {mod, plugins} );
[CommandType.MenuMsg]: (mod, plugins) => {
Files.ApplicationCommandStore[3].set(name, { mod, plugins });
},
[CommandType.Button] : (mod,plugins) => {
Files.MessageCompCommandStore[2].set(name, {mod, plugins});
[CommandType.Button]: (mod, plugins) => {
Files.MessageCompCommandStore[2].set(name, { mod, plugins });
},
[CommandType.MenuSelect] : ( mod, plugins ) => {
[CommandType.MenuSelect]: (mod, plugins) => {
Files.MessageCompCommandStore[2].set(name, { mod, plugins });
},
};
}
function isCmdPlugin (p : SernPlugin) : p is CommandPlugin {
function isCmdPlugin(p: SernPlugin): p is CommandPlugin {
return (p.type & PluginType.Command) !== 0;
}
export function isEventPlugin( p : SernPlugin) : p is EventPlugin {
export function isEventPlugin(p: SernPlugin): p is EventPlugin {
return (p.type & PluginType.Event) !== 0;
}
function registerModule <T extends ModuleType> (
name : string,
mod : ModuleStates[T],
plugins : SernPlugin[]
) {
return (<HandlerCallback<T>> handler(name)[mod.type])(mod, plugins);
function registerModule<T extends ModuleType>(name: string, mod: ModuleStates[T], plugins: SernPlugin[]) {
return (<HandlerCallback<CommandType>>handler(name)[mod.type])(mod, plugins);
}

View File

@@ -1,11 +1,11 @@
//
// Plugins can be inserted on all commands and are emitted
//
// 1.) on ready event, where all commands are loaded.
// 1.) on ready event, where all commands are loaded.
// 2.) on corresponding observable (command triggers)
//
// The goal of plugins is to organize commands and
// provide extensions to repetitive patterns
//
// The goal of plugins is to organize commands and
// provide extensions to repetitive patterns
// examples include refreshing modules,
// categorizing commands, cooldowns, permissions, etc
// Plugins are reminisce of middleware in express.
@@ -20,62 +20,50 @@ import type { BaseModule, PluggedModule } from '../structures/modules/module';
export enum PluginType {
Command = 0b01,
Event = 0b10
Event = 0b10,
}
export interface Controller {
next : () => Ok<void>,
stop : () => Err<void>
next: () => Ok<void>;
stop: () => Err<void>;
}
type executeCmdPlugin = { execute : ( wrapper : Wrapper, controller : Controller ) => Result<void, void> }
type executeCmdPlugin = { execute: (wrapper: Wrapper, controller: Controller) => Result<void, void> };
interface BasePlugin extends Override<BaseModule, executeCmdPlugin>{
type : PluginType
interface BasePlugin extends Override<BaseModule, executeCmdPlugin> {
type: PluginType;
}
export type CommandPlugin = {
type : PluginType.Command
} & Override<BasePlugin, {
execute : (
wrapper:Client, module : Module, controller:Controller
) => Awaitable<Result<void,void>>
}>;
type: PluginType.Command;
} & Override<
BasePlugin,
{
execute: (wrapper: Client, module: Module, controller: Controller) => Awaitable<Result<void, void>>;
}
>;
export type EventPlugin<T extends CommandType = 1> = {
type : PluginType.Event,
modTy : T
} & Override<BasePlugin, {
execute : ( event : Parameters<ModuleDefs[T]['execute']>, controller: Controller ) => Awaitable<Result<void,void>>
}>;
//TODO: rn adding the modType check a little hackish. Find better way to determine the
// module type of the event plugin
export type EventPlugin<T extends CommandType = CommandType> = {
type: PluginType.Event;
modType: T;
} & Override<
BasePlugin,
{
execute: (event: Parameters<ModuleDefs[T]['execute']>, controller: Controller) => Awaitable<Result<void, void>>;
}
>;
export type SernPlugin =
CommandPlugin
| EventPlugin;
export type SernPlugin = CommandPlugin | EventPlugin;
export function commmand(plug : CommandPlugin) {
export function plugins<T extends CommandType, V extends EventPlugin<T> | CommandPlugin>(...plug: V[]) {
return plug;
}
export function event(plug : EventPlugin) {
return plug;
export function sernModule(plugins: { command: CommandPlugin[]; onEvent: EventPlugin[] }, mod: Module): PluggedModule {
return {
mod,
plugins: [...plugins.command, ...plugins.onEvent],
};
}
export function apply(...plugins: SernPlugin[]) {
return plugins;
}
export function sernModule
(plugins : SernPlugin[], mod : Module ) : PluggedModule {
return {
mod,
plugins
};
}

View File

@@ -1,11 +1,6 @@
import type {
DiscordEvent,
} from '../types/handler';
import type { DiscordEvent } from '../types/handler';
import {
ApplicationCommandType,
Client,
} from 'discord.js';
import { ApplicationCommandType, Client } from 'discord.js';
import type Wrapper from './structures/wrapper';
import { fromEvent } from 'rxjs';
@@ -13,47 +8,51 @@ import { SernError } from './structures/errors';
import { onReady } from './events/readyEvent';
import { onMessageCreate } from './events/messageEvent';
import { onInteractionCreate } from './events/interactionCreate';
import { match } from 'ts-pattern';
import { P } from 'ts-pattern';
import { Sern } from '..';
import { match, P } from 'ts-pattern';
import { Err, Ok } from 'ts-results';
export function init( wrapper : Wrapper ) {
const { events, client } = wrapper;
if (events !== undefined) eventObserver(client, events);
onReady( wrapper );
onMessageCreate( wrapper );
onInteractionCreate ( wrapper );
export function init(wrapper: Wrapper) {
const { events, client } = wrapper;
if (events !== undefined) eventObserver(client, events);
onReady(wrapper);
onMessageCreate(wrapper);
onInteractionCreate(wrapper);
}
//TODO : Add event listener for any other generic node js event emitter
function eventObserver(client: Client, events: DiscordEvent[] ) {
events.forEach( ( [event, cb] ) => {
if (event === 'ready') throw Error(SernError.ReservedEvent);
fromEvent(client, event, cb).subscribe();
});
function eventObserver(client: Client, events: DiscordEvent[]) {
events.forEach(([event, cb]) => {
if (event === 'ready') throw Error(SernError.ReservedEvent);
fromEvent(client, event, cb).subscribe();
});
}
/**
* @enum { number };
*/
export enum CommandType {
Text = 0b0000001,
Slash = 0b0000010,
MenuUser = 0b0000100,
MenuMsg = 0b0001000,
Button = 0b0010000,
Text = 0b0000001,
Slash = 0b0000010,
MenuUser = 0b0000100,
MenuMsg = 0b0001000,
Button = 0b0010000,
MenuSelect = 0b0100000,
Both = 0b0000011,
Both = 0b0000011,
}
export function cmdTypeToDjs(ty: CommandType) {
return match(ty)
.with(CommandType.Slash, () => ApplicationCommandType.ChatInput)
.with(CommandType.MenuUser, () => ApplicationCommandType.User)
.with(CommandType.MenuMsg, ()=> ApplicationCommandType.Message)
.with(CommandType.Both, () => ApplicationCommandType.ChatInput )
.with(P._, () => { throw new Error(SernError.NonValidModuleType); })
.exhaustive();
return match(ty)
.with(CommandType.Slash, () => ApplicationCommandType.ChatInput)
.with(CommandType.MenuUser, () => ApplicationCommandType.User)
.with(CommandType.MenuMsg, () => ApplicationCommandType.Message)
.with(CommandType.Both, () => ApplicationCommandType.ChatInput)
.with(P._, () => {
throw new Error(SernError.NonValidModuleType);
})
.exhaustive();
}
export const controller = {
next: () => Ok.EMPTY,
stop: () => Err.EMPTY,
};

View File

@@ -1,36 +1,33 @@
import type { SernPlugin } from '../../../plugins/plugin';
import { CommandType } from '../../../sern';
import type {
TextCommand,
import type {
BothCommand,
ButtonCommand,
SlashCommand,
ContextMenuMsg,
ContextMenuMsg,
ContextMenuUser,
SelectMenuCommand
} from './module';
SelectMenuCommand,
SlashCommand,
TextCommand,
} 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> =
( mod: ModuleStates[K], plugins : SernPlugin[] ) => unknown;
// A handler callback that is called on each ModuleDef
export type HandlerCallback<K extends ModuleType> = (mod: ModuleStates[K], plugins: SernPlugin<K>[]) => unknown;
//An object that acts as the mapped object to handler
export type ModuleHandlers = { [K in ModuleType] : HandlerCallback<K> };
export type ModuleHandlers = { [K in ModuleType]: HandlerCallback<K> };

View File

@@ -1,17 +1,15 @@
import type { Awaitable, ChatInputCommandInteraction } from 'discord.js';
import type { Awaitable } from 'discord.js';
import type { Args, Module } from '../../..';
import type { CommandPlugin, EventPlugin, SernPlugin } from '../../plugins/plugin';
import type { SernPlugin } from '../../plugins/plugin';
import type Context from '../context';
export interface BaseModule {
name? : string;
description : string;
name?: string;
description: string;
execute: (ctx: Context, args: Args) => Awaitable<void>;
}
export interface PluggedModule {
mod : Module;
plugins : SernPlugin[];
mod: Module;
plugins: SernPlugin[];
}