From 1fea6fa1360d46d641103d7fb04e050c21bcc004 Mon Sep 17 00:00:00 2001 From: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com> Date: Sat, 6 May 2023 01:01:54 -0500 Subject: [PATCH] refactor: add createGenericHandler --- src/handler/events/generic.ts | 70 ++++++++++++++++++------ src/handler/events/interactions.ts | 6 +- src/handler/events/messages.ts | 36 ++---------- src/handler/events/observableHandling.ts | 5 +- 4 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/handler/events/generic.ts b/src/handler/events/generic.ts index 7c878a1..440c828 100644 --- a/src/handler/events/generic.ts +++ b/src/handler/events/generic.ts @@ -1,21 +1,35 @@ -import { BaseInteraction, ChatInputCommandInteraction, Interaction, InteractionType } from "discord.js"; +import { BaseInteraction, ChatInputCommandInteraction, Interaction, InteractionType, Message } from "discord.js"; import { Observable, filter, map } from "rxjs"; import { CommandType, ModuleManager } from "../../core"; import { SernError } from '../../core/structures/errors' import { filterMap } from '../../core/operators'; import { defaultModuleLoader } from "../../core/module-loading"; import { Processed } from "../../types/core"; -import { BothCommand, CommandModule } from "../../types/module"; +import { BothCommand, CommandModule, Module } from "../../types/module"; import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from "./dispatchers"; import { isAutocomplete } from "../../core/predicates"; -import { err } from "../../core/functions"; import { ObservableInput, pipe, switchMap} from "rxjs"; import { SernEmitter } from "../../core"; import { errTap } from '../../core/operators'; import * as Files from '../../core/module-loading'; import { sernMeta } from "../../commands"; import { AnyModule } from "../../types/module"; +import { Err, Result } from "ts-results-es"; +import { Awaitable } from "../../types/handler"; +import { fmt } from "./messages"; + + +function createGenericHandler( + source: Observable, + makeModule: (event: Narrowed) => Awaitable> +) { + return (pred: (i: Source) => i is Narrowed) => + source.pipe( + filter(pred), + filterMap(makeModule) + ) +} /** * * Creates an RxJS observable that filters and maps incoming interactions to their respective modules. @@ -23,21 +37,45 @@ import { AnyModule } from "../../types/module"; * @param mg The module manager instance used to retrieve the module path for each interaction. * @returns A handler to create a RxJS observable of dispatchers that take incoming interactions and execute their corresponding modules. */ -export function createHandler( - i: Observable, +export function createInteractionHandler( + source: Observable, mg: ModuleManager, ) { - return (pred: (i: BaseInteraction) => i is T) => - i.pipe( - filter(pred), - filterMap(event => { - const fullPath = mg.get(createId(event as unknown as Interaction)) - if(!fullPath) return err(); - return defaultModuleLoader(fullPath) - .then(res => res.map(module => ({ module, event }) )) - }), - map(createDispatcher) - ) + return createGenericHandler>( + source, + ( event ) => { + const fullPath = mg.get(createId(event as unknown as Interaction)) + if(!fullPath) return Err(SernError.UndefinedModule + " No full path found in module store"); + return defaultModuleLoader(fullPath) + .then(res => + res.map(module => createDispatcher({ module, event })) + ) + } + ) +} + +export function createMessageHandler( + source: Observable, + defaultPrefix: string, + mg: ModuleManager +) { + return createGenericHandler( + source, + ( event ) => { + const [prefix, ...rest] = fmt(event.content, defaultPrefix); + const fullPath = mg.get(`${prefix}__A0`); + if (fullPath === undefined) { + return Err(SernError.UndefinedModule + " No full path found in module store"); + } + return defaultModuleLoader(fullPath).then( + result => { + const args = contextArgs(event, rest); + return result.map(module => dispatchCommand(module, args)) + }) + + + } + ) } /** * Creates a unique ID for a given interaction object. diff --git a/src/handler/events/interactions.ts b/src/handler/events/interactions.ts index 17038bd..941fcaf 100644 --- a/src/handler/events/interactions.ts +++ b/src/handler/events/interactions.ts @@ -1,4 +1,4 @@ -import { Interaction } from 'discord.js'; +import { BaseInteraction, Interaction } from 'discord.js'; import { catchError, concatMap, @@ -14,7 +14,7 @@ import { useContainerRaw } from '../../core/dependencies'; import type { Logging, ModuleManager } from '../../core/contracts'; import type { EventEmitter } from 'node:events'; import { isAutocomplete, isCommand, isMessageComponent, isModal } from '../../core/predicates'; -import { createHandler } from './generic'; +import { createInteractionHandler } from './generic'; export function makeInteractionCreate([s, err, log, modules, client]: [ @@ -27,7 +27,7 @@ export function makeInteractionCreate([s, err, log, modules, client]: [ platform: WebsocketStrategy ) { const interactionStream$ = sharedObservable(client, platform.eventNames[0]); - const handle = createHandler(interactionStream$, modules); + const handle = createInteractionHandler(interactionStream$, modules); const interactionHandler$ = merge( handle(isMessageComponent), handle(isAutocomplete), diff --git a/src/handler/events/messages.ts b/src/handler/events/messages.ts index 727bd67..b0ad5aa 100644 --- a/src/handler/events/messages.ts +++ b/src/handler/events/messages.ts @@ -13,6 +13,7 @@ import { WebsocketStrategy, SernEmitter } from '../../core'; import { err } from '../../core/functions'; import { defaultModuleLoader } from '../../core/module-loading'; import { sharedObservable, filterMap } from '../../core/operators'; +import { createMessageHandler } from './generic'; /** * Removes the first character(s) _[depending on prefix length]_ of the message @@ -28,32 +29,6 @@ export function fmt(msg: string, prefix: string): string[] { return msg.slice(prefix.length).trim().split(/\s+/g); } -/** - * An operator function that processes a message to fetch a command module and prepares context payload. - * @param defaultPrefix - * @param get - */ -const createMessageProcessor = ( - defaultPrefix: string, - moduleManager: ModuleManager -) => - pipe( - ignoreNonBot(defaultPrefix), - filterMap(message => { - const [prefix, ...rest] = fmt(message.content, defaultPrefix); - const fullPath = moduleManager.get(`${prefix}__A0`); - if (fullPath === undefined) { - return err(); - } - return defaultModuleLoader(fullPath).then( - result => { - const args = contextArgs(message, rest); - return result.map(module => ({ module, args })) - }) - }), - map(({ args, module }) => dispatchCommand(module as Processed, args)), - ); - export function makeMessageCreate( [s, err, log, modules, client]: [ SernEmitter, @@ -68,10 +43,12 @@ export function makeMessageCreate( return EMPTY.subscribe() } const messageStream$ = sharedObservable(client, platform.eventNames[1]); - const messageProcessor = createMessageProcessor(platform.defaultPrefix, modules); - return messageStream$ + const handler = createMessageHandler(messageStream$, platform.defaultPrefix, modules); + const messageHandler = handler( + ignoreNonBot(platform.defaultPrefix) as (m: Message) => m is Message + ) + return messageHandler .pipe( - messageProcessor, makeModuleExecutor(module => { s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure)); }), @@ -84,5 +61,4 @@ export function makeMessageCreate( .then(() => log?.info({ message: 'Cleaning container and crashing' })); }), ) - .subscribe(); } diff --git a/src/handler/events/observableHandling.ts b/src/handler/events/observableHandling.ts index 8f0da0e..d588fe1 100644 --- a/src/handler/events/observableHandling.ts +++ b/src/handler/events/observableHandling.ts @@ -1,4 +1,4 @@ -import { concatMap, EMPTY, filter, from, Observable, of, tap, throwError } from 'rxjs'; +import { concatMap, EMPTY, from, Observable, of, tap, throwError } from 'rxjs'; import { Result } from 'ts-results-es'; import type { CommandModule, EventModule, Module } from '../../types/module'; import { SernEmitter } from '../../core'; @@ -18,9 +18,8 @@ function hasPrefix(prefix: string, content: string) { * @param prefix */ export function ignoreNonBot(prefix: string) { - const messageFromHumanAndHasPrefix = ({ author, content }: Message) => + return ({ author, content }: Message) => !author.bot && hasPrefix(prefix, content); - return filter(messageFromHumanAndHasPrefix); } /**