mirror of
https://github.com/sern-handler/handler
synced 2026-06-19 22:32:14 +00:00
feat: broadening EventPlugin default generic type, reformat with prettier
This commit is contained in:
@@ -3,5 +3,6 @@
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4
|
||||
"tabWidth": 4,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
**/
|
||||
**/
|
||||
};
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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> };
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user