Add commandManager and the first slash command
the command allows for summining the bot without sending an actual mention message that might hang in the chat log sent to openAi, consuming tokens
This commit is contained in:
parent
56a0e686b0
commit
8b4b35454b
6 changed files with 124 additions and 2 deletions
21
package-lock.json
generated
21
package-lock.json
generated
|
@ -13,10 +13,12 @@
|
||||||
"discord.js": "^14.8.0",
|
"discord.js": "^14.8.0",
|
||||||
"fold-to-ascii": "^5.0.1",
|
"fold-to-ascii": "^5.0.1",
|
||||||
"gpt-3-encoder": "^1.1.4",
|
"gpt-3-encoder": "^1.1.4",
|
||||||
"openai": "^3.2.1"
|
"openai": "^3.2.1",
|
||||||
|
"require-directory": "^2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/fold-to-ascii": "^5.0.0",
|
"@types/fold-to-ascii": "^5.0.0",
|
||||||
|
"@types/require-directory": "^2.1.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||||
"@typescript-eslint/parser": "^5.55.0",
|
"@typescript-eslint/parser": "^5.55.0",
|
||||||
"eslint": "^8.36.0",
|
"eslint": "^8.36.0",
|
||||||
|
@ -295,6 +297,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.2.tgz",
|
||||||
"integrity": "sha512-sDPHm2wfx2QhrMDK0pOt2J4KLJMAcerqWNvnED0itPRJWvI+bK+uNHzcH1dFsBlf7G3u8tqXmRF3wkvL9yUwMw=="
|
"integrity": "sha512-sDPHm2wfx2QhrMDK0pOt2J4KLJMAcerqWNvnED0itPRJWvI+bK+uNHzcH1dFsBlf7G3u8tqXmRF3wkvL9yUwMw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/require-directory": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/require-directory/-/require-directory-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-FUG5PJ2rsV2TssSspVZefTR8+wH3Ahr6KdAB6WanLNroSDu0A5ew4WVUxnnGU/E1k6nzip9ZawGAAe/ZqzGn5g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/semver": {
|
"node_modules/@types/semver": {
|
||||||
"version": "7.3.13",
|
"version": "7.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz",
|
||||||
|
@ -1710,6 +1721,14 @@
|
||||||
"url": "https://github.com/sponsors/Borewit"
|
"url": "https://github.com/sponsors/Borewit"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
|
|
|
@ -14,10 +14,12 @@
|
||||||
"discord.js": "^14.8.0",
|
"discord.js": "^14.8.0",
|
||||||
"fold-to-ascii": "^5.0.1",
|
"fold-to-ascii": "^5.0.1",
|
||||||
"gpt-3-encoder": "^1.1.4",
|
"gpt-3-encoder": "^1.1.4",
|
||||||
"openai": "^3.2.1"
|
"openai": "^3.2.1",
|
||||||
|
"require-directory": "^2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/fold-to-ascii": "^5.0.0",
|
"@types/fold-to-ascii": "^5.0.0",
|
||||||
|
"@types/require-directory": "^2.1.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||||
"@typescript-eslint/parser": "^5.55.0",
|
"@typescript-eslint/parser": "^5.55.0",
|
||||||
"eslint": "^8.36.0",
|
"eslint": "^8.36.0",
|
||||||
|
|
41
src/command.ts
Normal file
41
src/command.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { PermissionsBitField } from "discord.js";
|
||||||
|
import { RESTPostAPIApplicationCommandsJSONBody } from "discord.js";
|
||||||
|
import { ApplicationCommandOption, ApplicationCommandType, ChatInputCommandInteraction, LocalizationMap, MessageInteraction, PermissionResolvable, UserSelectMenuInteraction } from "discord.js";
|
||||||
|
|
||||||
|
type InteractionTypeMap = {
|
||||||
|
[ApplicationCommandType.ChatInput]: [ChatInputCommandInteraction, string];
|
||||||
|
[ApplicationCommandType.Message]: [MessageInteraction, never];
|
||||||
|
[ApplicationCommandType.User]: [UserSelectMenuInteraction, never];
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Command<Type extends keyof InteractionTypeMap = ApplicationCommandType> {
|
||||||
|
readonly name: string;
|
||||||
|
readonly name_localizations?: LocalizationMap;
|
||||||
|
readonly description: InteractionTypeMap[Type][1];
|
||||||
|
readonly description_localizations?: LocalizationMap;
|
||||||
|
readonly options?: ApplicationCommandOption[];
|
||||||
|
readonly default_member_permissions?: PermissionResolvable;
|
||||||
|
readonly type: Type;
|
||||||
|
readonly nsfw?: boolean;
|
||||||
|
readonly dm_permission?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Command<Type extends keyof InteractionTypeMap = ApplicationCommandType> {
|
||||||
|
abstract execute(interaction: InteractionTypeMap[Type][0]): Promise<void>;
|
||||||
|
|
||||||
|
toRESTPostApplicationCommands(): RESTPostAPIApplicationCommandsJSONBody {
|
||||||
|
return {
|
||||||
|
name: this.name,
|
||||||
|
name_localizations: this.name_localizations,
|
||||||
|
description: this.description,
|
||||||
|
description_localizations: this.description_localizations,
|
||||||
|
options: this.options,
|
||||||
|
default_member_permissions: this.default_member_permissions !== undefined ? new PermissionsBitField(this.default_member_permissions).bitfield.toString() : undefined,
|
||||||
|
type: this.type,
|
||||||
|
nsfw: this.nsfw,
|
||||||
|
dm_permission: this.dm_permission,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Command;
|
16
src/commands/summon.ts
Normal file
16
src/commands/summon.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { ApplicationCommandType, ChatInputCommandInteraction } from "discord.js";
|
||||||
|
|
||||||
|
import Command from "../command";
|
||||||
|
import { queueRequest } from "../execution";
|
||||||
|
|
||||||
|
|
||||||
|
export default class Summon extends Command {
|
||||||
|
name = "summon";
|
||||||
|
description = "Summons a bot to reply in chat without sending any message";
|
||||||
|
type = ApplicationCommandType.ChatInput;
|
||||||
|
dm_permission = false;
|
||||||
|
|
||||||
|
async execute(interaction: ChatInputCommandInteraction) {
|
||||||
|
queueRequest(interaction);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
import config from "./config";
|
import config from "./config";
|
||||||
import { queueRequest } from "./execution";
|
import { queueRequest } from "./execution";
|
||||||
|
import InteractionManager from "./interactionManager";
|
||||||
|
|
||||||
const discord = new DiscordApi.Client({
|
const discord = new DiscordApi.Client({
|
||||||
intents: [
|
intents: [
|
||||||
|
@ -19,6 +20,9 @@ export const openai = new OpenAIApi(new OpenAIApiConfiguration({
|
||||||
|
|
||||||
export const database = new PrismaClient();
|
export const database = new PrismaClient();
|
||||||
|
|
||||||
|
const interactionManager = new InteractionManager();
|
||||||
|
interactionManager.bindClient(discord);
|
||||||
|
|
||||||
discord.on("ready", async event => {
|
discord.on("ready", async event => {
|
||||||
console.log(`Connected to Discord as ${event.user.tag} (${event.user.id})`);
|
console.log(`Connected to Discord as ${event.user.tag} (${event.user.id})`);
|
||||||
});
|
});
|
||||||
|
|
40
src/interactionManager.ts
Normal file
40
src/interactionManager.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { Interaction, Client as DiscordClient } from "discord.js";
|
||||||
|
import requireDirectory from "require-directory";
|
||||||
|
|
||||||
|
import Command from "./command";
|
||||||
|
|
||||||
|
export default class CommandManager {
|
||||||
|
readonly commands: Command[] = [];
|
||||||
|
|
||||||
|
constructor(directory = "./commands") {
|
||||||
|
const files = requireDirectory(module, directory);
|
||||||
|
for (const i in files ) {
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
this.commands.push(new (files[i].default as Command)());
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(`Failed to construct command ${i} (${typeof e}):`);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInteraction(interaction: Interaction) {
|
||||||
|
if (
|
||||||
|
interaction.isChatInputCommand() ||
|
||||||
|
interaction.isMessageContextMenuCommand() ||
|
||||||
|
interaction.isUserContextMenuCommand()
|
||||||
|
) {
|
||||||
|
const foundCommand = this.commands.find((command) => command.name == interaction.commandName );
|
||||||
|
if (!foundCommand) throw new Error(`Unknown command received (${interaction.commandName}). Did you forgot to push updated commands?`);
|
||||||
|
foundCommand.execute(interaction);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindClient(client: DiscordClient) {
|
||||||
|
client.on("interactionCreate", (e) => this.onInteraction(e));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue