diff --git a/src/classic/context.ts b/src/classic/context.ts index d0a361d..ba550c2 100644 --- a/src/classic/context.ts +++ b/src/classic/context.ts @@ -1,27 +1,26 @@ -import { +import { ChatInputCommandInteraction, Client, InteractionReplyOptions, Message, MessageReplyOptions, Snowflake, - User -} from "discord.js"; -import { CoreContext } from "../core/structures/context"; -import { Result , Ok , Err } from 'ts-results-es'; -import { ReplyOptions } from "../types/handler"; + User, +} from 'discord.js'; +import { CoreContext } from '../core/structures/context'; +import { Result, Ok, Err } from 'ts-results-es'; +import { ReplyOptions } from '../types/handler'; /** * @since 1.0.0 * Provides values shared between * Message and ChatInputCommandInteraction */ export class Context extends CoreContext { - - get options() { - return this.interaction.options + get options() { + return this.interaction.options; } protected constructor(protected ctx: Result) { - super(ctx) + super(ctx); } public get id(): Snowflake { @@ -64,7 +63,7 @@ export class Context extends CoreContext { public get inGuild(): boolean { return this.ctx.val.inGuild(); } - + public async reply(content: ReplyOptions) { return safeUnwrap( this.ctx @@ -83,9 +82,6 @@ export class Context extends CoreContext { } } - function safeUnwrap(res: Result) { return res.val; } - - diff --git a/src/commands.ts b/src/commands.ts index d03e8ab..1e54395 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,11 +1,11 @@ -import { ClientEvents } from "discord.js"; -import { CommandType, EventType, PluginType } from "./core/structures"; -import { AnyEventPlugin, Plugin } from "./types/plugin"; -import { CommandModule, EventModule, InputCommand, InputEvent } from "./types/module"; -import { partition } from "./core/functions"; -import { filename, filePath } from "./core/module-loading"; -import { Awaitable } from "./types/handler"; -export const sernMeta = Symbol('@sern/meta') +import { ClientEvents } from 'discord.js'; +import { CommandType, EventType, PluginType } from './core/structures'; +import { AnyEventPlugin, Plugin } from './types/plugin'; +import { CommandModule, EventModule, InputCommand, InputEvent } from './types/module'; +import { partition } from './core/functions'; +import { filename, filePath } from './core/module-loading'; +import { Awaitable } from './types/handler'; +export const sernMeta = Symbol('@sern/meta'); const appBitField = 0b000000011111; /* * Generates a number based on CommandType. @@ -13,11 +13,9 @@ const appBitField = 0b000000011111; * TextCommands are 0 as they aren't either or. */ function apiType(t: CommandType) { - if(t === CommandType.Both || t === CommandType.Modal) return 1; + if (t === CommandType.Both || t === CommandType.Modal) return 1; const log = Math.log2(t); - return (appBitField & t) !== 0 - ? log - : log-2; + return (appBitField & t) !== 0 ? log : log - 2; } /* @@ -26,8 +24,8 @@ function apiType(t: CommandType) { * Then, another number generated by apiType function is appended */ function uniqueId(t: CommandType) { - const am = ((appBitField & t) !== 0) ? 'A' : 'C'; - return am+apiType(t); + const am = (appBitField & t) !== 0 ? 'A' : 'C'; + return am + apiType(t); } /** @@ -39,18 +37,18 @@ export function commandModule(mod: InputCommand): CommandModule { mod.plugins ?? [], el => (el as Plugin).type === PluginType.Control, ); - const fullPath = filePath() - const name = mod.name ?? filename(fullPath) + const fullPath = filePath(); + const name = mod.name ?? filename(fullPath); return { ...mod, - description: mod.description ?? "...", + description: mod.description ?? '...', name, onEvent, plugins, [sernMeta]: { id: `${name}__${uniqueId(mod.type)}`, fullPath, - } + }, } as CommandModule; } /** @@ -65,12 +63,12 @@ export function eventModule(mod: InputEvent): EventModule { ); const fullPath = filePath(); return { - name: mod.name ?? filename(fullPath), + name: mod.name ?? filename(fullPath), onEvent, plugins, [sernMeta]: { id: 'no-id', - fullPath + fullPath, }, ...mod, } as EventModule; @@ -89,7 +87,6 @@ export function discordEvent(mod: { }) { return eventModule({ type: EventType.Discord, - ...mod + ...mod, }); } - diff --git a/src/core/contracts/moduleManager.ts b/src/core/contracts/moduleManager.ts index e7aad80..f7bdd04 100644 --- a/src/core/contracts/moduleManager.ts +++ b/src/core/contracts/moduleManager.ts @@ -7,7 +7,7 @@ import { importModule } from '../module-loading'; export interface ModuleManager { get(id: string): string | undefined; set(id: string, path: string): void; - getPublishableCommands() : Promise + getPublishableCommands(): Promise; } /** * @since 2.0.0 @@ -18,16 +18,16 @@ export class DefaultModuleManager implements ModuleManager { return this.moduleStore.get(id); } set(id: string, path: string): void { - this.moduleStore.set(id, path) + this.moduleStore.set(id, path); } //not tested getPublishableCommands(): Promise { - const entries = this.moduleStore.entries(); - const publishable = 0b000000110; - return Promise.all( - Array.from(entries) - .filter(([id,]) => (Number.parseInt(id.at(-1)!) & publishable) !== 0) - .map(([, path]) => importModule(path))) + const entries = this.moduleStore.entries(); + const publishable = 0b000000110; + return Promise.all( + Array.from(entries) + .filter(([id]) => (Number.parseInt(id.at(-1)!) & publishable) !== 0) + .map(([, path]) => importModule(path)), + ); } } - diff --git a/src/core/dependencies.ts b/src/core/dependencies.ts index 266eac1..c9c9b01 100644 --- a/src/core/dependencies.ts +++ b/src/core/dependencies.ts @@ -27,7 +27,7 @@ export function single(cb: () => T) { * use transient if you want a new dependency every time your container getter is called * @param cb */ -export function transient(cb: (() => () => T) ) { +export function transient(cb: () => () => T) { return cb; } /** @@ -36,9 +36,7 @@ export function transient(cb: (() => () => T) ) { * Finally, update the containerSubject with the new container state * @param conf */ -export function composeRoot( - conf: DependencyConfiguration -) { +export function composeRoot(conf: DependencyConfiguration) { //This should have no client or logger yet. const currentContainer = containerSubject.getValue(); const excludeLogger = conf.exclude?.has('@sern/logger'); @@ -93,29 +91,29 @@ function defaultContainer() { >; } -const requiredDependencyKeys = [ - '@sern/emitter', - '@sern/errors', - '@sern/logger', -] as const; +const requiredDependencyKeys = ['@sern/emitter', '@sern/errors', '@sern/logger'] as const; /** - * A way for sern to grab only the necessary dependencies. + * A way for sern to grab only the necessary dependencies. * Returns a function which allows for the user to call for more dependencies. */ -export function makeFetcher(containerConfig : Wrapper['containerConfig']) { - return (otherKeys: [...Keys]) => - containerConfig.get(...requiredDependencyKeys, ...otherKeys as (keyof AnyDependencies)[]) as MapDeps< - Dep, - [...typeof requiredDependencyKeys, ...Keys] - >; +export function makeFetcher( + containerConfig: Wrapper['containerConfig'], +) { + return (otherKeys: [...Keys]) => + containerConfig.get( + ...requiredDependencyKeys, + ...(otherKeys as (keyof AnyDependencies)[]), + ) as MapDeps; } /** * @since 2.0.0 * @param conf a configuration for creating your project dependencies */ -export function makeDependencies(conf: DependencyConfiguration) { +export function makeDependencies( + conf: DependencyConfiguration, +) { //Until there are more optional dependencies, just check if the logger exists composeRoot(conf); return useContainer(); diff --git a/src/core/functions.ts b/src/core/functions.ts index 9ce8fc9..121cac2 100644 --- a/src/core/functions.ts +++ b/src/core/functions.ts @@ -2,10 +2,9 @@ import { Err, Ok } from 'ts-results-es'; import { ApplicationCommandOptionType, AutocompleteInteraction } from 'discord.js'; import type { SernAutocompleteData, SernOptionsData } from '../types/module'; - //function wrappers for empty ok / err export const ok = /* @__PURE__*/ () => Ok.EMPTY; -export const err =/* @__PURE__*/ () => Err.EMPTY; +export const err = /* @__PURE__*/ () => Err.EMPTY; export function partition(arr: (T & V)[], condition: (e: T & V) => boolean): [T[], V[]] { const t: T[] = []; @@ -20,7 +19,6 @@ export function partition(arr: (T & V)[], condition: (e: T & V) => boolean return [t, v]; } - /** * Uses an iterative DFS to check if an autocomplete node exists * @param iAutocomplete diff --git a/src/core/index.ts b/src/core/index.ts index 4625c33..5498a26 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,4 +1,4 @@ export * from './contracts'; export * from './plugins'; export * from './structures'; -export { single, transient, useContainerRaw, makeDependencies } from './dependencies' +export { single, transient, useContainerRaw, makeDependencies } from './dependencies'; diff --git a/src/core/module-loading.ts b/src/core/module-loading.ts index 54b5014..162b75b 100644 --- a/src/core/module-loading.ts +++ b/src/core/module-loading.ts @@ -2,38 +2,35 @@ import { SernError } from './structures/errors'; import { type Result, Err, Ok } from 'ts-results-es'; import { Processed } from '../types/core'; import { Module } from '../types/module'; -import * as assert from 'node:assert' -import util from 'node:util' +import * as assert from 'node:assert'; +import util from 'node:util'; import { type Observable, from, mergeMap, ObservableInput } from 'rxjs'; import { readdir, stat } from 'fs/promises'; import { basename, join, resolve } from 'path'; -export type ModuleResult = Promise, SernError>> -export type Loader = (absPath: string) => ModuleResult +export type ModuleResult = Promise, SernError>>; +export type Loader = (absPath: string) => ModuleResult; export async function importModule(absPath: string) { /// #if MODE === 'esm' - return (await import(absPath)).default as T + return import(absPath).then(i => i.default as T); /// #elif MODE === 'cjs' return require(absPath).default as T; // eslint-disable-line /// #endif } -export async function defaultModuleLoader( - absPath: string, -): ModuleResult { +export async function defaultModuleLoader(absPath: string): ModuleResult { // prettier-ignore const module = await importModule(absPath); if (module === undefined) { - return Err(SernError.UndefinedModule); + return Err(SernError.UndefinedModule); } - checkIsProcessed(module) + checkIsProcessed(module); return Ok(module); } function checkIsProcessed(m: T): asserts m is Processed { - assert.ok(m.name !== undefined, `name is not defined for ${util.format(m)}`) + assert.ok(m.name !== undefined, `name is not defined for ${util.format(m)}`); } - export const fmtFileName = (n: string) => n.substring(0, n.length - 3); /** * a directory string is converted into a stream of modules. @@ -42,7 +39,7 @@ export const fmtFileName = (n: string) => n.substring(0, n.length - 3); * @param commandDir */ export function buildModuleStream( - input: ObservableInput + input: ObservableInput, ): Observable, SernError>> { return from(input).pipe(mergeMap(defaultModuleLoader)); } @@ -52,28 +49,28 @@ export function getCommands(dir: string) { } export function filename(path: string) { - return fmtFileName(basename(path)) + return fmtFileName(basename(path)); } async function* readPath(dir: string): AsyncGenerator { - try { - const files = await readdir(dir); - for (const file of files) { - const fullPath = join(dir, file); - const fileStats = await stat(fullPath); - if (fileStats.isDirectory()) { - yield* readPath(fullPath); - } else { - /// #if MODE === 'esm' - yield 'file:///'+fullPath; - /// #elif MODE === 'cjs' - yield fullPath; - /// #endif - } + try { + const files = await readdir(dir); + for (const file of files) { + const fullPath = join(dir, file); + const fileStats = await stat(fullPath); + if (fileStats.isDirectory()) { + yield* readPath(fullPath); + } else { + /// #if MODE === 'esm' + yield 'file:///' + fullPath; + /// #elif MODE === 'cjs' + yield fullPath; + /// #endif + } + } + } catch (err) { + throw err; } - } catch (err) { - throw err; - } } //https://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function @@ -86,7 +83,8 @@ export function filePath() { Error.prepareStackTrace = undefined; const path = stack[2].getFileName(); - if(path === null) { - throw Error("Could not get the name of commandModule.") + if (path === null) { + throw Error('Could not get the name of commandModule.'); } - return path; } + return path; +} diff --git a/src/core/operators.ts b/src/core/operators.ts index 8a86f42..cb1f5e9 100644 --- a/src/core/operators.ts +++ b/src/core/operators.ts @@ -3,7 +3,20 @@ * Each function should be modular and testable, not bound to discord / sern * and independent of each other */ -import { concatMap, defaultIfEmpty, EMPTY, every, fromEvent, map, Observable, of, OperatorFunction, pipe, share, switchMap } from 'rxjs'; +import { + concatMap, + defaultIfEmpty, + EMPTY, + every, + fromEvent, + map, + Observable, + of, + OperatorFunction, + pipe, + share, + switchMap, +} from 'rxjs'; import type { PluginResult, VoidResult } from '../types/plugin'; import { Result } from 'ts-results-es'; import { Awaitable } from '../types/handler'; @@ -16,16 +29,18 @@ export function filterMapTo(item: () => V): OperatorFunction { return concatMap(shouldKeep => (shouldKeep ? of(item()) : EMPTY)); } -export function filterMap(cb: (i: In) => Awaitable>): OperatorFunction { +export function filterMap( + cb: (i: In) => Awaitable>, +): OperatorFunction { return pipe( switchMap(async input => cb(input)), concatMap(s => { - if(s.ok) { - return of(s.val) + if (s.ok) { + return of(s.val); } return EMPTY; - }) - ) + }), + ); } /** @@ -48,7 +63,6 @@ export function callPlugin(args: unknown): OperatorFunction< export const arrayifySource = map(src => (Array.isArray(src) ? (src as unknown[]) : [src])); - /** * If the current value in Result stream is an error, calls callback. * This also extracts the Ok value from Result @@ -75,7 +89,5 @@ export const everyPluginOk: OperatorFunction = pipe( ); export const sharedObservable = (e: EventEmitter, eventName: string) => { - return (fromEvent(e, eventName) as Observable).pipe(share()) + return (fromEvent(e, eventName) as Observable).pipe(share()); }; - - diff --git a/src/core/plugins/createPlugin.ts b/src/core/plugins/createPlugin.ts index 2b66d61..2b98534 100644 --- a/src/core/plugins/createPlugin.ts +++ b/src/core/plugins/createPlugin.ts @@ -9,7 +9,7 @@ export function makePlugin( ): Plugin { return { type, - execute + execute, } as Plugin; } /** diff --git a/src/core/predicates.ts b/src/core/predicates.ts index 5925ac9..e811cfa 100644 --- a/src/core/predicates.ts +++ b/src/core/predicates.ts @@ -1,12 +1,23 @@ -import { AnySelectMenuInteraction, AutocompleteInteraction, ButtonInteraction, ChatInputCommandInteraction, MessageContextMenuCommandInteraction, ModalSubmitInteraction, UserContextMenuCommandInteraction } from "discord.js"; -import { InteractionType } from "discord.js"; +import { + AnySelectMenuInteraction, + AutocompleteInteraction, + ButtonInteraction, + ChatInputCommandInteraction, + MessageContextMenuCommandInteraction, + ModalSubmitInteraction, + UserContextMenuCommandInteraction, +} from 'discord.js'; +import { InteractionType } from 'discord.js'; interface InteractionTypable { - type: InteractionType + type: InteractionType; } //discord.js pls fix ur typings or i will >:( type AnyMessageComponentInteraction = AnySelectMenuInteraction | ButtonInteraction; -type AnyCommandInteraction = ChatInputCommandInteraction | MessageContextMenuCommandInteraction | UserContextMenuCommandInteraction; +type AnyCommandInteraction = + | ChatInputCommandInteraction + | MessageContextMenuCommandInteraction + | UserContextMenuCommandInteraction; export function isMessageComponent(i: InteractionTypable): i is AnyMessageComponentInteraction { return i.type === InteractionType.MessageComponent; } diff --git a/src/core/structures/context.ts b/src/core/structures/context.ts index d463882..4fdb011 100644 --- a/src/core/structures/context.ts +++ b/src/core/structures/context.ts @@ -1,19 +1,18 @@ import { Result as Either } from 'ts-results-es'; import { SernError } from './errors'; -import * as assert from 'node:assert' - +import * as assert from 'node:assert'; /** - * @since 3.0.0 - */ + * @since 3.0.0 + */ export abstract class CoreContext { protected constructor(protected ctx: Either) { - assert.ok(typeof ctx.val === 'object' && ctx.val != null) + assert.ok(typeof ctx.val === 'object' && ctx.val != null); } get message(): M { return this.ctx.expect(SernError.MismatchEvent); - } - get interaction(): I { + } + get interaction(): I { return this.ctx.expectErr(SernError.MismatchEvent); } @@ -25,11 +24,11 @@ export abstract class CoreContext { return !this.isMessage(); } //todo: add agnostic options resolver for Context - abstract get options() : unknown - - abstract get id() : string + abstract get options(): unknown; - static wrap(_: unknown): unknown { throw Error("You need to override this method; cannot wrap an abstract class") } - + abstract get id(): string; + + static wrap(_: unknown): unknown { + throw Error('You need to override this method; cannot wrap an abstract class'); + } } - diff --git a/src/core/structures/enums.ts b/src/core/structures/enums.ts index 3e8eabf..288f11d 100644 --- a/src/core/structures/enums.ts +++ b/src/core/structures/enums.ts @@ -15,12 +15,12 @@ * ``` */ export enum CommandType { - Text = 1 << 0, - Slash = 1 << 1, - Both = 3, + Text = 1 << 0, + Slash = 1 << 1, + Both = 3, CtxUser = 1 << 2, - CtxMsg = 1 << 3, - Button = 1 << 4, + CtxMsg = 1 << 3, + Button = 1 << 4, StringSelect = 1 << 5, Modal = 1 << 6, ChannelSelect = 1 << 7, diff --git a/src/core/structures/index.ts b/src/core/structures/index.ts index 7984c53..5bd8ce9 100644 --- a/src/core/structures/index.ts +++ b/src/core/structures/index.ts @@ -1,3 +1,3 @@ export * from './enums'; -export * from './context' -export * from './sernEmitter' +export * from './context'; +export * from './sernEmitter'; diff --git a/src/core/structures/sernEmitter.ts b/src/core/structures/sernEmitter.ts index 43bfd82..318a3d7 100644 --- a/src/core/structures/sernEmitter.ts +++ b/src/core/structures/sernEmitter.ts @@ -84,4 +84,3 @@ export class SernEmitter extends EventEmitter { ); } } - diff --git a/src/handler/events/dispatchers/provideArgs.ts b/src/handler/events/dispatchers/provideArgs.ts index 12fca6c..661ef94 100644 --- a/src/handler/events/dispatchers/provideArgs.ts +++ b/src/handler/events/dispatchers/provideArgs.ts @@ -12,13 +12,18 @@ export function contextArgs( /* * @overload */ -export function contextArgs(wrappable: ChatInputCommandInteraction): () => [Context, ['slash', SlashOptions]]; +export function contextArgs( + wrappable: ChatInputCommandInteraction, +): () => [Context, ['slash', SlashOptions]]; /** * function overloads to create an arguments list for Context * @param wrap * @param messageArgs */ -export function contextArgs(wrappable: Message | ChatInputCommandInteraction, messageArgs?: string[]) { +export function contextArgs( + wrappable: Message | ChatInputCommandInteraction, + messageArgs?: string[], +) { const ctx = Context.wrap(wrappable); const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options]; return () => [ctx, args] as [Context, Args]; @@ -27,4 +32,3 @@ export function contextArgs(wrappable: Message | ChatInputCommandInteraction, me export function interactionArg(interaction: T) { return () => [interaction] as [T]; } - diff --git a/src/handler/events/generic.ts b/src/handler/events/generic.ts index d63fc88..56459e5 100644 --- a/src/handler/events/generic.ts +++ b/src/handler/events/generic.ts @@ -1,34 +1,34 @@ -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 { + 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, Module } from "../../types/module"; -import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from "./dispatchers"; -import { isAutocomplete } from "../../core/predicates"; -import { ObservableInput, pipe, switchMap} from "rxjs"; -import { SernEmitter } from "../../core"; +import { defaultModuleLoader } from '../../core/module-loading'; +import { Processed } from '../../types/core'; +import { BothCommand, CommandModule, Module } from '../../types/module'; +import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from './dispatchers'; +import { isAutocomplete } from '../../core/predicates'; +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"; - - +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> + makeModule: (event: Narrowed) => Awaitable>, ) { - return (pred: (i: Source) => i is Narrowed) => - source.pipe( - filter(pred), - filterMap(makeModule) - ) + return (pred: (i: Source) => i is Narrowed) => source.pipe(filter(pred), filterMap(makeModule)); } /** * @@ -43,37 +43,33 @@ export function createInteractionHandler( ) { 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 })) - ) - } - ) + 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 + 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)) - }) + 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. @@ -81,21 +77,27 @@ export function createMessageHandler( * @returns A unique string ID based on the type and properties of the interaction object. */ function createId(event: T) { - let id: string; - switch(event.type) { - case InteractionType.MessageComponent: { + let id: string; + switch (event.type) { + case InteractionType.MessageComponent: + { id = `${event.customId}__C${event.componentType}`; - } break; - case InteractionType.ApplicationCommand: - case InteractionType.ApplicationCommandAutocomplete: { + } + break; + case InteractionType.ApplicationCommand: + case InteractionType.ApplicationCommandAutocomplete: + { id = `${event.commandName}__A${event.commandType}`; - console.log(id) - } break; - case InteractionType.ModalSubmit: { + console.log(id); + } + break; + case InteractionType.ModalSubmit: + { id = `${event.customId}__C1`; - } break; - } - return id; + } + break; + } + return id; } function createDispatcher({ @@ -107,7 +109,7 @@ function createDispatcher({ }) { switch (module.type) { case CommandType.Text: - throw Error(SernError.MismatchEvent+ " Found a text module in interaction stream."); + throw Error(SernError.MismatchEvent + ' Found a text module in interaction stream.'); case CommandType.Slash: case CommandType.Both: { if (isAutocomplete(event)) { @@ -117,7 +119,7 @@ function createDispatcher({ * too different from regular command modules */ return dispatchAutocomplete(module as Processed, event); - } + } return dispatchCommand(module, contextArgs(event as ChatInputCommandInteraction)); } default: @@ -125,16 +127,15 @@ function createDispatcher({ } } - export function buildModules( - input: ObservableInput, sernEmitter: SernEmitter + input: ObservableInput, + sernEmitter: SernEmitter, ) { return pipe( switchMap(() => Files.buildModuleStream(input)), errTap(error => { sernEmitter.emit('module.register', SernEmitter.failure(undefined, error)); }), - map(module => ({ module, absPath: module[sernMeta].fullPath })) + map(module => ({ module, absPath: module[sernMeta].fullPath })), ); } - diff --git a/src/handler/events/interactions.ts b/src/handler/events/interactions.ts index aa93e78..e75c1fc 100644 --- a/src/handler/events/interactions.ts +++ b/src/handler/events/interactions.ts @@ -1,37 +1,30 @@ import { BaseInteraction, Interaction } from 'discord.js'; -import { - catchError, - concatMap, - finalize, - merge, -} from 'rxjs'; +import { catchError, concatMap, finalize, merge } from 'rxjs'; import { SernError } from '../../core/structures/errors'; import { executeModule, makeModuleExecutor } from './observableHandling'; import { ErrorHandling, handleError } from '../../core/contracts/errorHandling'; import { SernEmitter, WebsocketStrategy } from '../../core'; -import { sharedObservable } from '../../core/operators' +import { sharedObservable } from '../../core/operators'; 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 { createInteractionHandler } from './generic'; - export function makeInteractionCreate([s, err, log, modules, client]: [ SernEmitter, ErrorHandling, Logging | undefined, ModuleManager, - EventEmitter -] -) { + EventEmitter, +]) { const interactionStream$ = sharedObservable(client, 'interactionCreate'); const handle = createInteractionHandler(interactionStream$, modules); const interactionHandler$ = merge( handle(isMessageComponent), handle(isAutocomplete), handle(isCommand), - handle(isModal) + handle(isModal), ); return interactionHandler$ .pipe( diff --git a/src/handler/events/messages.ts b/src/handler/events/messages.ts index 01a7720..4fa8f6c 100644 --- a/src/handler/events/messages.ts +++ b/src/handler/events/messages.ts @@ -37,29 +37,26 @@ export function makeMessageCreate( ModuleManager, EventEmitter, ], - defaultPrefix: string | undefined + defaultPrefix: string | undefined, ) { - if(!defaultPrefix) { - log?.debug({ message: 'No prefix found. message handler shut down' }) - return EMPTY.subscribe() + if (!defaultPrefix) { + log?.debug({ message: 'No prefix found. message handler shut down' }); + return EMPTY.subscribe(); } const messageStream$ = sharedObservable(client, 'messageCreate'); const handler = createMessageHandler(messageStream$, defaultPrefix, modules); - const messageHandler = handler( - ignoreNonBot(defaultPrefix) as (m: Message) => m is Message - ) - return messageHandler - .pipe( - makeModuleExecutor(module => { - s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure)); - }), - concatMap(payload => executeModule(s, payload)), - catchError(handleError(err, log)), - finalize(() => { - log?.info({ message: 'messageCreate stream closed or reached end of lifetime' }); - useContainerRaw() - ?.disposeAll() - .then(() => log?.info({ message: 'Cleaning container and crashing' })); - }), - ) + const messageHandler = handler(ignoreNonBot(defaultPrefix) as (m: Message) => m is Message); + return messageHandler.pipe( + makeModuleExecutor(module => { + s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure)); + }), + concatMap(payload => executeModule(s, payload)), + catchError(handleError(err, log)), + finalize(() => { + log?.info({ message: 'messageCreate stream closed or reached end of lifetime' }); + useContainerRaw() + ?.disposeAll() + .then(() => log?.info({ message: 'Cleaning container and crashing' })); + }), + ); } diff --git a/src/handler/events/observableHandling.ts b/src/handler/events/observableHandling.ts index d588fe1..ab1a7a8 100644 --- a/src/handler/events/observableHandling.ts +++ b/src/handler/events/observableHandling.ts @@ -6,20 +6,18 @@ import { callPlugin, everyPluginOk, filterMapTo } from '../../core/operators'; import type { ImportPayload, Processed } from '../../types/core'; import type { ControlPlugin, VoidResult } from '../../types/plugin'; import { Awaitable } from '../../types/handler'; -import { Message } from 'discord.js' +import { Message } from 'discord.js'; function hasPrefix(prefix: string, content: string) { const prefixInContent = content.slice(0, prefix.length); return prefixInContent.localeCompare(prefix, undefined, { sensitivity: 'accent' }) === 0; } - /** * Ignores messages from any person / bot except itself * @param prefix */ export function ignoreNonBot(prefix: string) { - return ({ author, content }: Message) => - !author.bot && hasPrefix(prefix, content); + return ({ author, content }: Message) => !author.bot && hasPrefix(prefix, content); } /** @@ -76,7 +74,7 @@ export function createResultResolver< const task$ = config.createStream(args); return task$.pipe( tap(result => { - result.err && config.onStop?.(args.module); + result.err && config.onStop?.(args.module); }), everyPluginOk, filterMapTo(() => config.onNext(args)), diff --git a/src/handler/events/ready.ts b/src/handler/events/ready.ts index df08d6c..77df574 100644 --- a/src/handler/events/ready.ts +++ b/src/handler/events/ready.ts @@ -1,7 +1,7 @@ import { ObservableInput, fromEvent, of, take } from 'rxjs'; import { callInitPlugins } from './observableHandling'; import { CommandType } from '../../core/structures'; -import { SernError } from '../../core/structures/errors' +import { SernError } from '../../core/structures/errors'; import { Result } from 'ts-results-es'; import type { ModuleManager } from '../../core/contracts'; import { SernEmitter, PlatformStrategy, DispatchType } from '../../core'; @@ -12,7 +12,9 @@ import * as assert from 'node:assert'; import { buildModules } from './generic'; export function startReadyEvent( - [sEmitter, errorHandler, , moduleManager, client]: ServerlessDependencyList | WebsocketDependencyList, + [sEmitter, errorHandler, , moduleManager, client]: + | ServerlessDependencyList + | WebsocketDependencyList, input: ObservableInput, ) { const ready$ = fromEvent(client!, 'interactionCreate').pipe(take(1)); @@ -40,19 +42,15 @@ export function startReadyEvent( }); } - function registerModule>( manager: ModuleManager, module: T, ): Result { - const { id, fullPath } = module[sernMeta]; - if(module.type === CommandType.Both || module.type === CommandType.Text) { - assert.ok('alias' in module) - assert.ok(Array.isArray(module.alias)) - module.alias?.forEach(a => manager.set(`${a}__A0` , fullPath)) + const { id, fullPath } = module[sernMeta]; + if (module.type === CommandType.Both || module.type === CommandType.Text) { + assert.ok('alias' in module); + assert.ok(Array.isArray(module.alias)); + module.alias?.forEach(a => manager.set(`${a}__A0`, fullPath)); } - return Result.wrap(() => manager.set(id, fullPath)) + return Result.wrap(() => manager.set(id, fullPath)); } - - - diff --git a/src/handler/events/userDefined.ts b/src/handler/events/userDefined.ts index bcc2df2..9b4439f 100644 --- a/src/handler/events/userDefined.ts +++ b/src/handler/events/userDefined.ts @@ -5,7 +5,7 @@ import type { CommandModule, EventModule } from '../../types/module'; import type { EventEmitter } from 'node:events'; import { SernEmitter } from '../../core'; import type { ErrorHandling, Logging } from '../../core/contracts'; -import { EventType } from '../../core/structures' +import { EventType } from '../../core/structures'; import { SernError } from '../../core/structures/errors'; import { eventDispatcher } from './dispatchers'; import { handleError } from '../../core/contracts/errorHandling'; @@ -32,29 +32,31 @@ export function makeEventsHandler( ); } }; - of(null).pipe( - buildModules(eventsPath, s), - callInitPlugins({ - onStop: module => - s.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure)), - onNext: ({ module }) => { - s.emit('module.register', SernEmitter.success(module)); - return module; - }, - }), - map(intoDispatcher), - /** - * Where all events are turned on - */ - mergeAll(), - catchError(handleError(err, log)), - finalize(() => { - log?.info({ message: 'an event module reached end of lifetime' }); - useContainerRaw() - ?.disposeAll() - .then(() => { - log?.info({ message: 'Cleaning container and crashing' }); - }); - }), - ).subscribe(); + of(null) + .pipe( + buildModules(eventsPath, s), + callInitPlugins({ + onStop: module => + s.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure)), + onNext: ({ module }) => { + s.emit('module.register', SernEmitter.success(module)); + return module; + }, + }), + map(intoDispatcher), + /** + * Where all events are turned on + */ + mergeAll(), + catchError(handleError(err, log)), + finalize(() => { + log?.info({ message: 'an event module reached end of lifetime' }); + useContainerRaw() + ?.disposeAll() + .then(() => { + log?.info({ message: 'Cleaning container and crashing' }); + }); + }), + ) + .subscribe(); } diff --git a/src/handler/sern.ts b/src/handler/sern.ts index 0ad0b84..a1e7045 100644 --- a/src/handler/sern.ts +++ b/src/handler/sern.ts @@ -27,7 +27,9 @@ export function init(wrapper: Wrapper) { const dependencies = dependenciesAnd(['@sern/modules', '@sern/client']); if (wrapper.events !== undefined) { makeEventsHandler( - dependenciesAnd(['@sern/client']), wrapper.events, wrapper.containerConfig + dependenciesAnd(['@sern/client']), + wrapper.events, + wrapper.containerConfig, ); } startReadyEvent(dependencies, getCommands(wrapper.commands)); @@ -35,16 +37,15 @@ export function init(wrapper: Wrapper) { makeInteractionCreate(dependencies); const endTime = performance.now(); dependencies[2]?.info({ message: `sern : ${(endTime - startTime).toFixed(2)} ms` }); - } /** - * @deprecated - Please import the function directly: - * ```ts - * import { makeDependencies } from '@sern/handler' - * - * ``` - */ -export { makeDependencies } + * @deprecated - Please import the function directly: + * ```ts + * import { makeDependencies } from '@sern/handler' + * + * ``` + */ +export { makeDependencies }; /** * @since 1.0.0 * The object passed into every plugin to control a command's behavior @@ -53,4 +54,3 @@ export const controller = { next: ok, stop: err, }; - diff --git a/src/index.ts b/src/index.ts index 211ae9b..e38453d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,6 @@ export * from './types/handler'; export * from './types/module'; export * from './types/plugin'; export * from './core'; -export { controller } from './handler/sern' -export { commandModule, eventModule } from './commands' -export { Context } from './classic/context' +export { controller } from './handler/sern'; +export { commandModule, eventModule } from './commands'; +export { Context } from './classic/context'; diff --git a/src/types/core.ts b/src/types/core.ts index cf87d0e..9beb715 100644 --- a/src/types/core.ts +++ b/src/types/core.ts @@ -1,10 +1,21 @@ -import { type EventEmitter } from "node:events"; -import { ErrorHandling, Logging, ModuleManager, SernEmitter } from "../core"; -import { Container, UnpackFunction } from "iti"; +import { type EventEmitter } from 'node:events'; +import { ErrorHandling, Logging, ModuleManager, SernEmitter } from '../core'; +import { Container, UnpackFunction } from 'iti'; -export type ModuleStore = Map -export type ServerlessDependencyList = [ SernEmitter,ErrorHandling, Logging | undefined, ModuleManager]; -export type WebsocketDependencyList = [SernEmitter,ErrorHandling, Logging | undefined, ModuleManager, EventEmitter]; +export type ModuleStore = Map; +export type ServerlessDependencyList = [ + SernEmitter, + ErrorHandling, + Logging | undefined, + ModuleManager, +]; +export type WebsocketDependencyList = [ + SernEmitter, + ErrorHandling, + Logging | undefined, + ModuleManager, + EventEmitter, +]; /** * After modules are transformed, name and description are given default values if none * are provided to Module. This type represents that transformation @@ -22,20 +33,18 @@ export interface CoreDependencies { '@sern/errors': Singleton; } /** - * To support older versions. Type alias for WebsocketDependencies - * @deprecated - */ -export type Dependencies = WebsocketDependencies + * To support older versions. Type alias for WebsocketDependencies + * @deprecated + */ +export type Dependencies = WebsocketDependencies; export interface ServerlessDependencies extends CoreDependencies { - '@sern/client': never + '@sern/client': never; } export interface WebsocketDependencies extends CoreDependencies { '@sern/client': Singleton; } -export type AnyDependencies = - | ServerlessDependencies - | WebsocketDependencies; +export type AnyDependencies = ServerlessDependencies | WebsocketDependencies; //prettier-ignore export type MapDeps = T extends [ @@ -58,8 +67,8 @@ export interface DependencyConfiguration { export interface ImportPayload { module: T; - absPath: string -}; + absPath: string; +} export interface Wrapper { commands: string; @@ -67,5 +76,5 @@ export interface Wrapper { events?: string; containerConfig: { get: (...keys: (keyof WebsocketDependencies)[]) => unknown[]; - } + }; } diff --git a/src/types/handler.ts b/src/types/handler.ts index 82cf19d..53b39c1 100644 --- a/src/types/handler.ts +++ b/src/types/handler.ts @@ -1,4 +1,8 @@ -import type { InteractionReplyOptions, MessageReplyOptions, CommandInteractionOptionResolver } from 'discord.js'; +import type { + InteractionReplyOptions, + MessageReplyOptions, + CommandInteractionOptionResolver, +} from 'discord.js'; import { Processed } from './core'; import { AnyModule, CommandModule, EventModule } from './module'; import { PayloadType } from '../core'; @@ -14,13 +18,11 @@ export type Args = ParseType<{ text: string[]; slash: SlashOptions }>; export type SlashOptions = Omit; - export type ReplyOptions = | string | Omit | MessageReplyOptions; - export type AnyDefinedModule = Processed; export type Payload = | { type: PayloadType.Success; module: AnyModule } @@ -32,4 +34,4 @@ export interface SernEventsMapping { 'module.activate': [Payload]; error: [Payload]; warning: [Payload]; -}; +} diff --git a/src/types/module.ts b/src/types/module.ts index a85aa43..ef9364b 100644 --- a/src/types/module.ts +++ b/src/types/module.ts @@ -10,7 +10,7 @@ import type { ApplicationCommandSubGroupData, BaseApplicationCommandOptionsData, } from 'discord.js'; -import { +import { AutocompleteInteraction, ButtonInteraction, ChannelSelectMenuInteraction, @@ -21,13 +21,12 @@ import { RoleSelectMenuInteraction, StringSelectMenuInteraction, UserContextMenuCommandInteraction, - UserSelectMenuInteraction - -} from "discord.js"; -import { InitArgs, } from "../core"; -import { Args, Payload, SlashOptions } from "../types/handler"; -import { Context } from "../classic/context"; -import { Processed } from "../types/core"; + UserSelectMenuInteraction, +} from 'discord.js'; +import { InitArgs } from '../core'; +import { Args, Payload, SlashOptions } from '../types/handler'; +import { Context } from '../classic/context'; +import { Processed } from '../types/core'; import { CommandType, PluginType } from '../core/structures/enums'; import type { Awaitable, SernEventsMapping } from './handler'; import type { InitPlugin, ControlPlugin } from './plugin'; @@ -36,8 +35,8 @@ import type { AnyCommandPlugin, AnyEventPlugin } from './plugin'; import { sernMeta } from '../commands'; interface CommandMeta { - fullPath: string; - id: string; + fullPath: string; + id: string; } export interface Module { @@ -46,14 +45,14 @@ export interface Module { onEvent: ControlPlugin[]; plugins: InitPlugin[]; description?: string; - [sernMeta] : CommandMeta + [sernMeta]: CommandMeta; execute: (...args: any[]) => Awaitable; } export interface CommandTypeModule extends Module { - type: CommandType + type: CommandType; } export interface EventTypeModule extends Module { - type: EventType + type: EventType; } export interface SernEventCommand extends Module { @@ -68,7 +67,6 @@ export interface ExternalEventCommand extends Module { execute(...args: unknown[]): Awaitable; } - export interface ContextMenuUser extends Module { type: CommandType.CtxUser; execute: (ctx: UserContextMenuCommandInteraction) => Awaitable; @@ -195,7 +193,7 @@ export interface CommandArgsMatrix { [PluginType.Control]: [/* library coupled */ ModalSubmitInteraction]; [PluginType.Init]: [InitArgs>]; }; -}; +} export interface EventArgsMatrix { [EventType.Discord]: { @@ -210,7 +208,7 @@ export interface EventArgsMatrix { [PluginType.Control]: unknown[]; [PluginType.Init]: [InitArgs>]; }; -}; +} export type EventModule = DiscordEventCommand | SernEventCommand | ExternalEventCommand; export type CommandModule = | TextCommand @@ -243,13 +241,13 @@ export interface CommandModuleDefs { [CommandType.MentionableSelect]: MentionableSelectCommand; [CommandType.UserSelect]: UserSelectCommand; [CommandType.Modal]: ModalSubmitCommand; -}; +} export interface EventModuleDefs { [EventType.Sern]: SernEventCommand; [EventType.Discord]: DiscordEventCommand; [EventType.External]: ExternalEventCommand; -}; +} export interface SernAutocompleteData extends Omit { diff --git a/src/types/plugin.ts b/src/types/plugin.ts index 7c1ab44..621564b 100644 --- a/src/types/plugin.ts +++ b/src/types/plugin.ts @@ -40,4 +40,3 @@ export interface ControlPlugin { export type AnyCommandPlugin = ControlPlugin | InitPlugin<[InitArgs>]>; export type AnyEventPlugin = ControlPlugin | InitPlugin<[InitArgs>]>; - diff --git a/tsup.config.js b/tsup.config.js index 7f7afbb..f347d7f 100644 --- a/tsup.config.js +++ b/tsup.config.js @@ -19,10 +19,8 @@ export default defineConfig([ target: 'node16', tsconfig: './tsconfig-esm.json', outDir: './dist/esm', - splitting: false, - esbuildPlugins: [ - ifdefPlugin({ variables: { MODE: 'esm' }, verbose: true }), - ], + splitting: false, + esbuildPlugins: [ifdefPlugin({ variables: { MODE: 'esm' }, verbose: true })], outExtension() { return { js: '.mjs',