mirror of
https://github.com/sern-handler/handler
synced 2026-06-06 01:16:55 +00:00
chore: rename, wip refactoring
This commit is contained in:
@@ -1,64 +0,0 @@
|
||||
import { readdirSync, statSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { type Observable, from, mergeMap } from 'rxjs';
|
||||
import { SernError } from '../structures/errors';
|
||||
import { type Result, Err, Ok } from 'ts-results-es';
|
||||
import { ImportPayload } from '../../types/handler';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
// Courtesy @Townsy45
|
||||
function readPath(dir: string, arrayOfFiles: string[] = []): string[] {
|
||||
try {
|
||||
const files = readdirSync(dir);
|
||||
for (const file of files) {
|
||||
if (statSync(dir + '/' + file).isDirectory()) readPath(dir + '/' + file, arrayOfFiles);
|
||||
else arrayOfFiles.push(join(dir, '/', file));
|
||||
}
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return arrayOfFiles;
|
||||
}
|
||||
export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
|
||||
// export const isLazy = (n: string) => n.indexOf(".lazy.", n.length-9) !== -1;
|
||||
|
||||
export async function defaultModuleLoader<T>(
|
||||
absPath: string,
|
||||
): Promise<Result<ImportPayload<T>, SernError>> {
|
||||
// prettier-ignore
|
||||
let module: T | undefined
|
||||
/// #if MODE === 'esm'
|
||||
= (await import(pathToFileURL(absPath).toString())).default
|
||||
/// #elif MODE === 'cjs'
|
||||
= require(absPath).default; // eslint-disable-line
|
||||
/// #endif
|
||||
if (module === undefined) {
|
||||
return Err(SernError.UndefinedModule);
|
||||
}
|
||||
try {
|
||||
module = new (module as unknown as new () => T)();
|
||||
} catch {}
|
||||
return Ok({ module, absPath });
|
||||
}
|
||||
|
||||
/**
|
||||
* a directory string is converted into a stream of modules.
|
||||
* starts the stream of modules that sern needs to process on init
|
||||
* @returns {Observable<{ mod: Module; absPath: string; }[]>} data from command files
|
||||
* @param commandDir
|
||||
*/
|
||||
export function buildModuleStream<T>(
|
||||
commandDir: string,
|
||||
): Observable<Result<ImportPayload<T>, SernError>> {
|
||||
const commands = getCommands(commandDir);
|
||||
return from(commands).pipe(mergeMap(defaultModuleLoader<T>));
|
||||
}
|
||||
|
||||
export function fullPathFrom(dir: string) {
|
||||
return join(process.cwd(), dir);
|
||||
}
|
||||
|
||||
export function getCommands(dir: string): string[] {
|
||||
return readPath(fullPathFrom(dir));
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { Processed } from '../../../types/handler';
|
||||
import type { AutocompleteInteraction } from 'discord.js';
|
||||
import { SernError } from '../../../core/structures';
|
||||
import treeSearch from '../../../core/utilities/treeSearch';
|
||||
import type { BothCommand, CommandModule, Module, SlashCommand } from '../../../types/module';
|
||||
import { treeSearch } from '../../../core/functions';
|
||||
import type { CommandModule, Module } from '../../../types/module';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as assert from 'assert';
|
||||
import { concatMap, from, fromEvent, map, OperatorFunction, pipe } from 'rxjs';
|
||||
import { arrayifySource, callPlugin } from '../operators';
|
||||
import { arrayifySource, callPlugin } from '../../../core/operators';
|
||||
import { createResultResolver } from '../observableHandling';
|
||||
|
||||
export function dispatchCommand(module: Processed<CommandModule>, createArgs: () => unknown[]) {
|
||||
@@ -51,7 +51,7 @@ export function eventDispatcher(module: Processed<Module>, source: unknown) {
|
||||
}
|
||||
|
||||
export function dispatchAutocomplete(
|
||||
module: Processed<BothCommand | SlashCommand>,
|
||||
module: Processed<Module>,
|
||||
interaction: AutocompleteInteraction,
|
||||
) {
|
||||
const option = treeSearch(interaction, module.options);
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
import type { ChatInputCommandInteraction, Interaction, Message } from 'discord.js';
|
||||
import { Context } from '../../structures';
|
||||
import type { Message, ChatInputCommandInteraction } from 'discord.js';
|
||||
import { CoreContext } from '../../../core/structures'
|
||||
import type { Args, SlashOptions } from '../../../types/handler';
|
||||
import Context from '../../../classic/context';
|
||||
|
||||
/**
|
||||
* function overloads to create an arguments list for Context
|
||||
* @param wrap
|
||||
* @param messageArgs
|
||||
/*
|
||||
* @overload
|
||||
*/
|
||||
export function contextArgs(
|
||||
wrap: Message,
|
||||
messageArgs?: string[],
|
||||
): () => [Context, ['text', string[]]];
|
||||
export function contextArgs(wrap: Interaction): () => [Context, ['slash', SlashOptions]];
|
||||
export function contextArgs(wrap: Interaction | Message, messageArgs?: string[]) {
|
||||
const ctx = Context.wrap(wrap as ChatInputCommandInteraction | Message);
|
||||
const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.interaction.options];
|
||||
/*
|
||||
* @overload
|
||||
*/
|
||||
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[]) {
|
||||
const ctx = Context.wrap(wrappable);
|
||||
const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options];
|
||||
return () => [ctx, args] as [Context, Args];
|
||||
}
|
||||
|
||||
export function interactionArg<T extends Interaction>(interaction: T) {
|
||||
export function interactionArg<T>(interaction: T) {
|
||||
return () => [interaction] as [T];
|
||||
}
|
||||
|
||||
|
||||
export function multiplatformArgs<
|
||||
M,
|
||||
I,
|
||||
Ctx extends typeof CoreContext<M, I>
|
||||
>(c: Ctx) {
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Interaction } from 'discord.js';
|
||||
import { ChatInputCommandInteraction, Interaction, InteractionType } from 'discord.js';
|
||||
import {
|
||||
catchError,
|
||||
concatMap,
|
||||
@@ -12,25 +12,63 @@ import {
|
||||
OperatorFunction,
|
||||
pipe,
|
||||
} from 'rxjs';
|
||||
import { CommandType, type ModuleStore, SernError } from '../../core/structures';
|
||||
import { CommandType,SernError } from '../../core/structures';
|
||||
import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from './dispatchers';
|
||||
import { executeModule, makeModuleExecutor } from './observableHandling';
|
||||
import type { CommandModule } from '../../types/module';
|
||||
import { ErrorHandling, handleError } from '../../core/contracts/errorHandling';
|
||||
import { SernEmitter } from '../../core';
|
||||
import { SernEmitter, WebsocketStrategy } from '../../core';
|
||||
import type { Processed } from '../../types/handler';
|
||||
import { useContainerRaw } from '../../core/dependencies';
|
||||
import type { Logging, ModuleManager } from '../../core/contracts';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
import { ModuleGetter, createModuleGetter } from '../../core/contracts/moduleManager';
|
||||
|
||||
|
||||
function handleMessageComponents(i: Observable<Interaction>, mg: ModuleGetter) {
|
||||
return i.pipe(
|
||||
filter(e => e.isMessageComponent()),
|
||||
map(event => ({ module: mg('' as any), event }) )
|
||||
)
|
||||
}
|
||||
|
||||
function handleAutocomplete(i: Observable<Interaction>, mg: ModuleGetter) {
|
||||
return i.pipe(
|
||||
filter(e => e.isAutocomplete()),
|
||||
map(event => ({ module: mg('' as any), event }) )
|
||||
)
|
||||
}
|
||||
|
||||
function handleApplicationCommands(i: Observable<Interaction>, mg: ModuleGetter) {
|
||||
return i.pipe(
|
||||
filter(e => e.isCommand()),
|
||||
map(event => ({ module: mg('' as any), event }) )
|
||||
)
|
||||
}
|
||||
|
||||
function handleModal(i: Observable<Interaction>, mg: ModuleGetter) {
|
||||
return i.pipe(
|
||||
filter(e => e.isModalSubmit()),
|
||||
map(event => ({ module: mg('' as any), event }) )
|
||||
)
|
||||
}
|
||||
function makeInteractionProcessor(
|
||||
modules: ModuleManager,
|
||||
): OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }> {
|
||||
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
|
||||
return modules.get(cb);
|
||||
};
|
||||
const get = createModuleGetter(modules);
|
||||
return pipe(
|
||||
concatMap(event => {
|
||||
switch(event.type) {
|
||||
case InteractionType.MessageComponent:
|
||||
case InteractionType.ModalSubmit: {
|
||||
const id = `${event.customId}__M${event.componentType}`
|
||||
} break;
|
||||
case InteractionType.ApplicationCommand:
|
||||
case InteractionType.ApplicationCommandAutocomplete: {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (event.isMessageComponent()) {
|
||||
const customId = event.customId;
|
||||
const module = get(ms => {
|
||||
@@ -58,15 +96,17 @@ function makeInteractionProcessor(
|
||||
) as OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }>;
|
||||
}
|
||||
|
||||
export function makeInteractionCreate([s, client, err, log, modules]: [
|
||||
export function makeInteractionCreate([s, err, log, modules, client]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
]) {
|
||||
EventEmitter
|
||||
],
|
||||
platform: WebsocketStrategy
|
||||
) {
|
||||
//map. If nothing again,this means a slash command
|
||||
const interactionStream$ = fromEvent(client, 'interactionCreate') as Observable<Interaction>;
|
||||
const interactionStream$ = fromEvent(client, platform.eventNames[0]) as Observable<Interaction>;
|
||||
const interactionProcessor = makeInteractionProcessor(modules);
|
||||
return interactionStream$
|
||||
.pipe(
|
||||
@@ -109,7 +149,7 @@ function createDispatcher({
|
||||
*/
|
||||
return dispatchAutocomplete(module, event);
|
||||
} else {
|
||||
return dispatchCommand(module, contextArgs(event));
|
||||
return dispatchCommand(module, contextArgs(event as ChatInputCommandInteraction));
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -1,15 +1,17 @@
|
||||
import { catchError, concatMap, EMPTY, finalize, fromEvent, map, Observable, of, pipe } from 'rxjs';
|
||||
import { type ModuleStore, SernError } from '../structures';
|
||||
import { type ModuleStore, SernError } from '../../core/structures';
|
||||
import type { Message } from 'discord.js';
|
||||
import { executeModule, ignoreNonBot, makeModuleExecutor } from './observableHandling';
|
||||
import type { CommandModule, TextCommand } from '../../types/module';
|
||||
import { ErrorHandling, handleError } from '../contracts/errorHandling';
|
||||
import type { CommandModule } from '../../types/module';
|
||||
import { ErrorHandling, handleError } from '../../core/contracts/errorHandling';
|
||||
import { contextArgs, dispatchCommand } from './dispatchers';
|
||||
import SernEmitter from '../sernEmitter';
|
||||
import SernEmitter from '../../core/sernEmitter';
|
||||
import type { Processed } from '../../types/handler';
|
||||
import { useContainerRaw } from '../dependencies';
|
||||
import type { Logging, ModuleManager } from '../contracts';
|
||||
import { useContainerRaw } from '../../core/dependencies';
|
||||
import type { Logging, ModuleManager } from '../../core/contracts';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
import { WebsocketStrategy } from '../../core';
|
||||
import { createModuleGetter } from '../../core/contracts/moduleManager';
|
||||
|
||||
/**
|
||||
* Removes the first character(s) _[depending on prefix length]_ of the message
|
||||
@@ -52,27 +54,25 @@ const createMessageProcessor = (
|
||||
};
|
||||
return of(payload);
|
||||
}),
|
||||
map(({ args, module }) => dispatchCommand(module as Processed<TextCommand>, args)),
|
||||
map(({ args, module }) => dispatchCommand(module as Processed<CommandModule>, args)),
|
||||
);
|
||||
|
||||
export function makeMessageCreate(
|
||||
[s, client, err, log, modules]: [
|
||||
[s, err, log, modules, client]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
EventEmitter,
|
||||
],
|
||||
defaultPrefix?: string,
|
||||
platform: WebsocketStrategy
|
||||
) {
|
||||
if (!defaultPrefix) {
|
||||
return EMPTY.subscribe();
|
||||
if(!platform.defaultPrefix) {
|
||||
return EMPTY.subscribe()
|
||||
}
|
||||
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
|
||||
return modules.get(cb);
|
||||
};
|
||||
const messageStream$ = fromEvent(client, 'messageCreate') as Observable<Message>;
|
||||
const messageProcessor = createMessageProcessor(defaultPrefix, get);
|
||||
const get = createModuleGetter(modules);
|
||||
const messageStream$ = fromEvent(client, platform.eventNames[1]) as Observable<Message>;
|
||||
const messageProcessor = createMessageProcessor(platform.defaultPrefix, get);
|
||||
return messageStream$
|
||||
.pipe(
|
||||
messageProcessor,
|
||||
@@ -77,9 +77,7 @@ export function createResultResolver<
|
||||
const task$ = config.createStream(args);
|
||||
return task$.pipe(
|
||||
tap(result => {
|
||||
if (result.err) {
|
||||
config.onStop?.(args.module);
|
||||
}
|
||||
result.err && config.onStop?.(args.module);
|
||||
}),
|
||||
everyPluginOk,
|
||||
filterMapTo(() => config.onNext(args)),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fromEvent, map, pipe, switchMap, take } from 'rxjs';
|
||||
import * as Files from '../../core/module-loading/readFile';
|
||||
import { Subscription, fromEvent, map, of, pipe, switchMap, take } from 'rxjs';
|
||||
import * as Files from '../../core/module-loading';
|
||||
import { callInitPlugins } from './observableHandling';
|
||||
import { CommandType, type ModuleStore, SernError } from '../../core/structures';
|
||||
import { Result } from 'ts-results-es';
|
||||
@@ -7,10 +7,11 @@ import { ApplicationCommandType, ComponentType } from 'discord.js';
|
||||
import type { CommandModule } from '../../types/module';
|
||||
import type { Processed } from '../../types/handler';
|
||||
import type { ErrorHandling, Logging, ModuleManager } from '../../core/contracts';
|
||||
import { err, ok } from '../../core/utilities/functions';
|
||||
import { err, ok } from '../../core/functions';
|
||||
import { errTap, fillDefaults } from '../../core/operators';
|
||||
import SernEmitter from '../../core/sernEmitter';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
import { DispatchType, PlatformStrategy, ServerlessStrategy, WebsocketStrategy } from '../../core';
|
||||
|
||||
function buildCommandModules(commandDir: string, sernEmitter: SernEmitter) {
|
||||
return pipe(
|
||||
@@ -21,18 +22,49 @@ function buildCommandModules(commandDir: string, sernEmitter: SernEmitter) {
|
||||
map(fillDefaults),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @overload
|
||||
*/
|
||||
export function makeReadyEvent(
|
||||
[sEmitter, client, errorHandler, , moduleManager]: [
|
||||
dependencies: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
],
|
||||
commandDir: string,
|
||||
platform: ServerlessStrategy
|
||||
|
||||
): Subscription
|
||||
export function makeReadyEvent(
|
||||
dependencies: [
|
||||
SernEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
EventEmitter
|
||||
],
|
||||
commandDir: string,
|
||||
platform: WebsocketStrategy
|
||||
|
||||
): Subscription
|
||||
|
||||
export function makeReadyEvent(
|
||||
[sEmitter, errorHandler, , moduleManager, client]: [
|
||||
SernEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
EventEmitter?
|
||||
],
|
||||
commandDir: string,
|
||||
platform: PlatformStrategy
|
||||
) {
|
||||
const readyOnce$ = fromEvent(client, 'ready').pipe(take(1));
|
||||
return readyOnce$
|
||||
const ready$ = platform.type === DispatchType.Serverless
|
||||
? of(null)
|
||||
: fromEvent(client!, platform.eventNames[2]).pipe(take(1));
|
||||
return ready$
|
||||
.pipe(
|
||||
buildCommandModules(commandDir, sEmitter),
|
||||
callInitPlugins({
|
||||
@@ -1,21 +1,22 @@
|
||||
import { catchError, finalize, map, mergeAll } from 'rxjs';
|
||||
import * as Files from '../../core/module-loading/readFile';
|
||||
import * as Files from '../../core/module-loading';
|
||||
import type { Processed, WebsocketDependencies } from '../../types/handler';
|
||||
import { callInitPlugins } from './observableHandling';
|
||||
import type { CommandModule, EventModule } from '../../types/module';
|
||||
import type { EventEmitter } from 'events';
|
||||
import SernEmitter from '../../core/sernEmitter';
|
||||
import type { ErrorHandling, Logging } from '../../core/contracts';
|
||||
import { SernError, EventType, type Wrapper } from '../../core/structures';
|
||||
import { SernError, EventType } from '../../core/structures';
|
||||
import { eventDispatcher } from './dispatchers';
|
||||
import { handleError } from '../../core/contracts/errorHandling';
|
||||
import { errTap, fillDefaults } from '../../core/operators';
|
||||
import { useContainerRaw } from '../../core/dependencies';
|
||||
import { AnyWrapper } from '../../core/structures/wrapper';
|
||||
|
||||
export function makeEventsHandler(
|
||||
[s, client, err, log]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined],
|
||||
[s, err, log, client]: [SernEmitter, ErrorHandling, Logging | undefined, EventEmitter],
|
||||
eventsPath: string,
|
||||
containerGetter: Wrapper['containerConfig'],
|
||||
containerGetter: AnyWrapper['containerConfig'],
|
||||
) {
|
||||
const lazy = (k: string) => containerGetter.get(k as keyof WebsocketDependencies)[0];
|
||||
const eventStream$ = eventObservable(eventsPath, s);
|
||||
Reference in New Issue
Block a user