diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/commands/index.ts | 2 | ||||
| -rw-r--r-- | src/commands/ping.ts | 145 | ||||
| -rw-r--r-- | src/index.ts | 43 | ||||
| -rw-r--r-- | src/register.ts | 17 | ||||
| -rw-r--r-- | src/sys/events/client.ts | 173 | ||||
| -rw-r--r-- | src/sys/events/index.ts | 2 | ||||
| -rw-r--r-- | src/sys/events/transport.ts | 332 | ||||
| -rw-r--r-- | src/sys/handler/builder.ts | 26 | ||||
| -rw-r--r-- | src/sys/handler/index.ts | 83 |
9 files changed, 421 insertions, 402 deletions
diff --git a/src/commands/index.ts b/src/commands/index.ts index 366e781..56a879c 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,3 +1,3 @@ -import { ping } from "./ping"; +import {ping} from './ping'; export const commands = [ping]; diff --git a/src/commands/ping.ts b/src/commands/ping.ts index a39e25a..8e03af8 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,86 +1,95 @@ import { - APIApplicationCommandInteraction, - APIInteractionResponse, - ApplicationCommandOptionType, - ApplicationCommandType, - InteractionResponseType, -} from "discord-api-types/v10"; -import { CommandBuilder, HandlerFn } from "../sys/handler"; -import { promise } from "ping"; + type APIApplicationCommandInteraction, + type APIInteractionResponse, + ApplicationCommandOptionType, + ApplicationCommandType, + InteractionResponseType, +} from 'discord-api-types/v10.js'; +import {promise} from 'ping'; +import {CommandBuilder, type HandlerFn} from '../sys/handler'; type Messages = { - latency_message: [number]; - failure_message: [string]; + latencyMessage: [number]; + failureMessage: [string]; }; type Locales = { - [K in keyof Messages]: Partial< - Record< - APIApplicationCommandInteraction["locale"] & "_", - (...args: Messages[K]) => string - > - >; + [K in keyof Messages]: Partial< + Record< + APIApplicationCommandInteraction['locale'] | '_', + (...args: Messages[K]) => string + > + >; }; const locales: Locales = { - latency_message: { - fr: (latency: number) => - `**Temps de complétion du ping:** \`${latency}ms\``, - _: (latency: number) => `**Ping time:** \`${latency}ms\``, - }, - failure_message: { - _: (error: string) => `**The host seems unreachable** \`\`\`${error}\`\`\``, - }, + latencyMessage: { + fr: (latency: number) => + `**Temps de complétion du ping:** \`${latency}ms\``, + _: (latency: number) => `**Ping time:** \`${latency}ms\``, + }, + failureMessage: { + _: (error: string) => `**The host seems unreachable** \`\`\`${error}\`\`\``, + }, }; -const resolve = ( - msg: keyof Messages, - locale: APIApplicationCommandInteraction["locale"], - ...args: Messages[typeof msg] -) => (locales[msg][locale] ?? locales[msg]["_"])(args); +const resolve = <T extends keyof Messages>( + message: T, + locale: APIApplicationCommandInteraction['locale'], + ...args: Messages[T] +) => + ( + (locales[message][locale] ?? locales[message]._) as ( + ...args: Messages[T] + ) => string + )(...args); const handler: HandlerFn = async ( - data: APIApplicationCommandInteraction + data: APIApplicationCommandInteraction, ): Promise<APIInteractionResponse> => { - if ( - data.data.type === ApplicationCommandType.ChatInput && - data.data.options[0].name === "host" && - data.data.options[0].type === ApplicationCommandOptionType.String - ) { - let host = data.data.options[0].value; - let response = await promise.probe(host); + if ( + data.data.type === ApplicationCommandType.ChatInput && + data.data.options[0].name === 'host' && + data.data.options[0].type === ApplicationCommandOptionType.String + ) { + const host = data.data.options[0].value; + const response = await promise.probe(host); - if (response.alive) { - return { - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: resolve("latency_message", data.locale, response.avg), - }, - }; - } else { - let output = response.output; - return { - type: InteractionResponseType.ChannelMessageWithSource, - data: { - content: resolve("failure_message", data.locale, output), - }, - }; - } - } + if (response.alive) { + return { + type: InteractionResponseType.ChannelMessageWithSource, + data: { + content: resolve( + 'latencyMessage', + data.locale, + Number.parseInt(response.avg, 10), + ), + }, + }; + } + + const output = response.output; + return { + type: InteractionResponseType.ChannelMessageWithSource, + data: { + content: resolve('failureMessage', data.locale, output), + }, + }; + } }; export const ping = new CommandBuilder() - .setName("ping") - .handler(handler) - .setDescription("Measure the bot network latency") - .addStringOption((option) => - option - .setName("host") - .setDescription("The host to test pings against") - .setRequired(true) - ) - .setDescriptionLocalizations({ - fr: "Mesure la latence reseau du bot", - }) - .setDMPermission(false) - .build(); + .setName('ping') + .handler(handler) + .setDescription('Measure the bot network latency') + .addStringOption((option) => + option + .setName('host') + .setDescription('The host to test pings against') + .setRequired(true), + ) + .setDescriptionLocalizations({ + fr: 'Mesure la latence reseau du bot', + }) + .setDMPermission(false) + .build(); diff --git a/src/index.ts b/src/index.ts index c95e664..2a9ea9e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,34 +1,35 @@ -import { Client } from "./sys/events"; -import { buildHandler } from "./sys/handler"; -import { commands } from "./commands"; +import 'source-map-support'; +import {Client} from './sys/events'; +import {buildHandler} from './sys/handler'; +import {commands} from './commands'; /** * We instanciate our nova broker client. */ const emitter = new Client({ - transport: { - additionalEvents: [], - nats: { - servers: ["localhost:4222"], - }, - queue: "nova-worker-common", - }, - rest: { - api: "http://localhost:8090/api", - }, + transport: { + additionalEvents: [], + nats: { + servers: ['localhost:4222'], + }, + queue: 'nova-worker-common', + }, + rest: { + api: 'http://localhost:8090/api', + }, }); // We register our slash command handler. -emitter.on("interactionCreate", buildHandler(commands)); +emitter.on('interactionCreate', buildHandler(commands)); // Simple message handler -emitter.on("messageCreate", (message) => { - if (message.content === "~ping") { - message.client.channels.createMessage(message.channel_id, { - content: `Bonjour! <@${message.author.id}>`, - }); - } +emitter.on('messageCreate', async (message) => { + if (message.content === '~ping') { + await message.client.channels.createMessage(message.channel_id, { + content: `Bonjour! <@${message.author.id}>`, + }); + } }); // We connect ourselves to the nova nats broker. -emitter.start().catch(console.log); +(async () => emitter.start())(); diff --git a/src/register.ts b/src/register.ts index 92ce36a..303f4d8 100644 --- a/src/register.ts +++ b/src/register.ts @@ -1,15 +1,14 @@ -import { REST } from "@discordjs/rest"; -import { commands } from "./commands"; -import { registerCommands } from "./sys/handler"; +import 'source-map-support'; +import {REST} from '@discordjs/rest'; +import {commands} from './commands'; +import {registerCommands} from './sys/handler'; const rest = new REST({ - version: "10", - headers: { Authorization: "" }, - api: "http://localhost:8090/api", - }).setToken("_"); + version: '10', + api: 'http://localhost:8090/api', +}).setToken('_'); - /** * We register the commands with discord */ -registerCommands(commands, rest, "807188335717384212");
\ No newline at end of file +(async () => registerCommands(commands, rest, '807188335717384212'))(); diff --git a/src/sys/events/client.ts b/src/sys/events/client.ts index 90d7447..73fe34f 100644 --- a/src/sys/events/client.ts +++ b/src/sys/events/client.ts @@ -1,17 +1,15 @@ +import {EventEmitter} from 'node:stream'; +import {type CamelCase, type PascalCase} from 'type-fest'; +import {REST, type RESTOptions} from '@discordjs/rest'; import { - APIApplicationCommandPermissionsConstant, - APIInteractionResponse, - APIInteractionResponseCallbackData, - GatewayDispatchPayload, - GatewayInteractionCreateDispatchData, -} from "discord-api-types/v10"; -import { Transport, TransportOptions } from "./transport"; -import { CamelCase, PascalCase } from "type-fest"; -import { REST, RESTOptions } from "@discordjs/rest"; -import { EventEmitter } from "stream"; -import TypedEmitter from "typed-emitter"; - -import { API } from "@discordjs/core"; + type APIInteractionResponse, + type APIInteractionResponseCallbackData, + type GatewayDispatchPayload, + type GatewayInteractionCreateDispatchData, +} from 'discord-api-types/v10.js'; +import type TypedEmitter from 'typed-emitter'; +import {API} from '@discordjs/core'; +import {Transport, type TransportOptions} from './transport'; /** * Maps an event name (O['t']) and a Union O and extracts alla the union members that have a matching O['t'] @@ -24,27 +22,31 @@ import { API } from "@discordjs/core"; * let variant2: ExtractVariant<ExampleUnion, 'type2'>; // Type of variant2 is Variant2 * */ -type ExtractVariant<O extends { t: string }, U extends O["t"]> = Extract< - O & { t: Exclude<O["t"], Exclude<O["t"], U>> }, - { t: U } +type ExtractVariant<O extends {t: string}, U extends O['t']> = Extract< + O & {t: Exclude<O['t'], Exclude<O['t'], U>>}, + {t: U} >; /** * Add intrisics properties to the event, such as `client` and `rest` */ -export type WithIntrisics<T> = T & { client: Client }; +export type WithIntrisics<T> = T & {client: Client}; + +/** + * CamelCased event name + */ export type EventName = keyof EventsHandlerArguments; /** * Reprends a handler function with one argument */ export type HandlerFunction<Arg extends unknown[]> = ( - ...args: Arg -) => unknown | Promise<unknown>; + ...args: Arg +) => PromiseLike<void>; export type EventTypes = { - [P in GatewayDispatchPayload["t"]]: WithIntrisics< - ExtractVariant<GatewayDispatchPayload, P>["d"] - >; + [P in GatewayDispatchPayload['t']]: WithIntrisics< + ExtractVariant<GatewayDispatchPayload, P>['d'] + >; }; /** @@ -52,16 +54,16 @@ export type EventTypes = { * Also reteives the type of the event using ExtractEvent */ export type EventsHandlerArguments = { - [P in keyof EventTypes as `${CamelCase<P>}`]: HandlerFunction< - [EventTypes[P]] - >; + [P in keyof EventTypes as `${CamelCase<P>}`]: HandlerFunction< + [EventTypes[P]] + >; } & { - interactionCreate: HandlerFunction< - [ - WithIntrisics<GatewayInteractionCreateDispatchData>, - (interactionCreate: APIInteractionResponseCallbackData) => void - ] - >; + interactionCreate: HandlerFunction< + [ + WithIntrisics<GatewayInteractionCreateDispatchData>, + (interactionCreate: APIInteractionResponse) => void, + ] + >; }; /** @@ -69,74 +71,77 @@ export type EventsHandlerArguments = { * This is implemented by a Proxy */ export type EventsFunctions = { - [P in keyof EventsHandlerArguments as P extends string - ? `on${PascalCase<P>}` - : never]: (fn: EventsHandlerArguments[P]) => Client; + [P in keyof EventsHandlerArguments as P extends string + ? `on${PascalCase<P>}` + : never]: (fn: EventsHandlerArguments[P]) => Client; }; /** * Defines all the methods known to be implemented */ -interface ClientFunctions - extends EventsFunctions, - TypedEmitter<EventsHandlerArguments>, - API {} +type ClientFunctions = Record<string, unknown> & + EventsFunctions & + TypedEmitter<EventsHandlerArguments> & + API; /** * The real extended class is an EventEmitter. */ -const UndefinedClient: { new (): ClientFunctions } = EventEmitter as any; +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const undefinedClient: new () => ClientFunctions = EventEmitter as any; /** - * nova.js client + * Nova.js client * * Used to interact with nova, emits events from nova * Example: * client.on('messageCreate', (message) => { console.log('Message received', message.content) }); * client.on('interactionCreate', (message) => { }); */ -export class Client extends UndefinedClient { - private readonly transport: Transport; - private readonly api: API; - public readonly rest: REST; - - constructor(options: { - rest?: Partial<RESTOptions>; - transport: TransportOptions; - }) { - super(); - this.rest = new REST(options.rest); - this.transport = new Transport(this, options.transport); - this.api = new API(this.rest); - - // This is safe because this event is emitted by the EventEmitter itself. - this.on("newListener" as any, (event: EventName) => { - this.transport.subscribe(event); - }); - - // Using a proxy to provide the 'on...' functionality - return new Proxy(this, { - get(self, symbol) { - let name = symbol.toString(); - if (name.startsWith("on") && name.length > 2) { - // Get the event name - let eventName = [name[2].toLowerCase(), name.slice(3)].join( - "" - ) as EventName; - return (fn: EventsHandlerArguments[typeof eventName]) => - self.on(eventName, fn); - } - - if (self.api[symbol] && self[symbol]) { - return self.api[symbol]; - } - - return self[symbol]; - }, - }); - } - - public start() { - return this.transport.start(); - } +export class Client extends undefinedClient { + public readonly rest: REST; + + private readonly transport: Transport; + private readonly api: API; + + constructor(options: { + rest?: Partial<RESTOptions>; + transport: TransportOptions; + }) { + super(); + this.rest = new REST(options.rest); + this.transport = new Transport(this, options.transport); + this.api = new API(this.rest); + + // This is safe because this event is emitted by the EventEmitter itself. + this.on('newListener' as any, async (event: EventName) => { + await this.transport.subscribe(event); + }); + + // Using a proxy to provide the 'on...' functionality + return new Proxy(this, { + get(self, symbol: keyof typeof Client) { + const name = symbol.toString(); + if (name.startsWith('on') && name.length > 2) { + // Get the event name + const eventName = [name[2].toLowerCase(), name.slice(3)].join( + '', + ) as EventName; + return (fn: EventsHandlerArguments[typeof eventName]) => + self.on(eventName, fn); + } + + if (self.api[symbol] && self[symbol as string]) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return self.api[symbol]; + } + + return self[symbol as string]; + }, + }); + } + + public async start() { + return this.transport.start(); + } } diff --git a/src/sys/events/index.ts b/src/sys/events/index.ts index 5ec7692..4f1cce4 100644 --- a/src/sys/events/index.ts +++ b/src/sys/events/index.ts @@ -1 +1 @@ -export * from "./client"; +export * from './client'; diff --git a/src/sys/events/transport.ts b/src/sys/events/transport.ts index 28be5cf..1a2e86a 100644 --- a/src/sys/events/transport.ts +++ b/src/sys/events/transport.ts @@ -1,175 +1,185 @@ -import { connect, ConnectionOptions, NatsConnection, Subscription } from "nats"; +import {Buffer} from 'node:buffer'; import { - Client, - EventName, - EventTypes, - EventsHandlerArguments, - WithIntrisics, -} from "."; -import globRegex from "glob-regex"; + connect, + type ConnectionOptions, + type NatsConnection, + type Subscription, +} from 'nats'; +import globRegex from 'glob-regex'; import { - APIInteraction, - APIInteractionResponse, - APIInteractionResponseCallbackData, - GatewayDispatchPayload, - GatewayInteractionCreateDispatch, - Routes, -} from "discord-api-types/v10"; -import { CamelCase } from "type-fest"; + type APIInteractionResponse, + InteractionResponseType, + type APIInteractionResponseCallbackData, + type GatewayDispatchPayload, + Routes, +} from 'discord-api-types/v10.js'; +import {type CamelCase} from 'type-fest'; +import {type Client, type EventName, type EventsHandlerArguments} from '.'; /** * Options for the nats transport layer */ export type TransportOptions = { - additionalEvents?: (keyof EventsHandlerArguments)[]; - nats?: ConnectionOptions; - queue: string; + additionalEvents?: Array<keyof EventsHandlerArguments>; + nats?: ConnectionOptions; + queue: string; }; /** * Transport implements all the communication to Nova using Nats */ export class Transport { - // Nats connection - private nats: NatsConnection | null = null; - // Current subscriptions - private subscriptions: Map<string, Subscription> = new Map(); - // Current subscribed events - private events: Set<EventName> = new Set(); - - // Creats a new Transport instance. - constructor( - private emitter: Client, - private config: Partial<TransportOptions> - ) {} - - /** - * Starts a new nats client. - */ - public async start() { - this.nats = await connect(this.config?.nats); - - for (let eventName of this.events) { - await this.subscribe(eventName); - } - if (this.config.additionalEvents) { - for (let eventName of this.config.additionalEvents) { - await this.subscribe(eventName); - } - } - } - - /** - * Subscribe to a new topic - * @param event Event to subscribe to - * @returns - */ - public async subscribe(event: EventName) { - // If nats is not connected, we simply request to subscribe to it at startup - if (!this.nats) { - console.log("Requesting event " + event); - this.events.add(event); - return; - } - - // Since the event names used by this library are camelCase'd we need to - // re-transform it to the UPPER_CASE used by nova. - let dashed = event.replace(/[A-Z]/g, (m) => "_" + m.toLowerCase()); - // Construct the topic name used by nova. - // This **is going to change** as we implement the caching component. - let topic = `nova.cache.dispatch.${dashed.toUpperCase()}`; - - // To avoid having multiple subscriptions covering this event - // we check if each of our subscriptions covers this scope. - let isAlreadyPresent = [...this.subscriptions.keys()].reduce( - (previous, current) => { - if (previous) return previous; - let regex = globRegex(current); - - return regex.test(topic); - }, - false - ); - - // We abord the subscriptions if it's already covered. - if (isAlreadyPresent) { - console.warn("nats subscription already covered."); - return; - } - - // We remove all the subscriptions that are covered by out current subsciptions. - let regex = globRegex(topic); - [...this.subscriptions.keys()].map((key) => { - if (regex.test(key)) { - let subsciption = this.subscriptions.get(key); - if (!subsciption) { - return; - } - - console.log(`unsubscribing from ${key}`); - subsciption.unsubscribe(); - } - }); - - this._subscriptionTask(topic); - } - - // Task that monitors the subscription - // It also listens for a subscription end. - private async _subscriptionTask(topic: string) { - if (!this.nats) { - throw new Error("nats connection is not started"); - } - - console.log(`subscribing to ${topic}`); - // Create the nats subscription - let subscription = this.nats.subscribe(topic, { queue: this.config.queue }); - this.subscriptions.set(topic, subscription); - // Handle each event in the subscription stream. - for await (let publish of subscription) { - try { - // Decode the payload - let event: GatewayDispatchPayload = JSON.parse( - Buffer.from(publish.data).toString("utf-8") - ); - // Transform the event name to a camclCased name - const camelCasedName = event.t - .toLowerCase() - .replace(/_([a-z])/g, function (g) { - return g[1].toUpperCase(); - }) as CamelCase<typeof event.t>; - - // Since an interaction need a reponse, - // we need to handle the case where nova is not configured - // with a webhook endpoint, hence we need to use a post request - // against webhook execute endpoint with the interaction data. - if (event.t === "INTERACTION_CREATE") { - let interaction = event.d; - let respond = async (respond: APIInteractionResponseCallbackData) => { - if (publish.reply) { - publish.respond(Buffer.from(JSON.stringify(respond), "utf-8")); - } else { - await this.emitter.interactions.reply( - interaction.id, - interaction.token, - respond - ); - } - }; - // Emit the - this.emitter.emit( - camelCasedName, - { ...event.d, client: this.emitter }, - respond - ); - } else { - // Typescript refuses to infer this, whyyy - this.emitter.emit(camelCasedName, { - ...event.d, - client: this.emitter, - } as any); - } - } catch (e) {} - } - } + // Nats connection + private nats: NatsConnection | undefined = null; + // Current subscriptions + private readonly subscriptions = new Map<string, Subscription>(); + // Current subscribed events + private readonly events = new Set<EventName>(); + + // Creats a new Transport instance. + constructor( + private readonly emitter: Client, + private readonly config: Partial<TransportOptions>, + ) {} + + /** + * Starts a new nats client. + */ + public async start() { + this.nats = await connect(this.config?.nats); + + await Promise.all( + [...this.events].map(async (eventName) => this.subscribe(eventName)), + ); + + if (this.config.additionalEvents) { + await Promise.all( + this.config.additionalEvents.map(async (eventName) => + this.subscribe(eventName), + ), + ); + } + } + + /** + * Subscribe to a new topic + * @param event Event to subscribe to + * @returns + */ + public async subscribe(event: EventName) { + // If nats is not connected, we simply request to subscribe to it at startup + if (!this.nats) { + console.log('Requesting event ' + event); + this.events.add(event); + return; + } + + // Since the event names used by this library are camelCase'd we need to + // re-transform it to the UPPER_CASE used by nova. + const dashed = event.replace(/[A-Z]/g, (m) => '_' + m.toLowerCase()); + // Construct the topic name used by nova. + // This **is going to change** as we implement the caching component. + const topic = `nova.cache.dispatch.${dashed.toUpperCase()}`; + + // To avoid having multiple subscriptions covering this event + // we check if each of our subscriptions covers this scope. + const isAlreadyPresent = [...this.subscriptions.keys()].reduce( + (previous, current) => { + if (previous) { + return previous; + } + + const regex = globRegex(current); + + return regex.test(topic); + }, + false, + ); + + // We abord the subscriptions if it's already covered. + if (isAlreadyPresent) { + console.warn('nats subscription already covered.'); + return; + } + + // We remove all the subscriptions that are covered by out current subsciptions. + const regex = globRegex(topic); + for (const key of this.subscriptions.keys()) { + if (regex.test(key)) { + const subsciption = this.subscriptions.get(key); + if (!subsciption) { + continue; + } + + console.log(`unsubscribing from ${key}`); + subsciption.unsubscribe(); + } + } + + void this._subscriptionTask(topic); + } + + // Task that monitors the subscription + // It also listens for a subscription end. + private async _subscriptionTask(topic: string) { + if (!this.nats) { + throw new Error('nats connection is not started'); + } + + console.log(`subscribing to ${topic}`); + // Create the nats subscription + const subscription = this.nats.subscribe(topic, { + queue: this.config.queue, + }); + this.subscriptions.set(topic, subscription); + // Handle each event in the subscription stream. + for await (const publish of subscription) { + try { + // Decode the payload + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const event: GatewayDispatchPayload = JSON.parse( + Buffer.from(publish.data).toString('utf8'), + ); + // Transform the event name to a camclCased name + const camelCasedName = event.t + .toLowerCase() + .replace(/_([a-z])/g, (g) => g[1].toUpperCase()) as CamelCase< + typeof event.t + >; + + // Since an interaction need a reponse, + // we need to handle the case where nova is not configured + // with a webhook endpoint, hence we need to use a post request + // against webhook execute endpoint with the interaction data. + if (event.t === 'INTERACTION_CREATE') { + const interaction = event.d; + const respond = async (respond: APIInteractionResponse) => { + if (publish.reply) { + publish.respond(Buffer.from(JSON.stringify(respond), 'utf8')); + } else { + await this.emitter.rest.post( + Routes.interactionCallback(interaction.id, interaction.token), + { + body: respond, + }, + ); + } + }; + + // Emit the + this.emitter.emit( + camelCasedName, + {...event.d, client: this.emitter}, + respond, + ); + } else { + // Typescript refuses to infer this, whyyy + this.emitter.emit(camelCasedName, { + ...event.d, + client: this.emitter, + } as any); + } + } catch {} + } + } } diff --git a/src/sys/handler/builder.ts b/src/sys/handler/builder.ts index feb5780..71732a8 100644 --- a/src/sys/handler/builder.ts +++ b/src/sys/handler/builder.ts @@ -1,22 +1,18 @@ -import { SlashCommandBuilder } from "@discordjs/builders"; -import { Command, HandlerFn } from "."; +import {SlashCommandBuilder} from '@discordjs/builders'; +import {type Command, type HandlerFn} from '.'; /** - * Simple wrapper around the SlashCommandBuilder provided by Discord.js + * Simple wrapper around the SlashCommandBuilder provided by Discord.js */ export class CommandBuilder extends SlashCommandBuilder { - private _handler: HandlerFn; + private _handler: HandlerFn; - constructor() { - super(); - } + handler(handler: HandlerFn): this { + this._handler = handler; + return this; + } - handler(handler: HandlerFn): this { - this._handler = handler; - return this; - } - - build(): Command { - return { json: this.toJSON(), handler: this._handler }; - } + build(): Command { + return {json: this.toJSON(), handler: this._handler}; + } } diff --git a/src/sys/handler/index.ts b/src/sys/handler/index.ts index c9a5343..2d5e232 100644 --- a/src/sys/handler/index.ts +++ b/src/sys/handler/index.ts @@ -1,28 +1,27 @@ -import { REST } from "@discordjs/rest"; +import {type REST} from '@discordjs/rest'; import { - APIApplicationCommandInteraction, - APIInteraction, - APIInteractionResponse, - APIInteractionResponseCallbackData, - InteractionType, - RESTPostAPIApplicationCommandsJSONBody, - RESTPostAPIChatInputApplicationCommandsJSONBody, - Routes, -} from "discord-api-types/v10"; + type APIApplicationCommandInteraction, + type APIInteraction, + InteractionType, + type RESTPostAPIApplicationCommandsJSONBody, + type RESTPostAPIChatInputApplicationCommandsJSONBody, + Routes, + type APIInteractionResponse, +} from 'discord-api-types/v10.js'; -export * from "./builder"; +export * from './builder'; export type PromiseLike<T> = T | Promise<T>; /** * A simple function that executes a slash command. */ export type HandlerFn = ( - data: APIApplicationCommandInteraction -) => PromiseLike<APIInteractionResponseCallbackData>; + data: APIApplicationCommandInteraction, +) => PromiseLike<APIInteractionResponse>; export type Command = { - json: RESTPostAPIChatInputApplicationCommandsJSONBody; - handler: HandlerFn; + json: RESTPostAPIChatInputApplicationCommandsJSONBody; + handler: HandlerFn; }; /** @@ -32,15 +31,15 @@ export type Command = { * @param applicationId Current application id */ export const registerCommands = async ( - commands: Iterable<Command>, - rest: REST, - applicationId: string + commands: Iterable<Command>, + rest: REST, + appId: string, ) => { - await rest.post(Routes.applicationCommands(applicationId), { - body: [...commands].map( - (x) => x.json - ) as RESTPostAPIApplicationCommandsJSONBody[], - }); + await rest.post(Routes.applicationCommands(appId), { + body: [...commands].map( + (x) => x.json, + ) as RESTPostAPIApplicationCommandsJSONBody[], + }); }; /** @@ -49,26 +48,26 @@ export const registerCommands = async ( * @returns Handler function */ export const buildHandler = (commands: Iterable<Command>) => { - let internal: Map<String, Command> = new Map(); - for (const command of commands) { - internal.set(command.json.name, command); - } + const internal = new Map<string, Command>(); + for (const command of commands) { + internal.set(command.json.name, command); + } - return async ( - event: APIInteraction, - reply?: (data: APIInteractionResponseCallbackData) => void - ) => { - console.log("executing: ", event.data); - if (event.type === InteractionType.ApplicationCommand) { - console.log("executing: ", event.data); - let command = internal.get(event.data.name); + return async ( + event: APIInteraction, + reply?: (data: APIInteractionResponse) => void, + ) => { + console.log('executing:', event.data); + if (event.type === InteractionType.ApplicationCommand) { + console.log('executing:', event.data); + const command = internal.get(event.data.name); - if (command) { - let data = await command.handler(event); - console.log("sending reply", data); + if (command) { + const data = await command.handler(event); + console.log('sending reply', data); - reply(data); - } - } - }; + reply(data); + } + } + }; }; |
