pkgs/core/src/components.ts (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
import type {
ButtonInteraction,
AnySelectMenuInteraction,
ModalSubmitInteraction,
} from "discord.js";
import type { Option, InferOptions } from "./options";
import { CustomIdTooLong } from "./errors";
export type KittenComponentInteraction =
| ButtonInteraction
| AnySelectMenuInteraction
| ModalSubmitInteraction;
export class KittenComponent<
I extends KittenComponentInteraction = KittenComponentInteraction,
O extends Record<string, Option<any, any>> = Record<string, Option<any, any>>,
> {
type = "component" as const;
constructor(
public name: string,
public config: {
options?: Record<string, Option<any, any>>;
run: (interaction: I, args: any) => Promise<void> | void;
},
) {}
/**
* @throws {CustomIdTooLong} if the generated custom ID exceeds Discord's 100 character limit.
* @returns a custom ID string that encodes the component name and its options' values.
*/
id(
...[args]: {} extends InferOptions<O> ? [args?: InferOptions<O>] : [args: InferOptions<O>]
): string {
const keys = Object.keys(this.config.options || {});
const values = keys.map((key) => encodeURIComponent(String(args?.[key] ?? "")));
const customId = [this.name, ...values].join(":");
if (customId.length > 100) throw new CustomIdTooLong(this.name, customId.length);
return customId;
}
parseArgs(customId: string): Record<string, any> {
const [, ...parts] = customId.split(":");
const keys = Object.keys(this.config.options || {});
const parsed: Record<string, any> = {};
keys.forEach((key, index) => {
const rawVal = parts[index];
if (rawVal === undefined) return;
const val = decodeURIComponent(rawVal);
const opt = this.config.options?.[key];
if (opt?.type === "boolean") parsed[key] = val === "true";
else if (opt?.type === "integer" || opt?.type === "number") parsed[key] = Number(val);
else parsed[key] = val === "" ? undefined : val;
});
return parsed;
}
}
|