diff --git a/src/handler/events/observableHandling.ts b/src/handler/events/observableHandling.ts index 04112f5..64a9bfe 100644 --- a/src/handler/events/observableHandling.ts +++ b/src/handler/events/observableHandling.ts @@ -1,12 +1,12 @@ import type { Message } from 'discord.js'; import { Observable, throwError } from 'rxjs'; import { SernError } from '../structures/errors'; -import type { Module, ModuleDefs } from '../structures/module'; +import type { Module, CommandModuleDefs } from '../structures/module'; import { correctModuleType } from '../utilities/predicates'; import type { Result } from 'ts-results'; -export function filterCorrectModule(cmdType: T) { +export function filterCorrectModule(cmdType: T) { return (src: Observable) => - new Observable(subscriber => { + new Observable(subscriber => { return src.subscribe({ next(mod) { if (mod === undefined) { diff --git a/src/handler/events/readyEvent.ts b/src/handler/events/readyEvent.ts index e562b00..7ae5962 100644 --- a/src/handler/events/readyEvent.ts +++ b/src/handler/events/readyEvent.ts @@ -6,13 +6,13 @@ import type { Result } from 'ts-results'; import { Err, Ok } from 'ts-results'; import type { Awaitable } from 'discord.js'; import { ApplicationCommandType, ComponentType } from 'discord.js'; -import type { CommandModule, Module } from '../structures/module'; +import type { CommandModule } from '../structures/module'; import { match } from 'ts-pattern'; import { SernError } from '../structures/errors'; -import type { DefinedCommandModule, DefinedModule } from '../../types/handler'; +import type { DefinedCommandModule } from '../../types/handler'; import { CommandType, PluginType } from '../structures/enums'; import { errTap } from './observableHandling'; -import { processCommandPlugins$ } from './userDefinedEventsHandling'; +import { processCommandPlugins } from './userDefinedEventsHandling'; export function onReady(wrapper: Wrapper) { const { client, commands } = wrapper; @@ -37,11 +37,8 @@ export function onReady(wrapper: Wrapper) { ); const processPlugins$ = processCommandFiles$.pipe( concatMap(mod => { - const cmdPluginRes = processCommandPlugins$(wrapper, mod); - if (cmdPluginRes.err) { - return cmdPluginRes.val; - } - return of({ mod, cmdPluginRes: cmdPluginRes.val }); + const cmdPluginRes = processCommandPlugins(wrapper, mod); + return of({ mod, cmdPluginRes }); }), ); @@ -84,9 +81,9 @@ export function onReady(wrapper: Wrapper) { }); } -function registerModule(mod: DefinedModule): Result { +function registerModule(mod: DefinedCommandModule): Result { const name = mod.name; - return match(mod) + return match(mod) .with({ type: CommandType.Text }, mod => { mod.alias?.forEach(a => Files.TextCommands.aliases.set(a, mod)); Files.TextCommands.text.set(name, mod); diff --git a/src/handler/events/userDefinedEventsHandling.ts b/src/handler/events/userDefinedEventsHandling.ts index 98823ef..2d4f3bf 100644 --- a/src/handler/events/userDefinedEventsHandling.ts +++ b/src/handler/events/userDefinedEventsHandling.ts @@ -1,76 +1,27 @@ -import { CommandType } from '../structures/enums'; -import { from, fromEvent, map, throwError } from 'rxjs'; -import { SernError } from '../structures/errors'; +import { from, fromEvent, map } from 'rxjs'; import * as Files from '../utilities/readFile'; import { buildData, ExternalEventEmitters } from '../utilities/readFile'; import { controller } from '../sern'; -import type { DefinedEventModule, DefinedModule, SpreadParams } from '../../types/handler'; +import type { DefinedCommandModule, DefinedEventModule, SpreadParams } from '../../types/handler'; import type { EventModule } from '../structures/module'; import type Wrapper from '../structures/wrapper'; import { basename } from 'path'; import { match, P } from 'ts-pattern'; import { isDiscordEvent, isSernEvent } from '../utilities/predicates'; -import type { CommandPlugin } from '../plugins/plugin'; -import { Err, Ok } from 'ts-results'; import { errTap } from './observableHandling'; /** * Utility function to process command plugins for all Modules * @param client - * @param sernEmitter * @param mod - * @param absPath */ -export function processCommandPlugins$( - { client, sernEmitter }: Wrapper, - mod: T, -) { - return match(mod as DefinedModule) - .with({ type: CommandType.External }, m => - Ok( - m.plugins.map(plug => ({ - ...plug, - name: plug?.name ?? 'Unnamed Plugin', - description: plug?.description ?? '...', - execute: plug.execute(ExternalEventEmitters.get(m.emitter)!, m, controller), - })), - ), - ) - .with({ type: CommandType.Sern }, m => - Ok( - m.plugins.map(plug => ({ - ...plug, - name: plug?.name ?? 'Unnamed Plugin', - description: plug?.description ?? '...', - execute: plug.execute(sernEmitter!, m, controller), - })), - ), - ) - .with( - { - type: P.not(CommandType.Autocomplete), - plugins: P.array({} as P.infer), - }, - m => { - return Ok( - m.plugins.map(plug => ({ - ...plug, - name: plug?.name ?? 'Unnamed Plugin', - description: plug?.description ?? '...', - execute: plug.execute(client, m, controller), - })), - ); - }, - ) - .otherwise(() => - Err( - throwError( - () => - SernError.NonValidModuleType + - `. You cannot use command plugins and Autocomplete.`, - ), - ), - ); +export function processCommandPlugins({ client }: Wrapper, mod: T) { + return mod.plugins.map(plug => ({ + ...plug, + name: plug?.name ?? 'Unnamed Plugin', + description: plug?.description ?? '...', + execute: plug.execute(client, mod, controller), + })); } export function processEvents( diff --git a/src/handler/plugins/plugin.ts b/src/handler/plugins/plugin.ts index b1d3630..6c8b641 100644 --- a/src/handler/plugins/plugin.ts +++ b/src/handler/plugins/plugin.ts @@ -14,12 +14,18 @@ import type { Awaitable, Client } from 'discord.js'; import type { Err, Ok, Result } from 'ts-results'; import type { DefinitelyDefined, Module, Override } from '../..'; -import { CommandType } from '../..'; -import type { AutocompleteCommand, BaseModule, ModuleDefs } from '../structures/module'; +import type { CommandType } from '../..'; +import type { + BaseModule, + EventModule, + CommandModuleDefs, + CommandModule, +} from '../structures/module'; import { PluginType } from '../structures/enums'; import type { EventEmitter } from 'events'; import type { ExternalEventCommand, SernEventCommand } from '../structures/events'; import type SernEmitter from '../sernEmitter'; +import type { AutocompleteInteraction } from 'discord.js'; export interface Controller { next: () => Ok; @@ -33,14 +39,14 @@ type BasePlugin = Override< } >; -export type CommandPlugin = { +export type CommandPlugin = { [K in T]: Override< BasePlugin, { type: PluginType.Command; execute: ( wrapper: Client, - module: DefinitelyDefined, + module: DefinitelyDefined, controller: Controller, ) => Awaitable>; } @@ -71,13 +77,24 @@ export type SernEmitterPlugin = Override< } >; -export type EventPlugin = { +export type AutocompletePlugin = Override< + BaseModule, + { + type: PluginType.Event; + execute: ( + autocmp: AutocompleteInteraction, + controlller: Controller, + ) => Awaitable; + } +>; + +export type EventPlugin = { [K in T]: Override< BasePlugin, { type: PluginType.Event; execute: ( - event: Parameters, + event: Parameters, controller: Controller, ) => Awaitable>; } @@ -92,7 +109,7 @@ export type EventPlugin = { // } export type ModuleNoPlugins = { - [T in keyof ModuleDefs]: Omit; + [T in keyof CommandModuleDefs]: Omit; }; function isEventPlugin( @@ -107,31 +124,54 @@ function isCommandPlugin( } //TODO: I WANT BETTER TYPINGS AHHHHHHHHHHHHHHH // Maybe add overlaods -export function sernModule( + +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.Slash], +): Module; +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.Text], +): Module; +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.Button], +): Module; +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.Both], +): Module; +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.MenuMsg], +): Module; +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.MenuSelect], +): Module; + +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.Modal], +): Module; + +export function sernModule( + plugin: (CommandPlugin | EventPlugin)[], + mod: ModuleNoPlugins[CommandType.MenuUser], +): Module; +export function sernModule( plugin: (CommandPlugin | EventPlugin)[], mod: ModuleNoPlugins[T], -): Module { +): CommandModule { const onEvent = plugin.filter(isEventPlugin); const plugins = plugin.filter(isCommandPlugin); - if (mod.type === CommandType.Autocomplete) { - throw new Error( - 'You cannot use this function declaration for Autocomplete Interactions! use the raw object for options or' + - 'sernAutoComplete function', - ); - } else - return { - onEvent, - plugins, - ...mod, - } as Module; -} - -export function sernAutocomplete( - onEvent: EventPlugin[], - mod: Omit, -): Omit { return { onEvent, + plugins, ...mod, - }; + } as CommandModule; +} + +export function eventModule(): EventModule { + return {} as EventModule; } diff --git a/src/handler/structures/module.ts b/src/handler/structures/module.ts index 22b1b73..f4e1c3e 100644 --- a/src/handler/structures/module.ts +++ b/src/handler/structures/module.ts @@ -18,9 +18,9 @@ import type { UserContextMenuCommandInteraction, } from 'discord.js'; import type { Args, Override, SlashOptions } from '../../types/handler'; -import type { CommandPlugin, EventPlugin } from '../plugins/plugin'; +import type { AutocompletePlugin, CommandPlugin, EventPlugin } from '../plugins/plugin'; import type Context from './context'; -import { CommandType, PluginType } from './enums'; +import { CommandType, EventType, PluginType } from './enums'; import type { DiscordEventCommand, ExternalEventCommand, SernEventCommand } from './events'; export interface BaseModule { @@ -122,9 +122,10 @@ export type ModalSubmitCommand = Override< export type AutocompleteCommand = Override< BaseModule, { - type: CommandType.Autocomplete; - name: string; - onEvent: EventPlugin[]; + name?: never; + description?: never; + type?: never; + onEvent: AutocompletePlugin[]; execute: (ctx: AutocompleteInteraction) => Awaitable; } >; @@ -137,14 +138,13 @@ export type CommandModule = | ContextMenuMsg | ButtonCommand | SelectMenuCommand - | ModalSubmitCommand - | AutocompleteCommand; + | ModalSubmitCommand; export type Module = CommandModule | EventModule; //https://stackoverflow.com/questions/64092736/alternative-to-switch-statement-for-typescript-discriminated-union // Explicit Module Definitions for mapping -export type ModuleDefs = { +export type CommandModuleDefs = { [CommandType.Text]: TextCommand; [CommandType.Slash]: SlashCommand; [CommandType.Both]: BothCommand; @@ -153,10 +153,12 @@ export type ModuleDefs = { [CommandType.Button]: ButtonCommand; [CommandType.MenuSelect]: SelectMenuCommand; [CommandType.Modal]: ModalSubmitCommand; - [CommandType.Autocomplete]: AutocompleteCommand; - [CommandType.Sern]: SernEventCommand; - [CommandType.Discord]: DiscordEventCommand; - [CommandType.External]: ExternalEventCommand; +}; + +export type EventModuleDefs = { + [EventType.Sern]: SernEventCommand; + [EventType.Discord]: DiscordEventCommand; + [EventType.External]: ExternalEventCommand; }; //TODO: support deeply nested Autocomplete @@ -171,7 +173,7 @@ export type SernAutocompleteData = Override< | ApplicationCommandOptionType.String | ApplicationCommandOptionType.Number | ApplicationCommandOptionType.Integer; - command: Omit; + command: AutocompleteCommand; } >; diff --git a/src/handler/utilities/predicates.ts b/src/handler/utilities/predicates.ts index b5d8b28..7809902 100644 --- a/src/handler/utilities/predicates.ts +++ b/src/handler/utilities/predicates.ts @@ -1,4 +1,4 @@ -import type { EventModule, Module, ModuleDefs } from '../structures/module'; +import type { CommandModuleDefs, EventModule, Module } from '../structures/module'; import type { Awaitable, ButtonInteraction, @@ -9,23 +9,23 @@ import type { SelectMenuInteraction, UserContextMenuCommandInteraction, } from 'discord.js'; -import type { - DiscordEventCommand, - ExternalEventCommand, - SernEventCommand, -} from '../structures/events'; -import { CommandType } from '../..'; import { AutocompleteInteraction, Interaction, InteractionType, ModalSubmitInteraction, } from 'discord.js'; +import type { + DiscordEventCommand, + ExternalEventCommand, + SernEventCommand, +} from '../structures/events'; +import { EventType } from '../..'; -export function correctModuleType( +export function correctModuleType( plug: Module | undefined, type: T, -): plug is ModuleDefs[T] { +): plug is CommandModuleDefs[T] { // Another way to check if type is equivalent, // It will check based on flag system instead return plug !== undefined && (plug.type & type) !== 0; @@ -78,7 +78,7 @@ export function isPromise(promiseLike: Awaitable): promiseLike is PromiseL } export function isDiscordEvent(el: EventModule): el is DiscordEventCommand { - return el.type === CommandType.Discord; + return el.type === EventType.Discord; } export function isSernEvent(el: EventModule): el is SernEventCommand { return !isDiscordEvent(el); @@ -89,5 +89,5 @@ export function isExternalEvent(el: EventModule): el is ExternalEventCommand { } export function isEventModule(module: Module): module is EventModule { - return [CommandType.Discord, CommandType.Sern, CommandType.External].includes(module.type); + return [EventType.Sern, EventType.Discord, EventType.External].includes(module.type); }