diff --git a/package.json b/package.json index 405396f..f127ac6 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "lint": "eslint src/**/*.ts", "format": "eslint src/**/*.ts --fix", "build:dev": "tsup --metafile", - "build:prod": "tsup ", + "build:prod": "tsc", "prepare": "tsc", "pretty": "prettier --write .", "tdd": "npx --yes vitest", diff --git a/src/handlers/dispatchers.ts b/src/handlers/dispatchers.ts index 9471884..8b13789 100644 --- a/src/handlers/dispatchers.ts +++ b/src/handlers/dispatchers.ts @@ -1,84 +1 @@ -import { EventEmitter } from 'node:events'; -import * as assert from 'node:assert'; -import { concatMap, from, fromEvent, map, OperatorFunction, pipe } from 'rxjs'; -import { - arrayifySource, - callPlugin, - isAutocomplete, - treeSearch, - SernError, -} from '../core/_internal'; -import { createResultResolver } from './event-utils'; -import { BaseInteraction, Message } from 'discord.js'; -import { CommandType, Context } from '../core/structures'; -import type { Args } from '../types/utility'; -import { inspect } from 'node:util' -import type { CommandModule, Module, Processed } from '../types/core-modules'; -//TODO: refactor dispatchers so that it implements a strategy for each different type of payload? -export function dispatchMessage(module: Processed, args: [Context, Args]) { - return { module, args }; -} - -export function contextArgs(wrappable: Message | BaseInteraction, messageArgs?: string[]) { - const ctx = Context.wrap(wrappable); - const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options]; - return [ctx, args] as [Context, Args]; -} - - -function intoPayload(module: Processed, ) { - return pipe( - arrayifySource, - map(args => ({ module, args, }))); -} - -const createResult = createResultResolver< - Processed, - { module: Processed; args: unknown[] }, - unknown[] ->({ - createStream: ({ module, args }) => from(module.onEvent).pipe(callPlugin(args)), - onNext: ({ args }) => args, -}); -/** - * Creates an observable from { source } - * @param module - * @param source - */ -export function eventDispatcher(module: Processed, source: unknown) { - assert.ok(source instanceof EventEmitter, `${source} is not an EventEmitter`); - - const execute: OperatorFunction = - concatMap(async args => module.execute(...args)); - return fromEvent(source, module.name) - .pipe(intoPayload(module), - concatMap(createResult), - execute); -} - -export function createDispatcher(payload: { - module: Processed; - event: BaseInteraction; -}) { - assert.ok(CommandType.Text !== payload.module.type, - SernError.MismatchEvent + 'Found text command in interaction stream'); - switch (payload.module.type) { - case CommandType.Slash: - case CommandType.Both: { - if (isAutocomplete(payload.event)) { - const option = treeSearch(payload.event, payload.module.options); - assert.ok(option, SernError.NotSupportedInteraction + ` There is no autocomplete tag for ` + inspect(payload.module)); - const { command } = option; - - return { - ...payload, - module: command as Processed, //autocomplete is not a true "module" warning cast! - args: [payload.event], - }; - } - return { module: payload.module, args: contextArgs(payload.event) }; - } - default: return { module: payload.module, args: [payload.event] }; - } -} diff --git a/src/handlers/event-utils.ts b/src/handlers/event-utils.ts index 9b7ffa1..cc8e72b 100644 --- a/src/handlers/event-utils.ts +++ b/src/handlers/event-utils.ts @@ -1,4 +1,4 @@ -import { Interaction, Message } from 'discord.js'; +import type { Interaction, Message, BaseInteraction } from 'discord.js'; import { EMPTY, Observable, @@ -8,30 +8,100 @@ import { of, throwError, tap, + fromEvent, map, OperatorFunction, catchError, finalize, + pipe } from 'rxjs'; import { - Files, Id, callPlugin, everyPluginOk, filterMapTo, handleError, SernError, - VoidResult, + type VoidResult, resultPayload, + arrayifySource, + isAutocomplete, + treeSearch, } from '../core/_internal'; -import { Emitter, ErrorHandling, Logging } from '../core/interfaces'; +import type { Emitter, ErrorHandling, Logging } from '../core/interfaces'; import { PayloadType } from '../core/structures/enums' -import { contextArgs, createDispatcher } from './dispatchers'; -import { ObservableInput, pipe } from 'rxjs'; import { Err, Ok, Result } from 'ts-results-es'; import type { Awaitable } from '../types/utility'; import type { ControlPlugin } from '../types/core-plugin'; import type { AnyModule, CommandMeta, CommandModule, Module, Processed } from '../types/core-modules'; +import { EventEmitter } from 'node:events'; +import * as assert from 'node:assert'; +import { CommandType, Context } from '../core/structures'; +import type { Args } from '../types/utility'; +import { inspect } from 'node:util' import { disposeAll } from '../core/ioc/base'; + +function contextArgs(wrappable: Message | BaseInteraction, messageArgs?: string[]) { + const ctx = Context.wrap(wrappable); + const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options]; + return [ctx, args] as [Context, Args]; +} + + +function intoPayload(module: Processed, ) { + return pipe( + arrayifySource, + map(args => ({ module, args, }))); +} + +const createResult = createResultResolver< + Processed, + { module: Processed; args: unknown[] }, + unknown[] +>({ + createStream: ({ module, args }) => from(module.onEvent).pipe(callPlugin(args)), + onNext: ({ args }) => args, +}); +/** + * Creates an observable from { source } + * @param module + * @param source + */ +export function eventDispatcher(module: Processed, source: unknown) { + assert.ok(source instanceof EventEmitter, `${source} is not an EventEmitter`); + + const execute: OperatorFunction = + concatMap(async args => module.execute(...args)); + return fromEvent(source, module.name) + .pipe(intoPayload(module), + concatMap(createResult), + execute); +} + +export function createDispatcher(payload: { + module: Processed; + event: BaseInteraction; +}) { + assert.ok(CommandType.Text !== payload.module.type, + SernError.MismatchEvent + 'Found text command in interaction stream'); + switch (payload.module.type) { + case CommandType.Slash: + case CommandType.Both: { + if (isAutocomplete(payload.event)) { + const option = treeSearch(payload.event, payload.module.options); + assert.ok(option, SernError.NotSupportedInteraction + ` There is no autocomplete tag for ` + inspect(payload.module)); + const { command } = option; + + return { + ...payload, + module: command as Processed, //autocomplete is not a true "module" warning cast! + args: [payload.event], + }; + } + return { module: payload.module, args: contextArgs(payload.event) }; + } + default: return { module: payload.module, args: [payload.event] }; + } +} function createGenericHandler( source: Observable, makeModule: (event: Narrowed) => Promise, @@ -138,7 +208,7 @@ export function executeModule( }), ); -} +}; /** @@ -169,7 +239,7 @@ export function createResultResolver< filterMapTo(() => config.onNext(args)), ); }; -} +}; /** * Calls a module's init plugins and checks for Err. If so, call { onStop } and diff --git a/src/handlers/interaction-event.ts b/src/handlers/interaction-event.ts deleted file mode 100644 index a1def25..0000000 --- a/src/handlers/interaction-event.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Interaction } from 'discord.js'; -import { mergeMap, merge, concatMap } from 'rxjs'; -import { PayloadType } from '../core/structures/enums'; -import { - isAutocomplete, - isCommand, - isMessageComponent, - isModal, - sharedEventStream, - SernError, - filterTap, - resultPayload, -} from '../core/_internal'; -import { createInteractionHandler, executeModule, makeModuleExecutor } from './event-utils'; -import type { DependencyList } from '../types/ioc'; - -export function interactionHandler([emitter, err, log, modules, client]: DependencyList) { - const interactionStream$ = sharedEventStream(client, 'interactionCreate'); - const handle = createInteractionHandler(interactionStream$, modules); - - const interactionHandler$ = merge( - handle(isMessageComponent), - handle(isAutocomplete), - handle(isCommand), - handle(isModal), - ); - return interactionHandler$ - .pipe( - filterTap(e => emitter.emit('warning', resultPayload(PayloadType.Warning, undefined, e))), - concatMap(makeModuleExecutor(module => - emitter.emit('module.activate', resultPayload(PayloadType.Failure, module, SernError.PluginFailure)))), - mergeMap(payload => executeModule(emitter, log, err, payload))); -} diff --git a/src/handlers/user-defined-events.ts b/src/handlers/user-defined-events.ts index 6c794f6..d195c6b 100644 --- a/src/handlers/user-defined-events.ts +++ b/src/handlers/user-defined-events.ts @@ -1,8 +1,7 @@ -import { ObservableInput, map, mergeAll } from 'rxjs'; +import { ObservableInput } from 'rxjs'; import { EventType } from '../core/structures'; import { SernError } from '../core/_internal'; -import { callInitPlugins, handleCrash } from './event-utils'; -import { eventDispatcher } from './dispatchers' +import { eventDispatcher } from './event-utils' import { Service } from '../core/ioc'; import type { DependencyList } from '../types/ioc'; import type { EventModule, Processed } from '../types/core-modules';