From 24cd2605315fe9d7ae6bfde2c003c84a12e977c5 Mon Sep 17 00:00:00 2001 From: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com> Date: Thu, 16 Jun 2022 20:10:59 -0500 Subject: [PATCH] feat: more support for event loading!! --- src/handler/plugins/plugin.ts | 7 +-- src/handler/sern.ts | 94 +++++++++++++++++++++++------ src/handler/sernEmitter.ts | 2 +- src/handler/structures/errors.ts | 1 + src/handler/structures/events.ts | 2 + src/handler/utilities/predicates.ts | 10 ++- src/types/handler.ts | 12 ++-- 7 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/handler/plugins/plugin.ts b/src/handler/plugins/plugin.ts index af79984..9cc82a8 100644 --- a/src/handler/plugins/plugin.ts +++ b/src/handler/plugins/plugin.ts @@ -36,7 +36,7 @@ export type CommandPlugin = { { type: PluginType.Command; execute: ( - wrapper: Client, + wrapper: K extends CommandType.External ? EventEmitter : Client, module: DefinitelyDefined, controller: Controller, ) => Awaitable>; @@ -77,9 +77,8 @@ function isCommandPlugin( ): e is CommandPlugin { return !isEventPlugin(e); } - -// TODO: Do better typings -export function sernModule( +//TODO: I WANT BETTER TYPINGS AHHHHHHHHHHHHHHH +export function sernModule( plugin: (CommandPlugin | EventPlugin)[], mod: ModuleNoPlugins[T], ): Module { diff --git a/src/handler/sern.ts b/src/handler/sern.ts index a48b624..beb01b6 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -1,36 +1,96 @@ -import type { DiscordEvent, EventEmitterRegister, SernEvent } from '../types/handler'; - import type Wrapper from './structures/wrapper'; -import { fromEvent } from 'rxjs'; import { onReady } from './events/readyEvent'; import { onMessageCreate } from './events/messageEvent'; import { onInteractionCreate } from './events/interactionCreate'; import { Err, Ok } from 'ts-results'; -import { isDiscordEvent, isSernEvent } from './utilities/predicates'; +import { buildData } from './utilities/readFile'; +import type { EventModule } from './structures/module'; +import { from, fromEvent, map, throwError } from 'rxjs'; +import { match } from 'ts-pattern'; +import { errTap } from './events/observableHandling'; +import { isDiscordEvent, isExternalEvent, isSernEvent } from './utilities/predicates'; +import { SernError } from './structures/errors'; +import type { SpreadParams } from '../types/handler'; +import * as Files from './utilities/readFile'; +import { basename } from 'path'; export function init(wrapper: Wrapper) { const { events } = wrapper; if (events !== undefined) { - eventObserver(wrapper, events); + processEvents(wrapper, events); } onReady(wrapper); onMessageCreate(wrapper); onInteractionCreate(wrapper); } -function eventObserver( - { client, sernEmitter }: Wrapper, - events: (DiscordEvent | EventEmitterRegister | SernEvent)[], +function processEvents(wrapper: Wrapper, events: string | EventModule[] | (() => EventModule[])) { + const eventStream = eventObservable$(wrapper, events); + const processPlugins$ = eventStream.pipe(map(mod => mod)); //for now, until i figure out what to do with how plugins are registered + const normalize$ = processPlugins$.pipe( + map(({ mod, absPath }) => { + return { + name: mod?.name ?? Files.fmtFileName(basename(absPath)), + description: mod?.description ?? '...', + ...mod, + }; + }), + ); + const processAndLoadEvents$ = normalize$.pipe( + map(mod => { + return match(mod as EventModule) + .when(isSernEvent, m => { + if (wrapper.sernEmitter === undefined) { + return throwError(() => SernError.UndefinedSernEmitter); + } + return fromEvent( + wrapper.sernEmitter, + m.name!, + m.execute as SpreadParams, + ); + }) + .when(isDiscordEvent, m => + fromEvent( + wrapper.client, + mod.name!, + m.execute as SpreadParams, + ), + ) + .when(isExternalEvent, m => fromEvent(m.emitter, m.name!, m.execute)) + .run(); + }), + ); +} + +function eventObservable$( + { sernEmitter }: Wrapper, + events: string | EventModule[] | (() => EventModule[]), ) { - events.forEach(event => { - if (isDiscordEvent(event)) { - fromEvent(client, event[0], event[1]).subscribe(); - } else if (isSernEvent(event)) { - sernEmitter && fromEvent(sernEmitter, event[0], event[1]).subscribe(); - } else { - fromEvent(event[0], event[1], event[2]).subscribe(); - } - }); + return match(events) + .when(Array.isArray, (arr: EventModule[]) => { + return from(arr.map(self => ({ mod: self, absPath: __filename }))); + }) + .when( + e => typeof e === 'string', + (eventsDir: string) => { + return buildData(eventsDir).pipe( + errTap(reason => + sernEmitter?.emit('module.register', { + type: 'failure', + module: undefined, + reason, + }), + ), + ); + }, + ) + .when( + e => typeof e === 'function', + (evs: () => EventModule[]) => { + return from(evs().map(self => ({ mod: self, absPath: __filename }))); + }, + ) + .run(); } export const controller = { diff --git a/src/handler/sernEmitter.ts b/src/handler/sernEmitter.ts index a31d60a..c7f1330 100644 --- a/src/handler/sernEmitter.ts +++ b/src/handler/sernEmitter.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import type { Module } from './structures/module'; -type Payload = +export type Payload = | { type: 'success'; module: Module } | { type: 'failure'; module: Module | undefined; reason: string | Error }; export type SernEventsMapping = { diff --git a/src/handler/structures/errors.ts b/src/handler/structures/errors.ts index 87a7232..ff3ac37 100644 --- a/src/handler/structures/errors.ts +++ b/src/handler/structures/errors.ts @@ -5,4 +5,5 @@ export enum SernError { NotSupportedInteraction = `This interaction is not supported.`, PluginFailure = `A plugin failed to call controller.next()`, MismatchEvent = `You cannot use message when an interaction fired or vice versa`, + UndefinedSernEmitter = `Could not find a Sern emitter`, } diff --git a/src/handler/structures/events.ts b/src/handler/structures/events.ts index f97332a..f6e86af 100644 --- a/src/handler/structures/events.ts +++ b/src/handler/structures/events.ts @@ -4,6 +4,7 @@ import type { CommandPlugin, EventPlugin } from '../plugins/plugin'; import type { CommandType } from './enums'; import type { SernEventsMapping } from '../sernEmitter'; import type { Awaitable, ClientEvents } from 'discord.js'; +import type { EventEmitter } from 'events'; export type SernEventCommand = Override< @@ -31,6 +32,7 @@ export type ExternalEventCommand = Override< BaseModule, { type: CommandType.External; + emitter: EventEmitter; onEvent: EventPlugin[]; plugins: CommandPlugin[]; execute(...args: unknown[]): Awaitable; diff --git a/src/handler/utilities/predicates.ts b/src/handler/utilities/predicates.ts index 48280e0..83276b3 100644 --- a/src/handler/utilities/predicates.ts +++ b/src/handler/utilities/predicates.ts @@ -9,7 +9,11 @@ import type { SelectMenuInteraction, UserContextMenuCommandInteraction, } from 'discord.js'; -import type { DiscordEventCommand, SernEventCommand } from '../structures/events'; +import type { + DiscordEventCommand, + ExternalEventCommand, + SernEventCommand, +} from '../structures/events'; import { CommandType } from '../..'; export function correctModuleType( @@ -57,6 +61,10 @@ export function isSernEvent(el: EventModule): el is SernEventCommand { return !isDiscordEvent(el); } +export function isExternalEvent(el: EventModule): el is ExternalEventCommand { + return !isDiscordEvent(el) && !isSernEvent(el); +} + export function isEventModule(module: Module): module is EventModule { return [CommandType.Discord, CommandType.Sern, CommandType.External].includes(module.type); } diff --git a/src/types/handler.ts b/src/types/handler.ts index 3df4a65..ed87ce9 100644 --- a/src/types/handler.ts +++ b/src/types/handler.ts @@ -48,14 +48,16 @@ type IsOptional = { [K in keyof T]-?: T[K] extends Required[K] ? false : true; }; -export type UnionToIntersection = (T extends unknown ? (x: T) => unknown : never) extends ( - x: infer R, -) => unknown - ? R - : never; export type ConformedEditOptions = Override< MessageEditOptions | WebhookEditMessageOptions, { embeds?: (JSONEncodable | APIEmbed)[]; } >; +/** + * Turns a function with a union of array of args into a single union + * [ T , V , B ] | [ A ] => T | V | B | A + */ +export type SpreadParams unknown> = ( + args: Parameters[number], +) => unknown;