style: pretty please (#247)

Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
This commit is contained in:
github-actions[bot]
2023-03-09 16:11:29 -06:00
committed by GitHub
parent 09610d0501
commit 507c9e7939
8 changed files with 2591 additions and 1882 deletions

4120
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,4 +21,3 @@ export class DefaultModuleManager implements ModuleManager {
strat(this.moduleStore);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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