fix: crashing when slash command is used as text command (#349)

* progress on fix

* fix: ids
This commit is contained in:
Jacob Nguyen
2024-01-07 15:26:08 -06:00
committed by GitHub
parent 655bb8d358
commit a359f73fa2
6 changed files with 66 additions and 67 deletions

View File

@@ -4,20 +4,20 @@ import { CommandType, EventType } from './structures';
/**
* Construct 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.
* @returns An array of unique string IDs based on the type and properties of the interaction object.
*/
export function reconstruct<T extends Interaction>(event: T) {
switch (event.type) {
case InteractionType.MessageComponent: {
return `${event.customId}_C${event.componentType}`;
return [`${event.customId}_C${event.componentType}`];
}
case InteractionType.ApplicationCommand:
case InteractionType.ApplicationCommandAutocomplete: {
return `${event.commandName}_A${event.commandType}`;
return [`${event.commandName}_A${event.commandType}`, `${event.commandName}_B`];
}
//Modal interactions are classified as components for sern
case InteractionType.ModalSubmit: {
return `${event.customId}_C1`;
return [`${event.customId}_M`];
}
}
}
@@ -27,21 +27,20 @@ export function reconstruct<T extends Interaction>(event: T) {
*/
const appBitField = 0b000000001111;
// Each index represents the exponent of a CommandType.
// Every CommandType is a power of two.
export const CommandTypeDiscordApi = [
1, // CommandType.Text
ApplicationCommandType.ChatInput,
ApplicationCommandType.User,
ApplicationCommandType.Message,
ComponentType.Button,
ComponentType.StringSelect,
1, // CommandType.Modal
ComponentType.UserSelect,
ComponentType.RoleSelect,
ComponentType.MentionableSelect,
ComponentType.ChannelSelect,
];
const TypeMap = new Map<number, number>([
[CommandType.Text, 0],
[CommandType.Both, 0],
[CommandType.Slash, ApplicationCommandType.ChatInput],
[CommandType.CtxUser, ApplicationCommandType.User],
[CommandType.CtxMsg, ApplicationCommandType.Message],
[CommandType.Button, ComponentType.Button],
[CommandType.Modal, InteractionType.ModalSubmit],
[CommandType.StringSelect, ComponentType.StringSelect],
[CommandType.UserSelect, ComponentType.UserSelect],
[CommandType.MentionableSelect, ComponentType.MentionableSelect],
[CommandType.RoleSelect, ComponentType.RoleSelect],
[CommandType.ChannelSelect, ComponentType.ChannelSelect]]);
/*
* Generates a number based on CommandType.
@@ -49,8 +48,7 @@ export const CommandTypeDiscordApi = [
* TextCommands are 0 as they aren't either or.
*/
function apiType(t: CommandType | EventType) {
if (t === CommandType.Both || t === CommandType.Modal) return 1;
return CommandTypeDiscordApi[Math.log2(t)];
return TypeMap.get(t)!;
}
/*
@@ -59,6 +57,18 @@ function apiType(t: CommandType | EventType) {
* Then, another number generated by apiType function is appended
*/
export function create(name: string, type: CommandType | EventType) {
if(type == CommandType.Text) {
return `${name}_T`;
}
if(type == CommandType.Both) {
return `${name}_B`;
}
if(type == CommandType.Modal) {
return `${name}_M`;
}
const am = (appBitField & type) !== 0 ? 'A' : 'C';
return name + '_' + am + apiType(type);
return `${name}_${am}${apiType(type)}`
}

View File

@@ -44,7 +44,10 @@ export class DefaultModuleManager implements ModuleManager {
const publishable = 0b000000110;
return Promise.all(
Array.from(entries)
.filter(([id]) => !(Number.parseInt(id.at(-1)!) & publishable))
.filter(([id]) => {
const last_entry = id.at(-1);
return last_entry == 'B' || !(publishable & Number.parseInt(last_entry!));
})
.map(([, path]) => Files.importModule<CommandModule>(path)),
);
}

View File

@@ -11,7 +11,7 @@ import {
import { createResultResolver } from './event-utils';
import { BaseInteraction, Message } from 'discord.js';
import { CommandType, Context } from '../core';
import type { AnyFunction, Args } from '../types/utility';
import type { Args } from '../types/utility';
import { inspect } from 'node:util'
import type { CommandModule, Module, Processed } from '../types/core-modules';
@@ -77,7 +77,7 @@ export function createDispatcher(payload: {
if (isAutocomplete(payload.event)) {
const option = treeSearch(payload.event, payload.module.options);
assert.ok(option, SernError.NotSupportedInteraction + ` There is no autocomplete tag for ` + inspect(payload.module));
const { command, name, parent } = option;
const { command } = option;
return {
...payload,

View File

@@ -71,18 +71,23 @@ export function createInteractionHandler<T extends Interaction>(
return createGenericHandler<Interaction, T, Result<ReturnType<typeof createDispatcher>, void>>(
source,
async event => {
const fullPath = mg.get(Id.reconstruct(event));
if(!fullPath) {
return Err.EMPTY
const possibleIds = Id.reconstruct(event);
let fullPaths= possibleIds
.map(id => mg.get(id))
.filter((id): id is string => id !== undefined);
if(fullPaths.length == 0) {
return Err.EMPTY;
}
const [ path ] = fullPaths;
return Files
.defaultModuleLoader<Processed<CommandModule>>(fullPath)
.then(payload => Ok(createDispatcher({
module: payload.module,
event,
})));
},
);
.defaultModuleLoader<Processed<CommandModule>>(path)
.then(payload => Ok(createDispatcher({
module: payload.module,
event,
})));
},
);
}
export function createMessageHandler(
@@ -92,10 +97,12 @@ export function createMessageHandler(
) {
return createGenericHandler(source, async event => {
const [prefix, ...rest] = fmt(event.content, defaultPrefix);
const fullPath = mg.get(`${prefix}_A1`);
let fullPath = mg.get(`${prefix}_T`);
if(!fullPath) {
return Err('Possibly undefined behavior: could not find a static id to resolve')
fullPath = mg.get(`${prefix}_B`);
if(!fullPath) {
return Err('Possibly undefined behavior: could not find a static id to resolve');
}
}
return Files
.defaultModuleLoader<Processed<CommandModule>>(fullPath)

View File

@@ -39,8 +39,12 @@ function register<T extends Processed<AnyModule>>(
validModuleType,
`Found ${module.name} at ${fullPath}, which does not have a valid type`,
);
if (module.type === CommandType.Both || module.type === CommandType.Text) {
module.alias?.forEach(a => manager.set(`${a}_A1`, fullPath));
if (module.type === CommandType.Both) {
module.alias?.forEach(a => manager.set(`${a}_B`, fullPath));
} else {
if(module.type === CommandType.Text){
module.alias?.forEach(a => manager.set(`${a}_T`, fullPath));
}
}
return Result.wrap(() => manager.set(id, fullPath));
}