chore: rename, wip refactoring

This commit is contained in:
Jacob Nguyen
2023-05-03 21:08:20 -05:00
parent d53b315157
commit 2ecf3e5a5c
8 changed files with 143 additions and 121 deletions

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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) {
}

View File

@@ -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:

View File

@@ -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,

View File

@@ -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)),

View File

@@ -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({

View File

@@ -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);