mirror of
https://github.com/sern-handler/handler
synced 2026-06-09 09:22:14 +00:00
style: pretty please (#247)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
09610d0501
commit
507c9e7939
4120
pnpm-lock.yaml
generated
4120
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -21,4 +21,3 @@ export class DefaultModuleManager implements ModuleManager {
|
||||
strat(this.moduleStore);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DefaultErrorHandling, DefaultLogging, DefaultModuleManager } from '../c
|
||||
import { Result } from 'ts-results-es';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { createContainer } from 'iti';
|
||||
import { type Wrapper, ModuleStore, SernError} from '../structures';
|
||||
import { type Wrapper, ModuleStore, SernError } from '../structures';
|
||||
|
||||
export const containerSubject = new BehaviorSubject(defaultContainer());
|
||||
|
||||
@@ -71,20 +71,16 @@ function defaultContainer() {
|
||||
>;
|
||||
}
|
||||
|
||||
|
||||
export function makeFetcher(
|
||||
wrapper: Wrapper
|
||||
) {
|
||||
const requiredDependencyKeys = [
|
||||
'@sern/emitter',
|
||||
'@sern/client',
|
||||
'@sern/errors',
|
||||
'@sern/logger'
|
||||
] as ['@sern/emitter', '@sern/client', '@sern/errors', '@sern/logger'];
|
||||
return <Keys extends (keyof Dependencies)[]>(otherKeys: [...Keys]) =>
|
||||
wrapper
|
||||
.containerConfig
|
||||
.get(...requiredDependencyKeys, ...otherKeys) as MapDeps<Dependencies, [...typeof requiredDependencyKeys, ...Keys]>; }
|
||||
|
||||
|
||||
|
||||
export function makeFetcher(wrapper: Wrapper) {
|
||||
const requiredDependencyKeys = [
|
||||
'@sern/emitter',
|
||||
'@sern/client',
|
||||
'@sern/errors',
|
||||
'@sern/logger',
|
||||
] as ['@sern/emitter', '@sern/client', '@sern/errors', '@sern/logger'];
|
||||
return <Keys extends (keyof Dependencies)[]>(otherKeys: [...Keys]) =>
|
||||
wrapper.containerConfig.get(...requiredDependencyKeys, ...otherKeys) as MapDeps<
|
||||
Dependencies,
|
||||
[...typeof requiredDependencyKeys, ...Keys]
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import type { Interaction } from 'discord.js';
|
||||
import { catchError, concatMap, EMPTY, filter, finalize, fromEvent, map, Observable, of, OperatorFunction, pipe } from 'rxjs';
|
||||
import {
|
||||
catchError,
|
||||
concatMap,
|
||||
EMPTY,
|
||||
filter,
|
||||
finalize,
|
||||
fromEvent,
|
||||
map,
|
||||
Observable,
|
||||
of,
|
||||
OperatorFunction,
|
||||
pipe,
|
||||
} from 'rxjs';
|
||||
import { CommandType, type ModuleStore, SernError } from '../structures';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from './dispatchers';
|
||||
@@ -12,94 +24,99 @@ import { useContainerRaw } from '../dependencies';
|
||||
import type { Logging, ModuleManager } from '../contracts';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
|
||||
|
||||
function makeInteractionProcessor(
|
||||
modules: ModuleManager
|
||||
): OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }> {
|
||||
modules: ModuleManager,
|
||||
): OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }> {
|
||||
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
|
||||
return modules.get(cb);
|
||||
return modules.get(cb);
|
||||
};
|
||||
return pipe(
|
||||
return pipe(
|
||||
concatMap(event => {
|
||||
if (event.isMessageComponent()) {
|
||||
const module = get(ms =>
|
||||
ms.InteractionHandlers[event.componentType].get(event.customId),
|
||||
);
|
||||
return of({module, event});
|
||||
} else if (event.isCommand() || event.isAutocomplete()) {
|
||||
const module = get(ms =>
|
||||
/**
|
||||
* try to fetch from ApplicationCommands, if nothing, try BothCommands
|
||||
* exists on the API but not sern
|
||||
*/
|
||||
ms.ApplicationCommands[event.commandType].get(event.commandName) ??
|
||||
ms.BothCommands.get(event.commandName),
|
||||
);
|
||||
return of({ module, event });
|
||||
} else if (event.isModalSubmit()) {
|
||||
const module = get(ms => ms.ModalSubmit.get(event.customId));
|
||||
return of({ module, event });
|
||||
}
|
||||
else return EMPTY;
|
||||
}),
|
||||
filter(m => m.module !== undefined)
|
||||
if (event.isMessageComponent()) {
|
||||
const module = get(ms =>
|
||||
ms.InteractionHandlers[event.componentType].get(event.customId),
|
||||
);
|
||||
return of({ module, event });
|
||||
} else if (event.isCommand() || event.isAutocomplete()) {
|
||||
const module = get(
|
||||
ms =>
|
||||
/**
|
||||
* try to fetch from ApplicationCommands, if nothing, try BothCommands
|
||||
* exists on the API but not sern
|
||||
*/
|
||||
ms.ApplicationCommands[event.commandType].get(event.commandName) ??
|
||||
ms.BothCommands.get(event.commandName),
|
||||
);
|
||||
return of({ module, event });
|
||||
} else if (event.isModalSubmit()) {
|
||||
const module = get(ms => ms.ModalSubmit.get(event.customId));
|
||||
return of({ module, event });
|
||||
} else return EMPTY;
|
||||
}),
|
||||
filter(m => m.module !== undefined),
|
||||
) as OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }>;
|
||||
}
|
||||
|
||||
export function makeInteractionCreate(
|
||||
[s, client, err, log, modules]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined, ModuleManager]
|
||||
) {
|
||||
|
||||
export function makeInteractionCreate([s, client, err, log, modules]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
]) {
|
||||
//map. If nothing again,this means a slash command
|
||||
const interactionStream$ = fromEvent(client, 'interactionCreate') as Observable<Interaction>;
|
||||
const interactionProcessor = makeInteractionProcessor(modules);
|
||||
return interactionStream$.pipe(
|
||||
interactionProcessor,
|
||||
map(createDispatcher),
|
||||
makeModuleExecutor(module => {
|
||||
s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
}),
|
||||
concatMap(module => executeModule(s, module)),
|
||||
catchError(handleError(err, log)),
|
||||
finalize(() => {
|
||||
log?.info({ message: 'interactionCreate stream closed or reached end of lifetime' });
|
||||
useContainerRaw()
|
||||
?.disposeAll()
|
||||
.then(() => log?.info({ message: 'Cleaning container and crashing' }));
|
||||
})
|
||||
).subscribe();
|
||||
return interactionStream$
|
||||
.pipe(
|
||||
interactionProcessor,
|
||||
map(createDispatcher),
|
||||
makeModuleExecutor(module => {
|
||||
s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
}),
|
||||
concatMap(module => executeModule(s, module)),
|
||||
catchError(handleError(err, log)),
|
||||
finalize(() => {
|
||||
log?.info({
|
||||
message: 'interactionCreate stream closed or reached end of lifetime',
|
||||
});
|
||||
useContainerRaw()
|
||||
?.disposeAll()
|
||||
.then(() => log?.info({ message: 'Cleaning container and crashing' }));
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
function createDispatcher({
|
||||
module,
|
||||
event,
|
||||
}: {
|
||||
event: Interaction;
|
||||
module: Processed<CommandModule>;
|
||||
}) {
|
||||
return (
|
||||
match(module)
|
||||
.with({ type: CommandType.Text }, () => {
|
||||
throw Error(SernError.MismatchEvent);
|
||||
})
|
||||
//P.union = either CommandType.Slash or CommandType.Both
|
||||
.with({ type: P.union(CommandType.Slash, CommandType.Both) }, module => {
|
||||
if (event.isAutocomplete()) {
|
||||
/**
|
||||
* Autocomplete is a special case that
|
||||
* must be handled separately, since it's
|
||||
* too different from regular command modules
|
||||
*/
|
||||
return dispatchAutocomplete(module, event);
|
||||
} else {
|
||||
return dispatchCommand(module, contextArgs(event));
|
||||
}
|
||||
})
|
||||
/**
|
||||
* Every other command module takes a one argument parameter, its corresponding interaction
|
||||
* this makes this usage safe
|
||||
*/
|
||||
.otherwise(mod => dispatchCommand(mod, interactionArg(event)))
|
||||
);
|
||||
}
|
||||
|
||||
module,
|
||||
event,
|
||||
}: {
|
||||
event: Interaction;
|
||||
module: Processed<CommandModule>;
|
||||
}) {
|
||||
return (
|
||||
match(module)
|
||||
.with({ type: CommandType.Text }, () => {
|
||||
throw Error(SernError.MismatchEvent);
|
||||
})
|
||||
//P.union = either CommandType.Slash or CommandType.Both
|
||||
.with({ type: P.union(CommandType.Slash, CommandType.Both) }, module => {
|
||||
if (event.isAutocomplete()) {
|
||||
/**
|
||||
* Autocomplete is a special case that
|
||||
* must be handled separately, since it's
|
||||
* too different from regular command modules
|
||||
*/
|
||||
return dispatchAutocomplete(module, event);
|
||||
} else {
|
||||
return dispatchCommand(module, contextArgs(event));
|
||||
}
|
||||
})
|
||||
/**
|
||||
* Every other command module takes a one argument parameter, its corresponding interaction
|
||||
* this makes this usage safe
|
||||
*/
|
||||
.otherwise(mod => dispatchCommand(mod, interactionArg(event)))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,32 +19,39 @@ import type { EventEmitter } from 'node:events';
|
||||
*/
|
||||
const createMessageProcessor = (
|
||||
defaultPrefix: string,
|
||||
get: (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => CommandModule|undefined,
|
||||
) => pipe(
|
||||
ignoreNonBot(defaultPrefix),
|
||||
//This concatMap checks if module is undefined, and if it is, do not continue.
|
||||
// Synonymous to filterMap, but I haven't thought of a generic implementation for filterMap yet
|
||||
concatMap(message => {
|
||||
const [prefix, ...rest] = fmt(message, defaultPrefix);
|
||||
const module = get(
|
||||
ms => ms.TextCommands.get(prefix) ?? ms.BothCommands.get(prefix),
|
||||
);
|
||||
if (module === undefined) {
|
||||
return EMPTY;
|
||||
}
|
||||
const payload = {
|
||||
args: contextArgs(message, rest),
|
||||
module,
|
||||
};
|
||||
return of(payload);
|
||||
}),
|
||||
map(({ args, module }) => dispatchCommand(module as Processed<TextCommand>, args)),
|
||||
);
|
||||
get: (
|
||||
cb: (ms: ModuleStore) => Processed<CommandModule> | undefined,
|
||||
) => CommandModule | undefined,
|
||||
) =>
|
||||
pipe(
|
||||
ignoreNonBot(defaultPrefix),
|
||||
//This concatMap checks if module is undefined, and if it is, do not continue.
|
||||
// Synonymous to filterMap, but I haven't thought of a generic implementation for filterMap yet
|
||||
concatMap(message => {
|
||||
const [prefix, ...rest] = fmt(message, defaultPrefix);
|
||||
const module = get(ms => ms.TextCommands.get(prefix) ?? ms.BothCommands.get(prefix));
|
||||
if (module === undefined) {
|
||||
return EMPTY;
|
||||
}
|
||||
const payload = {
|
||||
args: contextArgs(message, rest),
|
||||
module,
|
||||
};
|
||||
return of(payload);
|
||||
}),
|
||||
map(({ args, module }) => dispatchCommand(module as Processed<TextCommand>, args)),
|
||||
);
|
||||
|
||||
export function makeMessageCreate(
|
||||
[s, client, err, log, modules]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined, ModuleManager],
|
||||
defaultPrefix?: string) {
|
||||
|
||||
[s, client, err, log, modules]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
],
|
||||
defaultPrefix?: string,
|
||||
) {
|
||||
if (!defaultPrefix) {
|
||||
return EMPTY.subscribe();
|
||||
}
|
||||
@@ -57,10 +64,7 @@ export function makeMessageCreate(
|
||||
.pipe(
|
||||
messageProcessor,
|
||||
makeModuleExecutor(module => {
|
||||
s.emit(
|
||||
'module.activate',
|
||||
SernEmitter.failure(module, SernError.PluginFailure),
|
||||
);
|
||||
s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
}),
|
||||
concatMap(payload => executeModule(s, payload)),
|
||||
catchError(handleError(err, log)),
|
||||
@@ -70,5 +74,6 @@ export function makeMessageCreate(
|
||||
?.disposeAll()
|
||||
.then(() => log?.info({ message: 'Cleaning container and crashing' }));
|
||||
}),
|
||||
).subscribe();
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,13 @@ import SernEmitter from '../sernEmitter';
|
||||
import type { EventEmitter } from 'node:events';
|
||||
|
||||
export function makeReadyEvent(
|
||||
[sEmitter,client, errorHandler,, moduleManager]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined, ModuleManager],
|
||||
[sEmitter, client, errorHandler, , moduleManager]: [
|
||||
SernEmitter,
|
||||
EventEmitter,
|
||||
ErrorHandling,
|
||||
Logging | undefined,
|
||||
ModuleManager,
|
||||
],
|
||||
commandDir: string,
|
||||
) {
|
||||
const readyOnce$ = fromEvent(client, 'ready').pipe(take(1));
|
||||
@@ -25,23 +31,28 @@ export function makeReadyEvent(
|
||||
}),
|
||||
defineAllFields(),
|
||||
);
|
||||
return readyOnce$.pipe(
|
||||
parseCommandModules,
|
||||
scanModule({
|
||||
onFailure: module => {
|
||||
sEmitter.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
},
|
||||
onSuccess: ({ module }) => {
|
||||
sEmitter.emit('module.register', SernEmitter.success(module));
|
||||
return module;
|
||||
},
|
||||
}),
|
||||
).subscribe(module => {
|
||||
const result = registerModule(moduleManager, module as Processed<CommandModule>);
|
||||
if (result.err) {
|
||||
errorHandler.crash(Error(SernError.InvalidModuleType));
|
||||
}
|
||||
});
|
||||
return readyOnce$
|
||||
.pipe(
|
||||
parseCommandModules,
|
||||
scanModule({
|
||||
onFailure: module => {
|
||||
sEmitter.emit(
|
||||
'module.register',
|
||||
SernEmitter.failure(module, SernError.PluginFailure),
|
||||
);
|
||||
},
|
||||
onSuccess: ({ module }) => {
|
||||
sEmitter.emit('module.register', SernEmitter.success(module));
|
||||
return module;
|
||||
},
|
||||
}),
|
||||
)
|
||||
.subscribe(module => {
|
||||
const result = registerModule(moduleManager, module as Processed<CommandModule>);
|
||||
if (result.err) {
|
||||
errorHandler.crash(Error(SernError.InvalidModuleType));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function registerModule<T extends Processed<CommandModule>>(
|
||||
|
||||
@@ -13,11 +13,10 @@ import { handleError } from '../contracts/errorHandling';
|
||||
import { defineAllFields } from './operators';
|
||||
import { useContainerRaw } from '../dependencies';
|
||||
|
||||
|
||||
export function makeEventsHandler(
|
||||
[s, client, err, log]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined],
|
||||
eventsPath: string,
|
||||
containerGetter: Wrapper['containerConfig']
|
||||
containerGetter: Wrapper['containerConfig'],
|
||||
) {
|
||||
const lazy = (k: string) => containerGetter.get(k as keyof Dependencies)[0];
|
||||
const eventStream$ = eventObservable(eventsPath, s);
|
||||
@@ -27,14 +26,11 @@ export function makeEventsHandler(
|
||||
scanModule({
|
||||
onFailure: module => s.emit('module.register', SernEmitter.success(module)),
|
||||
onSuccess: ({ module }) => {
|
||||
s.emit(
|
||||
'module.register',
|
||||
SernEmitter.failure(module, SernError.PluginFailure),
|
||||
);
|
||||
s.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure));
|
||||
return module;
|
||||
},
|
||||
}),
|
||||
);
|
||||
);
|
||||
const intoDispatcher = (e: Processed<EventModule | CommandModule>) =>
|
||||
match(e)
|
||||
.with({ type: EventType.Sern }, m => eventDispatcher(m, s))
|
||||
@@ -50,18 +46,17 @@ export function makeEventsHandler(
|
||||
mergeAll(),
|
||||
catchError(handleError(err, log)),
|
||||
finalize(() => {
|
||||
log?.info({ message: 'an event module reached end of lifetime'});
|
||||
log?.info({ message: 'an event module reached end of lifetime' });
|
||||
useContainerRaw()
|
||||
?.disposeAll()
|
||||
.then(() => {
|
||||
log?.info({ message: 'Cleaning container and crashing' });
|
||||
});
|
||||
})
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
|
||||
function eventObservable(events: string, emitter: SernEmitter) {
|
||||
return buildData<EventModule>(events).pipe(
|
||||
errTap(reason => {
|
||||
|
||||
@@ -41,23 +41,11 @@ export function init(wrapper: Wrapper) {
|
||||
const startTime = performance.now();
|
||||
const { events } = wrapper;
|
||||
if (events !== undefined) {
|
||||
makeEventsHandler(
|
||||
requiredDependenciesAnd([]),
|
||||
events,
|
||||
wrapper.containerConfig
|
||||
);
|
||||
makeEventsHandler(requiredDependenciesAnd([]), events, wrapper.containerConfig);
|
||||
}
|
||||
makeReadyEvent(
|
||||
requiredDependenciesAnd(['@sern/modules']),
|
||||
wrapper.commands
|
||||
);
|
||||
makeMessageCreate(
|
||||
requiredDependenciesAnd(['@sern/modules']),
|
||||
wrapper.defaultPrefix
|
||||
);
|
||||
makeInteractionCreate(
|
||||
requiredDependenciesAnd(['@sern/modules'])
|
||||
);
|
||||
makeReadyEvent(requiredDependenciesAnd(['@sern/modules']), wrapper.commands);
|
||||
makeMessageCreate(requiredDependenciesAnd(['@sern/modules']), wrapper.defaultPrefix);
|
||||
makeInteractionCreate(requiredDependenciesAnd(['@sern/modules']));
|
||||
const endTime = performance.now();
|
||||
logger?.info({ message: `sern : ${(endTime - startTime).toFixed(2)} ms` });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user