mirror of
https://github.com/sern-handler/handler
synced 2026-06-06 01:16:55 +00:00
chore: progress of globalizing dependencies type
This commit is contained in:
@@ -23,7 +23,8 @@ export type {
|
||||
CommandModuleDefs,
|
||||
EventModuleDefs,
|
||||
BaseOptions,
|
||||
SernAutocompleteData
|
||||
SernAutocompleteData,
|
||||
SernOptionsData
|
||||
} from './types/modules';
|
||||
export type {
|
||||
Controller,
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { SernError } from './structures/errors';
|
||||
import { type Result, Err, Ok } from 'ts-results-es';
|
||||
import { Module } from './types/modules';
|
||||
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';
|
||||
import { Processed } from '../handler/types';
|
||||
|
||||
export type ModuleResult<T> = Promise<Result<Processed<T>, SernError>>;
|
||||
import { ImportPayload } from '../handler/types';
|
||||
import * as assert from 'node:assert'
|
||||
import { sernMeta } from '../handler/commands';
|
||||
export type ModuleResult<T> = Promise<Result<ImportPayload<T>, SernError>>;
|
||||
|
||||
export async function importModule<T>(absPath: string) {
|
||||
/// #if MODE === 'esm'
|
||||
@@ -18,19 +17,17 @@ export async function importModule<T>(absPath: string) {
|
||||
/// #endif
|
||||
}
|
||||
export async function defaultModuleLoader<T extends Module>(absPath: string): ModuleResult<T> {
|
||||
// prettier-ignore
|
||||
const module = await importModule<T>(absPath);
|
||||
if (module === undefined) {
|
||||
return Err(SernError.UndefinedModule);
|
||||
}
|
||||
checkIsProcessed(module);
|
||||
return Ok(module);
|
||||
|
||||
assert.ok(module.type > 0 && module.type < 1<<10, "Found a module that does not have a valid type");
|
||||
assert.ok(module[sernMeta], "Found a module that isn't marked with sernMeta");
|
||||
|
||||
return Ok({ module, absPath });
|
||||
}
|
||||
|
||||
function checkIsProcessed<T extends Module>(m: T): asserts m is Processed<T> {
|
||||
assert.ok(m.name !== undefined, `name is not defined for ${util.format(m)}`);
|
||||
assert.ok(m.description !== undefined, `description is not defined for ${util.format(m)}`);
|
||||
}
|
||||
|
||||
export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
|
||||
|
||||
@@ -42,7 +39,7 @@ export const fmtFileName = (n: string) => n.substring(0, n.length - 3);
|
||||
*/
|
||||
export function buildModuleStream<T extends Module>(
|
||||
input: ObservableInput<string>,
|
||||
): Observable<Result<Processed<T>, SernError>> {
|
||||
): Observable<Result<ImportPayload<T>, SernError>> {
|
||||
return from(input).pipe(mergeMap(defaultModuleLoader<T>));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,12 @@
|
||||
import { ClientEvents } from 'discord.js';
|
||||
import { CommandType, EventType, PluginType } from '../core/structures';
|
||||
import { EventType, PluginType } from '../core/structures';
|
||||
import { AnyEventPlugin, Plugin } from '../core/types/plugins';
|
||||
import { CommandModule, EventModule, InputCommand, InputEvent } from '../core/types/modules';
|
||||
import { partition } from '../core/functions';
|
||||
import { filename, filePath } from '../core/module-loading';
|
||||
import { Awaitable } from '../shared';
|
||||
export const sernMeta = Symbol('@sern/meta');
|
||||
const appBitField = 0b000000011111;
|
||||
/*
|
||||
* Generates a number based on CommandType.
|
||||
* This corresponds to an ApplicationCommandType or ComponentType
|
||||
* TextCommands are 0 as they aren't either or.
|
||||
*/
|
||||
function apiType(t: CommandType) {
|
||||
if (t === CommandType.Both || t === CommandType.Modal) return 1;
|
||||
const log = Math.log2(t);
|
||||
return (appBitField & t) !== 0 ? log : log - 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates an id based on CommandType.
|
||||
* A is for any ApplicationCommand. C is for any ComponentCommand
|
||||
* Then, another number generated by apiType function is appended
|
||||
*/
|
||||
function uniqueId(t: CommandType) {
|
||||
const am = (appBitField & t) !== 0 ? 'A' : 'C';
|
||||
return am + apiType(t);
|
||||
}
|
||||
export const UNREGISTERED = "meow meow meow";
|
||||
export const EMPTY_PATH = "purr purr purr";
|
||||
/**
|
||||
* @since 1.0.0 The wrapper function to define command modules for sern
|
||||
* @param mod
|
||||
@@ -36,18 +16,14 @@ export function commandModule(mod: InputCommand): CommandModule {
|
||||
mod.plugins ?? [],
|
||||
el => (el as Plugin).type === PluginType.Control,
|
||||
);
|
||||
const fullPath = filePath();
|
||||
const name = mod.name ?? filename(fullPath);
|
||||
return {
|
||||
...mod,
|
||||
description: mod.description ?? '...',
|
||||
name,
|
||||
onEvent,
|
||||
plugins,
|
||||
[sernMeta]: {
|
||||
id: `${name}__${uniqueId(mod.type)}`,
|
||||
fullPath,
|
||||
},
|
||||
id: UNREGISTERED,
|
||||
fullPath: EMPTY_PATH
|
||||
}
|
||||
} as CommandModule;
|
||||
}
|
||||
/**
|
||||
@@ -60,14 +36,12 @@ export function eventModule(mod: InputEvent): EventModule {
|
||||
mod.plugins ?? [],
|
||||
el => (el as Plugin).type === PluginType.Control,
|
||||
);
|
||||
const fullPath = filePath();
|
||||
return {
|
||||
name: mod.name ?? filename(fullPath),
|
||||
onEvent,
|
||||
plugins,
|
||||
[sernMeta]: {
|
||||
id: 'no-id',
|
||||
fullPath,
|
||||
id: UNREGISTERED,
|
||||
fullPath: EMPTY_PATH
|
||||
},
|
||||
...mod,
|
||||
} as EventModule;
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
InteractionType,
|
||||
Message,
|
||||
} from 'discord.js';
|
||||
import { EMPTY, Observable, concatMap, filter, from, map, of, throwError, tap } from 'rxjs';
|
||||
import { ModuleManager } from '../../core';
|
||||
import { EMPTY, Observable, concatMap, filter, from, of, throwError, tap, MonoTypeOperatorFunction } from 'rxjs';
|
||||
import { CommandType, EventType, ModuleManager } from '../../core';
|
||||
import { SernError } from '../../core/structures/errors';
|
||||
import { callPlugin, everyPluginOk, filterMap, filterMapTo } from '../../core/operators';
|
||||
import { defaultModuleLoader } from '../../core/module-loading';
|
||||
@@ -20,6 +20,7 @@ import { fmt } from './messages';
|
||||
import { ControlPlugin, VoidResult } from '../../core/types/plugins';
|
||||
import { ImportPayload, Processed } from '../types';
|
||||
import { Awaitable } from '../../shared';
|
||||
import { createId, uniqueId } from '../id';
|
||||
|
||||
function createGenericHandler<Source, Narrowed extends Source, Output>(
|
||||
source: Observable<Source>,
|
||||
@@ -44,8 +45,8 @@ export function createInteractionHandler<T extends Interaction>(
|
||||
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<CommandModule>(fullPath).then(res =>
|
||||
res.map(module => createDispatcher({ module, event })),
|
||||
return defaultModuleLoader<Processed<CommandModule>>(fullPath).then(res =>
|
||||
res.map(payload => createDispatcher({ module: payload.module, event })),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -58,39 +59,32 @@ export function createMessageHandler(
|
||||
) {
|
||||
return createGenericHandler(source, event => {
|
||||
const [prefix, ...rest] = fmt(event.content, defaultPrefix);
|
||||
const fullPath = mg.get(`${prefix}__A0`);
|
||||
const fullPath = mg.get(`${prefix}_A0`);
|
||||
if (fullPath === undefined) {
|
||||
return Err(SernError.UndefinedModule + ' No full path found in module store');
|
||||
}
|
||||
return defaultModuleLoader<CommandModule>(fullPath)
|
||||
return defaultModuleLoader<Processed<CommandModule>>(fullPath)
|
||||
.then(result => {
|
||||
const args = contextArgs(event, rest);
|
||||
return result.map(module => dispatchMessage(module, args))
|
||||
return result.map(payload => dispatchMessage(payload.module, args))
|
||||
})
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Creates a unique ID for a given interaction object.
|
||||
* @param event The interaction object for which to create an ID.
|
||||
* @returns A unique string ID based on the type and properties of the interaction object.
|
||||
*/
|
||||
function createId<T extends Interaction>(event: T) {
|
||||
switch (event.type) {
|
||||
case InteractionType.MessageComponent: {
|
||||
return `${event.customId}__C${event.componentType}`;
|
||||
}
|
||||
case InteractionType.ApplicationCommand:
|
||||
case InteractionType.ApplicationCommandAutocomplete: {
|
||||
return `${event.commandName}__A${event.commandType}`;
|
||||
}
|
||||
case InteractionType.ModalSubmit: {
|
||||
return `${event.customId}__C1`;
|
||||
}
|
||||
* IMPURE SIDE EFFECT
|
||||
* This function assigns remaining, incomplete data to each imported module.
|
||||
*/
|
||||
function assignDefaults<T extends Module>(): MonoTypeOperatorFunction<ImportPayload<T>> {
|
||||
return tap(
|
||||
({ module, absPath }) => {
|
||||
module.name ??= Files.filename(absPath);
|
||||
module.description ??= "...";
|
||||
module[sernMeta].fullPath = absPath;
|
||||
module[sernMeta].id = `${module.name}_${uniqueId(module.type)}`
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function buildModules<T extends AnyModule>(
|
||||
input: ObservableInput<string>,
|
||||
sernEmitter: SernEmitter,
|
||||
@@ -100,7 +94,7 @@ export function buildModules<T extends AnyModule>(
|
||||
errTap(error => {
|
||||
sernEmitter.emit('module.register', SernEmitter.failure(undefined, error));
|
||||
}),
|
||||
map(module => ({ module, absPath: module[sernMeta].fullPath })),
|
||||
assignDefaults<T>()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -184,7 +178,7 @@ export function createResultResolver<
|
||||
* ignore the module
|
||||
*/
|
||||
export function callInitPlugins<
|
||||
T extends Processed<Module>,
|
||||
T extends Processed<AnyModule>,
|
||||
Args extends ImportPayload<T>,
|
||||
>(config: { onStop?: (module: T) => unknown; onNext: (module: Args) => T }) {
|
||||
return concatMap(
|
||||
|
||||
@@ -26,3 +26,4 @@ export function makeInteractionHandler([emitter,,, modules, client]: DependencyL
|
||||
concatMap(payload => executeModule(emitter, payload)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export function startReadyEvent(
|
||||
const ready$ = fromEvent(client!, 'ready').pipe(take(1));
|
||||
return ready$
|
||||
.pipe(
|
||||
buildModules(allPaths, sEmitter),
|
||||
buildModules<Processed<AnyModule>>(allPaths, sEmitter),
|
||||
callInitPlugins({
|
||||
onStop: module => {
|
||||
sEmitter.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
@@ -43,7 +43,7 @@ function registerModule<T extends Processed<AnyModule>>(
|
||||
if (module.type === CommandType.Both
|
||||
|| module.type === CommandType.Text
|
||||
) {
|
||||
module.alias?.forEach(a => manager.set(`${a}__A0`, fullPath));
|
||||
module.alias?.forEach(a => manager.set(`${a}_A0`, fullPath));
|
||||
}
|
||||
return Result.wrap(() => manager.set(id, fullPath));
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function makeEventsHandler(
|
||||
};
|
||||
of(null)
|
||||
.pipe(
|
||||
buildModules(allPaths, emitter),
|
||||
buildModules<Processed<EventModule>>(allPaths, emitter),
|
||||
callInitPlugins({
|
||||
onStop: module =>
|
||||
emitter.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure)),
|
||||
|
||||
@@ -20,4 +20,5 @@ export interface InitArgs<T extends Processed<Module>> {
|
||||
export interface ImportPayload<T> {
|
||||
module: T;
|
||||
absPath: string;
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ export * as Sern from './handler/sern';
|
||||
export * from './core';
|
||||
export { commandModule, eventModule, discordEvent } from './handler/commands'
|
||||
export { controller } from './handler/sern';
|
||||
export type { Wrapper, Args } from './shared'
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import type {
|
||||
MessageReplyOptions,
|
||||
} from 'discord.js';
|
||||
import { PayloadType } from './core';
|
||||
import { Dependencies } from './core/ioc/types';
|
||||
import { AnyModule } from './core/types/modules';
|
||||
|
||||
export type ReplyOptions =
|
||||
@@ -27,7 +26,6 @@ export interface SernEventsMapping {
|
||||
|
||||
export type Awaitable<T> = PromiseLike<T> | T;
|
||||
|
||||
export type ModuleStore = Map<string, string>;
|
||||
|
||||
export type Deprecated<Message extends string> = [never, Message];
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ export default defineConfig([
|
||||
},
|
||||
{
|
||||
dts: {
|
||||
only: true
|
||||
only: true,
|
||||
entry: 'src/index.ts'
|
||||
},
|
||||
entry: ['src/index.ts'],
|
||||
outDir: 'dist'
|
||||
}
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user