mirror of
https://github.com/sern-handler/handler
synced 2026-06-17 05:12:16 +00:00
ready handler revamped so much cleaner
This commit is contained in:
11
package.json
11
package.json
@@ -36,12 +36,14 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.1.0",
|
||||
"node-cron": "^3.0.3",
|
||||
"rxjs": "^7.8.0",
|
||||
"ts-results-es": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.1",
|
||||
"@types/node": "~18.17.11",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "5.58.0",
|
||||
"@typescript-eslint/parser": "5.59.1",
|
||||
"discord.js": "^14.11.0",
|
||||
@@ -76,7 +78,10 @@
|
||||
"allowTemplateLiterals": true
|
||||
}
|
||||
],
|
||||
"semi": [ "error", "always" ],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/no-empty-interface": 0,
|
||||
"@typescript-eslint/ban-types": 0,
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
@@ -87,7 +92,7 @@
|
||||
"url": "git+https://github.com/sern-handler/handler.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.17.x"
|
||||
"node": ">= 20.0.x"
|
||||
},
|
||||
"homepage": "https://sern.dev"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import type { Result } from 'ts-results-es'
|
||||
import { CommandType, EventType, Plugin } from '..';
|
||||
import { AnyFunction } from '../types/utility';
|
||||
import { Module } from '../types/core-modules';
|
||||
|
||||
export * from './functions';
|
||||
|
||||
export type _Module = {
|
||||
meta: {
|
||||
id: string,
|
||||
absPath: string
|
||||
}
|
||||
name: string,
|
||||
execute : Function
|
||||
[key: PropertyKey]: unknown
|
||||
}
|
||||
|
||||
export type VoidResult = Result<void, void>;
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface Emitter {
|
||||
addListener(eventName: string | symbol, listener: AnyFunction): this;
|
||||
removeListener(eventName: string | symbol, listener: AnyFunction): this;
|
||||
emit(eventName: string | symbol, ...payload: any[]): boolean;
|
||||
on(eventName: string | symbol, listener: AnyFunction): this
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@ async function composeRoot(
|
||||
__add_container('@sern/logger', new __Services.DefaultLogging());
|
||||
}
|
||||
__add_container('@sern/errors', new __Services.DefaultErrorHandling());
|
||||
__add_container('@sern/cron', {})
|
||||
__add_container('@sern/modules', new Map())
|
||||
//Build the container based on the callback provided by the user
|
||||
conf.build(container as Container);
|
||||
|
||||
@@ -99,6 +101,7 @@ export async function makeDependencies (conf: ValidDependencyConfig) {
|
||||
__add_container('@sern/logger', new __Services.DefaultLogging);
|
||||
}
|
||||
__add_container('@sern/errors', new __Services.DefaultErrorHandling());
|
||||
__add_container('@sern/cron', {})
|
||||
await useContainerRaw().ready();
|
||||
} else {
|
||||
await composeRoot(useContainerRaw(), conf);
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as __Services from '../structures/default-services';
|
||||
* A semi-generic container that provides error handling, emitter, and module store.
|
||||
* For the handler to operate correctly, The only user provided dependency needs to be @sern/client
|
||||
*/
|
||||
export function hasCallableMethod(obj: object, name: PropertyKey) {
|
||||
function hasCallableMethod(obj: object, name: PropertyKey) {
|
||||
//@ts-ignore
|
||||
return Object.hasOwn(obj, name) && typeof obj[name] == 'function';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { existsSync } from 'fs';
|
||||
import { readdir } from 'fs/promises';
|
||||
import assert from 'node:assert';
|
||||
import * as Id from './id'
|
||||
import type { _Module } from './_internal';
|
||||
import { Module } from '../types/core-modules';
|
||||
|
||||
export const parseCallsite = (site: string) => {
|
||||
const pathobj = path.parse(site.replace(/file:\\?/, "")
|
||||
@@ -38,11 +38,11 @@ export const shouldHandle = (pth: string, filenam: string) => {
|
||||
export async function importModule<T>(absPath: string) {
|
||||
let fileModule = await import(absPath);
|
||||
|
||||
let commandModule: _Module = fileModule.default;
|
||||
let commandModule: Module = fileModule.default;
|
||||
|
||||
assert(commandModule , `No export @ ${absPath}. Forgot to ignore with "!"? (!${path.basename(absPath)})?`);
|
||||
if ('default' in commandModule) {
|
||||
commandModule = commandModule.default as _Module;
|
||||
commandModule = commandModule.default as Module;
|
||||
}
|
||||
const p = path.parse(absPath)
|
||||
commandModule.name ??= p.name; commandModule.description ??= "...";
|
||||
@@ -51,21 +51,18 @@ export async function importModule<T>(absPath: string) {
|
||||
id: Id.create(commandModule.name, commandModule.type),
|
||||
absPath,
|
||||
};
|
||||
return { module: commandModule } as T;
|
||||
return { module: commandModule as T };
|
||||
}
|
||||
|
||||
|
||||
export async function* readRecursive(dir: string): AsyncGenerator<string> {
|
||||
const files = await readdir(dir, { withFileTypes: true, recursive: true });
|
||||
const files = await readdir(dir, { recursive: true, withFileTypes: true });
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(file.path, file.name);
|
||||
const fullPath = path.join(file.parentPath, file.name);
|
||||
if(!file.name.startsWith('!') && !file.isDirectory()) {
|
||||
yield fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const fmtFileName = (fileName: string) => path.parse(fileName).name;
|
||||
|
||||
export const filename = (p: string) => fmtFileName(path.basename(p));
|
||||
|
||||
|
||||
@@ -4,37 +4,35 @@ import type { AnyEventPlugin, } from '../types/core-plugin';
|
||||
import type {
|
||||
InputCommand,
|
||||
InputEvent,
|
||||
Module,
|
||||
} from '../types/core-modules';
|
||||
import { type _Module, partitionPlugins } from './_internal';
|
||||
import { partitionPlugins } from './functions'
|
||||
import type { Awaitable } from '../types/utility';
|
||||
|
||||
/**
|
||||
* @since 1.0.0 The wrapper function to define command modules for sern
|
||||
* @param mod
|
||||
*/
|
||||
export function commandModule(mod: InputCommand): _Module {
|
||||
export function commandModule(mod: InputCommand): Module {
|
||||
const [onEvent, plugins] = partitionPlugins(mod.plugins);
|
||||
//@ts-ignore
|
||||
return {
|
||||
...mod,
|
||||
onEvent,
|
||||
plugins,
|
||||
};
|
||||
} as Module;
|
||||
}
|
||||
/**
|
||||
* @since 1.0.0
|
||||
* The wrapper function to define event modules for sern
|
||||
* @param mod
|
||||
*/
|
||||
export function eventModule(mod: InputEvent): _Module {
|
||||
export function eventModule(mod: InputEvent): Module {
|
||||
const [onEvent, plugins] = partitionPlugins(mod.plugins);
|
||||
|
||||
//@ts-ignore
|
||||
return {
|
||||
...mod,
|
||||
plugins,
|
||||
onEvent,
|
||||
};
|
||||
} as Module;
|
||||
}
|
||||
|
||||
/** Create event modules from discord.js client events,
|
||||
|
||||
@@ -15,10 +15,6 @@ import {
|
||||
} from 'rxjs';
|
||||
import {
|
||||
type VoidResult,
|
||||
resultPayload,
|
||||
isAutocomplete,
|
||||
treeSearch,
|
||||
_Module,
|
||||
} from '../core/_internal';
|
||||
import * as Id from '../core/id'
|
||||
import type { Emitter, ErrorHandling, Logging } from '../core/interfaces';
|
||||
@@ -42,7 +38,7 @@ function contextArgs(wrappable: Message | BaseInteraction, messageArgs?: string[
|
||||
const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options];
|
||||
return [ctx, args] as [Context, Args];
|
||||
}
|
||||
|
||||
import { resultPayload, isAutocomplete, treeSearch } from '../core/functions'
|
||||
|
||||
function intoPayload(module: Processed<Module>, ) {
|
||||
return pipe(map(arrayifySource),
|
||||
@@ -127,7 +123,7 @@ export function fmt(msg: string, prefix: string): string[] {
|
||||
*/
|
||||
export function createInteractionHandler<T extends Interaction>(
|
||||
source: Observable<Interaction>,
|
||||
mg: Map<string, _Module>, //TODO
|
||||
mg: Map<string, Module>, //TODO
|
||||
) {
|
||||
return createGenericHandler<Interaction, T, Result<ReturnType<typeof createDispatcher>, void>>(
|
||||
source,
|
||||
@@ -135,7 +131,7 @@ export function createInteractionHandler<T extends Interaction>(
|
||||
const possibleIds = Id.reconstruct(event);
|
||||
let fullPaths= possibleIds
|
||||
.map(id => mg.get(id))
|
||||
.filter((id): id is _Module => id !== undefined);
|
||||
.filter((id): id is Module => id !== undefined);
|
||||
|
||||
if(fullPaths.length == 0) {
|
||||
return Err.EMPTY;
|
||||
@@ -230,25 +226,6 @@ export function createResultResolver<
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls a module's init plugins and checks for Err. If so, call { onStop } and
|
||||
* ignore the module
|
||||
*/
|
||||
export function callInitPlugins<T extends Processed<Module>>(sernEmitter: Emitter) {
|
||||
return concatMap(
|
||||
createResultResolver({
|
||||
createStream: args => from(args.module.plugins).pipe(callPlugin(args)),
|
||||
onStop: (module: T) => {
|
||||
sernEmitter.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
},
|
||||
onNext: (payload) => {
|
||||
sernEmitter.emit('module.register', resultPayload(PayloadType.Success, payload.module));
|
||||
return payload as { module: T; metadata: CommandMeta };
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an executable task ( execute the command ) if all control plugins are successful
|
||||
* @param onStop emits a failure response to the SernEmitter
|
||||
@@ -264,19 +241,16 @@ export function makeModuleExecutor<
|
||||
});
|
||||
return createResultResolver({
|
||||
onStop,
|
||||
createStream: ({ args, module }) =>
|
||||
from(module.onEvent)
|
||||
.pipe(callPlugin(args)),
|
||||
createStream: ({ args, module }) => from(module.onEvent).pipe(callPlugin(args)),
|
||||
onNext,
|
||||
})
|
||||
}
|
||||
|
||||
export const handleCrash = (err: ErrorHandling,sernemitter: Emitter, log?: Logging) =>
|
||||
pipe(
|
||||
catchError(handleError(err, sernemitter, log)),
|
||||
pipe(catchError(handleError(err, sernemitter, log)),
|
||||
finalize(() => {
|
||||
log?.info({
|
||||
message: 'A stream closed or reached end of lifetime',
|
||||
});
|
||||
disposeAll(log);
|
||||
}));
|
||||
}))
|
||||
|
||||
@@ -2,16 +2,11 @@ import type { Interaction } from 'discord.js';
|
||||
import { mergeMap, merge, concatMap } from 'rxjs';
|
||||
import { PayloadType } from '../core/structures/enums';
|
||||
import { filterTap, sharedEventStream } from '../core/operators'
|
||||
import {
|
||||
isAutocomplete,
|
||||
isCommand,
|
||||
isMessageComponent,
|
||||
isModal,
|
||||
resultPayload,
|
||||
} from '../core/_internal';
|
||||
import { createInteractionHandler, executeModule, makeModuleExecutor } from './event-utils';
|
||||
import type { DependencyList } from '../types/ioc';
|
||||
import { SernError } from '../core/structures/enums'
|
||||
import { isAutocomplete, isCommand, isMessageComponent, isModal, resultPayload, } from '../core/functions'
|
||||
|
||||
export function interactionHandler([emitter, err, log, client]: DependencyList) {
|
||||
const interactionStream$ = sharedEventStream<Interaction>(client, 'interactionCreate');
|
||||
const modules = new Map();
|
||||
@@ -25,5 +20,5 @@ export function interactionHandler([emitter, err, log, client]: DependencyList)
|
||||
.pipe(filterTap(e => emitter.emit('warning', resultPayload(PayloadType.Warning, undefined, e))),
|
||||
concatMap(makeModuleExecutor(module =>
|
||||
emitter.emit('module.activate', resultPayload(PayloadType.Failure, module, SernError.PluginFailure)))),
|
||||
mergeMap(payload => executeModule(emitter, log, err, payload)));
|
||||
mergeMap(payload => executeModule(emitter, log, err, payload)));
|
||||
}
|
||||
|
||||
@@ -20,16 +20,15 @@ function hasPrefix(prefix: string, content: string) {
|
||||
}
|
||||
|
||||
export function messageHandler(
|
||||
[emitter, err, log, client]: DependencyList,
|
||||
[emitter, err, log, client, commands]: DependencyList,
|
||||
defaultPrefix: string | undefined,
|
||||
) {
|
||||
if (!defaultPrefix) {
|
||||
log?.debug({ message: 'No prefix found. message handler shutting down' });
|
||||
return EMPTY;
|
||||
}
|
||||
const modules = new Map()
|
||||
const messageStream$ = sharedEventStream<Message>(client, 'messageCreate');
|
||||
const handle = createMessageHandler(messageStream$, defaultPrefix, modules);
|
||||
const handle = createMessageHandler(messageStream$, defaultPrefix, commands);
|
||||
|
||||
const msgCommands$ = handle(isNonBot(defaultPrefix));
|
||||
|
||||
|
||||
@@ -23,11 +23,8 @@ const parseConfig = async (conf: Promise<PresenceResult>) => {
|
||||
};
|
||||
|
||||
export const presenceHandler = (path: string, setPresence: SetPresence) => {
|
||||
interface PresenceModule {
|
||||
module: PresenceConfig<(keyof Dependencies)[]>
|
||||
}
|
||||
const presence = Files
|
||||
.importModule<PresenceModule>(path)
|
||||
.importModule<PresenceConfig<(keyof Dependencies)[]>>(path)
|
||||
.then(({ module }) => {
|
||||
//fetch services with the order preserved, passing it to the execute fn
|
||||
const fetchedServices = Services(...module.inject ?? []);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { concat, first, fromEvent, ignoreElements, pipe, tap } from 'rxjs';
|
||||
import { _Module } from '../core/_internal';
|
||||
import { Logging } from '../core/interfaces';
|
||||
import type { DependencyList } from '../types/ioc';
|
||||
import { callInitPlugins } from './event-utils';
|
||||
|
||||
const once = (log: Logging | undefined) =>
|
||||
pipe(tap(() => { log?.info({ message: "Waiting on discord client to be ready..." }) }),
|
||||
first(),
|
||||
ignoreElements())
|
||||
|
||||
export function readyHandler(
|
||||
[sEmitter, , log, client]: DependencyList,
|
||||
) {
|
||||
//Todo: add module manager on on ready
|
||||
const ready$ = fromEvent(client!, 'ready').pipe(once(log));
|
||||
|
||||
return concat(ready$).pipe(callInitPlugins(sEmitter)).subscribe();
|
||||
|
||||
// const validModuleType = module.type >= 0 && module.type <= 1 << 10;
|
||||
// assert.ok(validModuleType,
|
||||
// `Found ${module.name} at ${module.meta.fullPath}, which does not have a valid type`);
|
||||
}
|
||||
|
||||
|
||||
30
src/handlers/ready.ts
Normal file
30
src/handlers/ready.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { DependencyList } from '../types/ioc';
|
||||
import * as Files from '../core/module-loading'
|
||||
import { once } from 'events';
|
||||
import { resultPayload } from '../core/functions';
|
||||
import { PayloadType } from '..';
|
||||
import { SernError } from '../core/structures/enums';
|
||||
import { Module } from '../types/core-modules';
|
||||
|
||||
export default async function(dir: string, [sEmitter,, log, client, commands]: DependencyList) {
|
||||
log?.info({ message: "Waiting on discord client to be ready..." })
|
||||
await once(client, "ready");
|
||||
log?.info({ message: "Client signaled ready, registering modules" });
|
||||
for await (const path of Files.readRecursive(dir)) {
|
||||
const { module } = await Files.importModule<Module>(path);
|
||||
const validModuleType = module.type >= 0 && module.type <= 1 << 10;
|
||||
if(!validModuleType) {
|
||||
throw Error(`Found ${module.name} at ${module.meta.absPath}, which has an incorrect \`type\``);
|
||||
}
|
||||
for(const plugin of module.plugins) {
|
||||
const res = await plugin.execute({ module, absPath: module.meta.absPath });
|
||||
if(res.isErr()) {
|
||||
sEmitter.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
throw Error("Plugin failed with controller.stop()");
|
||||
}
|
||||
}
|
||||
commands.set(module.meta.id, module);
|
||||
sEmitter.emit('module.register', resultPayload(PayloadType.Success, module));
|
||||
}
|
||||
sEmitter.emit('modulesLoaded');
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import { ObservableInput, map, mergeAll } from 'rxjs';
|
||||
import { EventType, SernError } from '../core/structures/enums';
|
||||
import { callInitPlugins, eventDispatcher, handleCrash } from './event-utils'
|
||||
import { eventDispatcher } from './event-utils'
|
||||
import { Service } from '../core/ioc';
|
||||
import type { DependencyList } from '../types/ioc';
|
||||
import type { EventModule, Processed } from '../types/core-modules';
|
||||
|
||||
export function eventsHandler(
|
||||
[emitter, err, log, client]: DependencyList,
|
||||
//allPaths: ObservableInput<string>,
|
||||
) {
|
||||
export default function( [emitter, err, log, client]: DependencyList, eventDir: string) {
|
||||
//code smell
|
||||
const intoDispatcher = (e: { module: Processed<EventModule> }) => {
|
||||
switch (e.module.type) {
|
||||
@@ -18,6 +14,9 @@ export function eventsHandler(
|
||||
return eventDispatcher(e.module, client);
|
||||
case EventType.External:
|
||||
return eventDispatcher(e.module, Service(e.module.emitter));
|
||||
case EventType.Cron:
|
||||
//@ts-ignore
|
||||
return eventDispatcher(e.module, Service('@sern/cron'))
|
||||
default:
|
||||
throw Error(SernError.InvalidModuleType + ' while creating event handler');
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export type {
|
||||
|
||||
|
||||
export type { Args, SlashOptions, Payload, SernEventsMapping } from './types/utility';
|
||||
export type { Singleton, Transient, CoreDependencies } from './types/ioc';
|
||||
export type { CoreDependencies } from './types/ioc';
|
||||
|
||||
export {
|
||||
commandModule,
|
||||
|
||||
31
src/sern.ts
31
src/sern.ts
@@ -2,8 +2,8 @@ import callsites from 'callsites';
|
||||
import * as Files from './core/module-loading';
|
||||
import { merge } from 'rxjs';
|
||||
import { Services } from './core/ioc';
|
||||
import { eventsHandler } from './handlers/user-defined-events';
|
||||
import { readyHandler } from './handlers/ready-event';
|
||||
import eventsHandler from './handlers/user-defined-events';
|
||||
import ready from './handlers/ready';
|
||||
import { messageHandler } from './handlers/message';
|
||||
import { interactionHandler } from './handlers/interaction';
|
||||
import { presenceHandler } from './handlers/presence';
|
||||
@@ -11,9 +11,9 @@ import { Client } from 'discord.js';
|
||||
import { handleCrash } from './handlers/event-utils';
|
||||
|
||||
interface Wrapper {
|
||||
commands?: string;
|
||||
commands: string;
|
||||
defaultPrefix?: string;
|
||||
events?: string;
|
||||
events: string;
|
||||
}
|
||||
/**
|
||||
* @since 1.0.0
|
||||
@@ -27,37 +27,38 @@ interface Wrapper {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function init(wrapper: Wrapper = { commands: "./dist/commands", events: "./dist/events" }) {
|
||||
export function init(maybeWrapper: Wrapper = { commands: "./dist/commands", events: "./dist/events" }) {
|
||||
const startTime = performance.now();
|
||||
const dependencies = Services('@sern/emitter',
|
||||
'@sern/errors',
|
||||
'@sern/logger',
|
||||
'@sern/client');
|
||||
'@sern/client',
|
||||
'@sern/modules');
|
||||
const logger = dependencies[2],
|
||||
errorHandler = dependencies[1];
|
||||
|
||||
if (wrapper.events !== undefined) {
|
||||
eventsHandler(dependencies);
|
||||
|
||||
if (maybeWrapper.events !== undefined) {
|
||||
eventsHandler(dependencies, maybeWrapper.events);
|
||||
}
|
||||
|
||||
const initCallsite = callsites()[1].getFileName();
|
||||
const presencePath = Files.shouldHandle(initCallsite!, "presence");
|
||||
//Ready event: load all modules and when finished, time should be taken and logged
|
||||
readyHandler(dependencies)
|
||||
.add(() => {
|
||||
logger?.info({ message: "Client signaled ready, registering modules" });
|
||||
ready(maybeWrapper.commands, dependencies)
|
||||
.then(() => {
|
||||
const time = ((performance.now() - startTime) / 1000).toFixed(2);
|
||||
dependencies[0].emit('modulesLoaded');
|
||||
logger?.info({ message: `sern: registered in ${time} s`, });
|
||||
if(presencePath.exists) {
|
||||
const setPresence = async (p: any) => {
|
||||
//@ts-ignore
|
||||
return (dependencies[3] as Client).user?.setPresence(p);
|
||||
}
|
||||
presenceHandler(presencePath.path, setPresence).subscribe();
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(err => { throw err });
|
||||
|
||||
const messages$ = messageHandler(dependencies, wrapper.defaultPrefix);
|
||||
const messages$ = messageHandler(dependencies, maybeWrapper.defaultPrefix);
|
||||
const interactions$ = interactionHandler(dependencies);
|
||||
// listening to the message stream and interaction stream
|
||||
merge(messages$, interactions$).pipe(handleCrash(errorHandler, dependencies[0], logger)).subscribe();
|
||||
|
||||
@@ -35,6 +35,10 @@ export interface Module {
|
||||
onEvent: ControlPlugin[];
|
||||
plugins: InitPlugin[];
|
||||
description?: string;
|
||||
meta: {
|
||||
id: string;
|
||||
absPath: string;
|
||||
}
|
||||
execute(...args: any[]): Awaitable<any>;
|
||||
}
|
||||
|
||||
@@ -44,12 +48,18 @@ export interface SernEventCommand<T extends keyof SernEventsMapping = keyof Sern
|
||||
type: EventType.Sern;
|
||||
execute(...args: SernEventsMapping[T]): Awaitable<unknown>;
|
||||
}
|
||||
|
||||
export interface ExternalEventCommand extends Module {
|
||||
name?: string;
|
||||
emitter: keyof Dependencies;
|
||||
type: EventType.External;
|
||||
execute(...args: unknown[]): Awaitable<unknown>;
|
||||
}
|
||||
export interface CronEventCommand extends Module {
|
||||
name?: string;
|
||||
type: EventType.Cron;
|
||||
execute(...args: unknown[]): Awaitable<unknown>;
|
||||
}
|
||||
|
||||
export interface ContextMenuUser extends Module {
|
||||
type: CommandType.CtxUser;
|
||||
@@ -127,7 +137,7 @@ export interface BothCommand extends Module {
|
||||
execute: (ctx: Context, args: Args) => Awaitable<unknown>;
|
||||
}
|
||||
|
||||
export type EventModule = DiscordEventCommand | SernEventCommand | ExternalEventCommand;
|
||||
export type EventModule = DiscordEventCommand | SernEventCommand | ExternalEventCommand | CronEventCommand;
|
||||
export type CommandModule =
|
||||
| TextCommand
|
||||
| SlashCommand
|
||||
@@ -178,10 +188,10 @@ export interface SernAutocompleteData
|
||||
}
|
||||
|
||||
type CommandModuleNoPlugins = {
|
||||
[T in CommandType]: Omit<CommandModuleDefs[T], 'plugins' | 'onEvent'>;
|
||||
[T in CommandType]: Omit<CommandModuleDefs[T], 'plugins' | 'onEvent' | 'meta'>;
|
||||
};
|
||||
type EventModulesNoPlugins = {
|
||||
[T in EventType]: Omit<EventModuleDefs[T], 'plugins' | 'onEvent'>;
|
||||
[T in EventType]: Omit<EventModuleDefs[T], 'plugins' | 'onEvent' | 'meta'>;
|
||||
};
|
||||
|
||||
export type InputEvent = {
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
import type { Container } from '../core/ioc/container';
|
||||
import * as Contracts from '../core/interfaces';
|
||||
import type { UnpackFunction } from './utility'
|
||||
/**
|
||||
* Type to annotate that something is a singleton.
|
||||
* T is created once and lazily.
|
||||
*/
|
||||
export type Singleton<T> = () => T;
|
||||
/**
|
||||
* Type to annotate that something is transient.
|
||||
* Every time this is called, a new object is created
|
||||
*/
|
||||
export type Transient<T> = () => () => T;
|
||||
|
||||
import type { Client } from 'discord.js'
|
||||
import { Module } from './core-modules';
|
||||
export type DependencyList = [
|
||||
Contracts.Emitter,
|
||||
Contracts.ErrorHandling,
|
||||
Contracts.Logging | undefined,
|
||||
Contracts.Emitter,
|
||||
Client,
|
||||
Map<string, Module>
|
||||
];
|
||||
|
||||
export interface CoreDependencies {
|
||||
'@sern/client': () => Contracts.Emitter;
|
||||
'@sern/client': () => Client;
|
||||
'@sern/emitter': () => Contracts.Emitter;
|
||||
'@sern/errors': () => Contracts.ErrorHandling;
|
||||
'@sern/logger'?: () => Contracts.Logging;
|
||||
'@sern/modules': () => Map<string, Module>
|
||||
}
|
||||
|
||||
export type DependencyFromKey<T extends keyof Dependencies> = Dependencies[T];
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Module } from './core-modules';
|
||||
|
||||
export type Awaitable<T> = PromiseLike<T> | T;
|
||||
|
||||
export type AnyFunction = (...args: unknown[]) => unknown;
|
||||
export type AnyFunction = (...args: never[]) => unknown;
|
||||
|
||||
// Thanks to @kelsny
|
||||
type ParseType<T> = {
|
||||
|
||||
46
yarn.lock
46
yarn.lock
@@ -235,12 +235,14 @@ __metadata:
|
||||
resolution: "@sern/handler@workspace:."
|
||||
dependencies:
|
||||
"@faker-js/faker": ^8.0.1
|
||||
"@types/node": ~18.17.11
|
||||
"@types/node": ^20.0.0
|
||||
"@types/node-cron": ^3.0.11
|
||||
"@typescript-eslint/eslint-plugin": 5.58.0
|
||||
"@typescript-eslint/parser": 5.59.1
|
||||
callsites: ^3.1.0
|
||||
discord.js: ^14.11.0
|
||||
eslint: 8.39.0
|
||||
node-cron: ^3.0.3
|
||||
prettier: 2.8.8
|
||||
rxjs: ^7.8.0
|
||||
ts-results-es: ^4.1.0
|
||||
@@ -255,6 +257,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node-cron@npm:^3.0.11":
|
||||
version: 3.0.11
|
||||
resolution: "@types/node-cron@npm:3.0.11"
|
||||
checksum: a73f69bcca52a5f3b1671cfb00a8e4a1d150d0aef36a611564a2f94e66b6981bade577e267ceeeca6fcee241768902d55eb8cf3a81f9ef4ed767a23112fdb16d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*":
|
||||
version: 20.5.9
|
||||
resolution: "@types/node@npm:20.5.9"
|
||||
@@ -262,10 +271,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:~18.17.11":
|
||||
version: 18.17.19
|
||||
resolution: "@types/node@npm:18.17.19"
|
||||
checksum: 6ab47127cd7534511aa199550659d56b44e5a6dbec9df054d0cde279926b4d43f0e6438f92c8392b039ab4e2a85aa0f698b95926430aff860e23bfc36c96576c
|
||||
"@types/node@npm:^20.0.0":
|
||||
version: 20.12.12
|
||||
resolution: "@types/node@npm:20.12.12"
|
||||
dependencies:
|
||||
undici-types: ~5.26.4
|
||||
checksum: 5373983874b9af7c216e7ca5d26b32a8d9829c703a69f1e66f2113598b5be8582c0e009ca97369f1ec9a6282b3f92812208d06eb1e9fc3bd9b939b022303d042
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1198,6 +1209,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-cron@npm:^3.0.3":
|
||||
version: 3.0.3
|
||||
resolution: "node-cron@npm:3.0.3"
|
||||
dependencies:
|
||||
uuid: 8.3.2
|
||||
checksum: 351c37491ebf717d0ae69cc941465de118e5c2ef5d48bc3f87c98556241b060f100402c8a618c7b86f9f626b44756b20d8b5385b70e52f80716f21e55db0f1c5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"once@npm:^1.3.0":
|
||||
version: 1.4.0
|
||||
resolution: "once@npm:1.4.0"
|
||||
@@ -1506,6 +1526,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici-types@npm:~5.26.4":
|
||||
version: 5.26.5
|
||||
resolution: "undici-types@npm:5.26.5"
|
||||
checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici@npm:5.27.2":
|
||||
version: 5.27.2
|
||||
resolution: "undici@npm:5.27.2"
|
||||
@@ -1524,6 +1551,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"uuid@npm:8.3.2":
|
||||
version: 8.3.2
|
||||
resolution: "uuid@npm:8.3.2"
|
||||
bin:
|
||||
uuid: dist/bin/uuid
|
||||
checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which@npm:^2.0.1":
|
||||
version: 2.0.2
|
||||
resolution: "which@npm:2.0.2"
|
||||
|
||||
Reference in New Issue
Block a user