mirror of
https://github.com/sern-handler/handler
synced 2026-06-27 18:22:14 +00:00
handling customId params working
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { ApplicationCommandType, ComponentType, Interaction, InteractionType } from 'discord.js';
|
||||
import { ApplicationCommandType, ComponentType, type Interaction, InteractionType } from 'discord.js';
|
||||
import { CommandType, EventType } from './structures/enums';
|
||||
|
||||
const parseParams = (event: { customId: string }, id: string, append: string) => {
|
||||
const hasSlash = event.customId.indexOf('/')
|
||||
if(hasSlash === -1) {
|
||||
return { id };
|
||||
return { id:id+append };
|
||||
}
|
||||
const baseid = event.customId.substring(0, hasSlash);
|
||||
const params = event.customId.substring(hasSlash+1);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { DependencyConfiguration } from '../../types/ioc';
|
||||
import { Container } from './container';
|
||||
import * as __Services from '../structures/default-services';
|
||||
import type { Logging } from '../interfaces';
|
||||
@@ -11,11 +10,11 @@ export function disposeAll(logger: Logging|undefined) {
|
||||
.then(() => logger?.info({ message: 'Cleaning container and crashing' }));
|
||||
}
|
||||
|
||||
|
||||
type Insertable =
|
||||
| ((container: Dependencies) => object)
|
||||
| object
|
||||
const dependencyBuilder = (container: Container, excluded: string[] ) => {
|
||||
|
||||
const dependencyBuilder = (container: Container) => {
|
||||
return {
|
||||
/**
|
||||
* Insert a dependency into your container.
|
||||
@@ -29,14 +28,6 @@ const dependencyBuilder = (container: Container, excluded: string[] ) => {
|
||||
container.addWiredSingleton(key, (cntr) => v(cntr))
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Exclude any dependencies from being added.
|
||||
* Warning: this could lead to bad errors if not used correctly
|
||||
*/
|
||||
exclude(...keys: (keyof Dependencies)[]) {
|
||||
keys.forEach(key => excluded.push(key));
|
||||
},
|
||||
|
||||
/**
|
||||
* @param key the key of the dependency
|
||||
* @param v The dependency to swap out.
|
||||
@@ -49,60 +40,28 @@ const dependencyBuilder = (container: Container, excluded: string[] ) => {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
type ValidDependencyConfig =
|
||||
| ((c: ReturnType<typeof dependencyBuilder>) => any)
|
||||
| DependencyConfiguration;
|
||||
|
||||
/**
|
||||
* Given the user's conf, check for any excluded/included dependency keys.
|
||||
* Then, call conf.build to get the rest of the users' dependencies.
|
||||
* Finally, update the containerSubject with the new container state
|
||||
* @param conf
|
||||
*/
|
||||
async function composeRoot(
|
||||
container: Container,
|
||||
conf: DependencyConfiguration,
|
||||
) {
|
||||
//container should have no client or logger yet.
|
||||
const hasLogger = container.hasKey('@sern/logger');
|
||||
if (!hasLogger) {
|
||||
__add_container('@sern/logger', new __Services.DefaultLogging());
|
||||
}
|
||||
__add_container('@sern/errors', new __Services.DefaultErrorHandling());
|
||||
__add_container('@sern/modules', new Map())
|
||||
__add_container('@sern/emitter', new EventEmitter())
|
||||
__add_wiredcontainer('@sern/cron', deps => new __Services.Cron(deps))
|
||||
//Build the container based on the callback provided by the user
|
||||
conf.build(container as Container);
|
||||
|
||||
if (!hasLogger) {
|
||||
container.get<Logging>('@sern/logger')
|
||||
?.info({ message: 'All dependencies loaded successfully.' });
|
||||
}
|
||||
await container.ready();
|
||||
}
|
||||
|
||||
export async function makeDependencies (conf: ValidDependencyConfig) {
|
||||
const container = await __init_container({ autowire: false });
|
||||
if(typeof conf === 'function') {
|
||||
const excluded: string[] = [];
|
||||
conf(dependencyBuilder(container, excluded));
|
||||
//We only include logger if it does not exist
|
||||
const includeLogger =
|
||||
!excluded.includes('@sern/logger')
|
||||
&& !container.hasKey('@sern/logger');
|
||||
conf(dependencyBuilder(container));
|
||||
//We only include logger if it does not exist
|
||||
const includeLogger = !container.hasKey('@sern/logger');
|
||||
|
||||
if(includeLogger) {
|
||||
__add_container('@sern/logger', new __Services.DefaultLogging);
|
||||
}
|
||||
__add_container('@sern/errors', new __Services.DefaultErrorHandling());
|
||||
__add_container('@sern/modules', new Map())
|
||||
__add_container('@sern/emitter', new EventEmitter())
|
||||
__add_wiredcontainer('@sern/cron', deps => new __Services.Cron(deps))
|
||||
await container.ready();
|
||||
} else {
|
||||
await composeRoot(container, conf);
|
||||
if(includeLogger) {
|
||||
__add_container('@sern/logger', new __Services.DefaultLogging);
|
||||
}
|
||||
__add_container('@sern/errors', new __Services.DefaultErrorHandling);
|
||||
__add_container('@sern/modules', new Map)
|
||||
__add_container('@sern/emitter', new EventEmitter)
|
||||
__add_wiredcontainer('@sern/cron', deps => new __Services.Cron(deps))
|
||||
await container.ready();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
|
||||
get options() {
|
||||
return this.interaction.options;
|
||||
}
|
||||
|
||||
//TODO
|
||||
args(type: 'message'|'interaction', parser?: Function) {
|
||||
switch(type) {
|
||||
case 'message': {
|
||||
@@ -42,10 +42,12 @@ export class Context extends CoreContext<Message, ChatInputCommandInteraction> {
|
||||
}
|
||||
|
||||
protected constructor(protected ctx: Result<Message, ChatInputCommandInteraction>,
|
||||
public prefix?: string) {
|
||||
private __prefix?: string) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
public get prefix() {
|
||||
return this.__prefix;
|
||||
}
|
||||
public get id(): Snowflake {
|
||||
return safeUnwrap(this.ctx
|
||||
.map(m => m.id)
|
||||
|
||||
@@ -181,7 +181,7 @@ export function executeModule(
|
||||
* - if all results are ok, the stream is converted to { config.onNext }
|
||||
* config.onNext will be returned if everything is okay.
|
||||
* @param config
|
||||
* @returns receiver function for flattening a stream of data
|
||||
* @returns function which calls all plugins and returns onNext or fail
|
||||
*/
|
||||
export function createResultResolver<Output>(config: {
|
||||
onStop?: (module: Module, err?: string) => unknown;
|
||||
@@ -198,11 +198,11 @@ export function createResultResolver<Output>(config: {
|
||||
};
|
||||
};
|
||||
|
||||
export async function callInitPlugins(module: Module, deps: Dependencies, sEmitter?: Emitter) {
|
||||
export async function callInitPlugins(module: Module, deps: Dependencies, emit?: boolean ) {
|
||||
let _module = module;
|
||||
for(const plugin of _module.plugins ?? []) {
|
||||
const res = await plugin.execute({
|
||||
module, absPath: _module.meta.absPath ,
|
||||
module, absPath: _module.meta.absPath,
|
||||
updateModule: (partial: Partial<Module>) => {
|
||||
_module = { ..._module, ...partial };
|
||||
return _module;
|
||||
@@ -210,7 +210,10 @@ export async function callInitPlugins(module: Module, deps: Dependencies, sEmitt
|
||||
deps
|
||||
});
|
||||
if(res.isErr()) {
|
||||
sEmitter?.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
if(emit) {
|
||||
deps['@sern/emitter']
|
||||
?.emit('module.register', resultPayload(PayloadType.Failure, module, SernError.PluginFailure));
|
||||
}
|
||||
throw Error("Plugin failed with controller.stop()");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default async function(dir: string, deps : UnpackedDependencies) {
|
||||
if(!validType) {
|
||||
throw Error(`Found ${module.name} at ${module.meta.absPath}, which has incorrect \`type\``);
|
||||
}
|
||||
const resultModule = await callInitPlugins(module, deps, sEmitter);
|
||||
const resultModule = await callInitPlugins(module, deps, true);
|
||||
// FREEZE! no more writing!!
|
||||
commands.set(resultModule.meta.id, Object.freeze(resultModule));
|
||||
sEmitter.emit('module.register', resultPayload(PayloadType.Success, resultModule));
|
||||
|
||||
@@ -33,10 +33,7 @@ export default async function(deps: UnpackedDependencies, eventDir: string) {
|
||||
}
|
||||
from(eventModules)
|
||||
.pipe(map(intoDispatcher(deps)),
|
||||
/**
|
||||
* Where all events are turned on
|
||||
*/
|
||||
mergeAll(),
|
||||
mergeAll(), // all eventListeners are turned on
|
||||
handleCrash(deps))
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//@ts-nocheck
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { PluginType, SernOptionsData, controller } from '../../src/index';
|
||||
import { partitionPlugins, treeSearch } from '../../src/core/functions';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { ApplicationCommandOptionType, AutocompleteInteraction } from 'discord.js';
|
||||
|
||||
vi.mock('discord.js', () => {
|
||||
const Collection = Map;
|
||||
vi.mock('discord.js', async (importOriginal) => {
|
||||
const mod = await importOriginal()
|
||||
const ModalSubmitInteraction = class {
|
||||
customId;
|
||||
type = 5;
|
||||
@@ -36,35 +38,11 @@ vi.mock('discord.js', () => {
|
||||
};
|
||||
|
||||
return {
|
||||
Collection,
|
||||
ComponentType: {
|
||||
Button: 2,
|
||||
},
|
||||
InteractionType: {
|
||||
Ping: 1,
|
||||
ApplicationCommand: 2,
|
||||
MessageComponent: 3,
|
||||
ApplicationCommandAutocomplete: 4,
|
||||
ModalSubmit: 5,
|
||||
},
|
||||
ApplicationCommandOptionType: {
|
||||
Subcommand: 1,
|
||||
SubcommandGroup: 2,
|
||||
String: 3,
|
||||
Integer: 4,
|
||||
Boolean: 5,
|
||||
User: 6,
|
||||
Channel: 7,
|
||||
Role: 8,
|
||||
Mentionable: 9,
|
||||
Number: 10,
|
||||
Attachment: 11,
|
||||
},
|
||||
ApplicationCommandType: {
|
||||
ChatInput: 1,
|
||||
User: 2,
|
||||
Message: 3,
|
||||
},
|
||||
Collection: mod.Collection,
|
||||
ComponentType: mod.ComponentType,
|
||||
InteractionType: mod.InteractionType,
|
||||
ApplicationCommandOptionType: mod.ApplicationCommandOptionType,
|
||||
ApplicationCommandType: mod.ApplicationCommandType,
|
||||
ModalSubmitInteraction,
|
||||
ButtonInteraction,
|
||||
AutocompleteInteraction,
|
||||
|
||||
@@ -1,7 +1,51 @@
|
||||
//@ts-nocheck
|
||||
import { expect, test, vi } from 'vitest'
|
||||
import { CommandType } from '../../src/core/structures/enums';
|
||||
import * as Id from '../../src/core/id'
|
||||
import { expect, test } from 'vitest'
|
||||
|
||||
import * as Id from '../../src/core/id'
|
||||
import { ButtonInteraction, ModalSubmitInteraction } from 'discord.js';
|
||||
vi.mock('discord.js', async (importOriginal) => {
|
||||
const mod = await importOriginal()
|
||||
const ModalSubmitInteraction = class {
|
||||
customId;
|
||||
type = 5;
|
||||
isModalSubmit = vi.fn();
|
||||
constructor(customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
};
|
||||
const ButtonInteraction = class {
|
||||
customId;
|
||||
type = 3;
|
||||
componentType = 2;
|
||||
isButton = vi.fn();
|
||||
constructor(customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
};
|
||||
const AutocompleteInteraction = class {
|
||||
type = 4;
|
||||
option: string;
|
||||
constructor(s: string) {
|
||||
this.option = s;
|
||||
}
|
||||
options = {
|
||||
getFocused: vi.fn(),
|
||||
getSubcommand: vi.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
Collection: mod.Collection,
|
||||
ComponentType: mod.ComponentType,
|
||||
InteractionType: mod.InteractionType,
|
||||
ApplicationCommandOptionType: mod.ApplicationCommandOptionType,
|
||||
ApplicationCommandType: mod.ApplicationCommandType,
|
||||
ModalSubmitInteraction,
|
||||
ButtonInteraction,
|
||||
AutocompleteInteraction,
|
||||
};
|
||||
});
|
||||
test('id -> Text', () => {
|
||||
const bothCmdId = Id.create("ping", CommandType.Text)
|
||||
expect(bothCmdId).toBe("ping_T")
|
||||
@@ -60,5 +104,37 @@ test('id -> ChannelSelect', () => {
|
||||
expect(modal).toBe("mychannelselect_C8");
|
||||
})
|
||||
|
||||
test('id reconstruct button', () => {
|
||||
const idload = Id.reconstruct(new ButtonInteraction("btn"))
|
||||
expect(idload[0].id).toBe("btn_C2")
|
||||
})
|
||||
|
||||
test('id reconstruct button with params', () => {
|
||||
const idload = Id.reconstruct(new ButtonInteraction("btn/asdf"))
|
||||
expect(idload[0].id).toBe("btn_C2")
|
||||
expect(idload[0].params).toBe("asdf")
|
||||
})
|
||||
test('id reconstruct modal with params', () => {
|
||||
const idload = Id.reconstruct(new ModalSubmitInteraction("btn/asdf"))
|
||||
expect(idload[0].id).toBe("btn_M")
|
||||
expect(idload[0].params).toBe("asdf")
|
||||
})
|
||||
test('id reconstruct modal', () => {
|
||||
const idload = Id.reconstruct(new ModalSubmitInteraction("btn"))
|
||||
expect(idload[0].id).toBe("btn_M")
|
||||
expect(idload[0].params).toBe(undefined)
|
||||
})
|
||||
test('id reconstruct button with empty params', () => {
|
||||
const idload = Id.reconstruct(new ButtonInteraction("btn/"))
|
||||
expect(idload[0].id).toBe("btn_C2")
|
||||
expect(idload[0].params).toBe("")
|
||||
})
|
||||
|
||||
test('id reconstruct button', () => {
|
||||
const idload = Id.reconstruct(new ButtonInteraction("btn"))
|
||||
expect(idload[0].id).toBe("btn_C2")
|
||||
expect(idload[0].params).toBe(undefined)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { beforeEach, describe, expect, vi, it } from 'vitest';
|
||||
import { eventDispatcher } from '../../src/handlers/event-utils';
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { Module } from '../../src/types/core-modules';
|
||||
import { Processed } from '../../src/types/core-modules';
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventType } from '../../dist/core/structures/enums';
|
||||
|
||||
function createRandomModule(): Processed<Module> {
|
||||
return {
|
||||
type: EventType.Discord,
|
||||
meta: { id:"", absPath: "" },
|
||||
description: faker.string.alpha(),
|
||||
name: "abc",
|
||||
onEvent: [],
|
||||
plugins: [],
|
||||
execute: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function mockDeps() {
|
||||
return {
|
||||
'@sern/client': {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
describe('eventDispatcher standard', () => {
|
||||
let m: Processed<Module>;
|
||||
let ee: EventEmitter;
|
||||
beforeEach(() => {
|
||||
ee = new EventEmitter();
|
||||
m = createRandomModule();
|
||||
});
|
||||
|
||||
it('should throw', () => {
|
||||
expect(() => eventDispatcher(mockDeps(), m, 'not event emitter')).toThrowError();
|
||||
});
|
||||
|
||||
it("Shouldn't throw", () => {
|
||||
expect(() => eventDispatcher(mockDeps(), m, ee)).not.toThrowError();
|
||||
});
|
||||
});
|
||||
136
test/handlers/index.test.ts
Normal file
136
test/handlers/index.test.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
//@ts-nocheck
|
||||
import { beforeEach, describe, expect, vi, it } from 'vitest';
|
||||
import { callInitPlugins, eventDispatcher } from '../../src/handlers/event-utils';
|
||||
|
||||
import { Client } from 'discord.js'
|
||||
import { faker } from '@faker-js/faker';
|
||||
import { Module } from '../../src/types/core-modules';
|
||||
import { Processed } from '../../src/types/core-modules';
|
||||
import { EventEmitter } from 'events';
|
||||
import { EventType } from '../../dist/core/structures/enums';
|
||||
import { CommandInitPlugin, controller } from '../../src';
|
||||
|
||||
vi.mock('discord.js', () => {
|
||||
const Client = vi.fn()
|
||||
Client.prototype.login= vi.fn()
|
||||
const Collection = Map;
|
||||
const ModalSubmitInteraction = class {
|
||||
customId;
|
||||
type = 5;
|
||||
isModalSubmit = vi.fn();
|
||||
constructor(customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
};
|
||||
const ButtonInteraction = class {
|
||||
customId;
|
||||
type = 3;
|
||||
componentType = 2;
|
||||
isButton = vi.fn();
|
||||
constructor(customId) {
|
||||
this.customId = customId;
|
||||
}
|
||||
};
|
||||
const AutocompleteInteraction = class {
|
||||
type = 4;
|
||||
option: string;
|
||||
constructor(s: string) {
|
||||
this.option = s;
|
||||
}
|
||||
options = {
|
||||
getFocused: vi.fn(),
|
||||
getSubcommand: vi.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
Client,
|
||||
Collection,
|
||||
ComponentType: {
|
||||
Button: 2,
|
||||
},
|
||||
InteractionType: {
|
||||
Ping: 1,
|
||||
ApplicationCommand: 2,
|
||||
MessageComponent: 3,
|
||||
ApplicationCommandAutocomplete: 4,
|
||||
ModalSubmit: 5,
|
||||
},
|
||||
ApplicationCommandOptionType: {
|
||||
Subcommand: 1,
|
||||
SubcommandGroup: 2,
|
||||
String: 3,
|
||||
Integer: 4,
|
||||
Boolean: 5,
|
||||
User: 6,
|
||||
Channel: 7,
|
||||
Role: 8,
|
||||
Mentionable: 9,
|
||||
Number: 10,
|
||||
Attachment: 11,
|
||||
},
|
||||
ApplicationCommandType: {
|
||||
ChatInput: 1,
|
||||
User: 2,
|
||||
Message: 3,
|
||||
},
|
||||
ModalSubmitInteraction,
|
||||
ButtonInteraction,
|
||||
AutocompleteInteraction,
|
||||
};
|
||||
})
|
||||
function createRandomPlugin (s: 'go', mut?: Partial<Module>) {
|
||||
return CommandInitPlugin(({ module, updateModule }) => {
|
||||
if(mut) {
|
||||
updateModule(mut)
|
||||
}
|
||||
return s == 'go'
|
||||
? controller.next()
|
||||
: controller.stop()
|
||||
})
|
||||
}
|
||||
function createRandomModule(plugins: any[]): Processed<Module> {
|
||||
return {
|
||||
type: EventType.Discord,
|
||||
meta: { id:"", absPath: "" },
|
||||
description: faker.string.alpha(),
|
||||
plugins,
|
||||
name: "cheese",
|
||||
onEvent: [],
|
||||
execute: vi.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
function mockDeps() {
|
||||
return {
|
||||
'@sern/client': new Client(),
|
||||
'@sern/emitter': new EventEmitter()
|
||||
}
|
||||
}
|
||||
|
||||
describe('eventDispatcher standard', () => {
|
||||
let m: Processed<Module>;
|
||||
let ee: EventEmitter;
|
||||
beforeEach(() => {
|
||||
ee = new EventEmitter();
|
||||
m = createRandomModule();
|
||||
});
|
||||
|
||||
it('should throw', () => {
|
||||
expect(() => eventDispatcher(mockDeps(), m, 'not event emitter')).toThrowError();
|
||||
});
|
||||
|
||||
it("Shouldn't throw", () => {
|
||||
expect(() => eventDispatcher(mockDeps(), m, ee)).not.toThrowError();
|
||||
});
|
||||
it('mutate with init plugins', async () => {
|
||||
const deps = mockDeps()
|
||||
const plugins = createRandomPlugin('go', { name: "abc" })
|
||||
const mod = createRandomModule([plugins])
|
||||
const s = await callInitPlugins(mod, deps, false)
|
||||
expect(s.name).not.equal(mod.name)
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user