From e987c176b72539ca76abe93d3474b13800c26ebb Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Sat, 21 Jun 2025 22:16:50 +0200 Subject: [PATCH] Add autocomplete manager + context menu bugfixes --- src/core/api/modules/client.ts | 96 ++++++++++++++++++++++++----- src/core/startup/manageMigration.ts | 5 +- src/data/framework/codeLoader.ts | 2 + src/index.ts | 6 +- 4 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/core/api/modules/client.ts b/src/core/api/modules/client.ts index 456248a..7425a7d 100644 --- a/src/core/api/modules/client.ts +++ b/src/core/api/modules/client.ts @@ -78,6 +78,8 @@ export class ODClientManager { textCommands: ODTextCommandManager /**The context menu manager is responsible for all context menus & their events inside the bot. */ contextMenus: ODContextMenuManager + /**The autocomplete manager is responsible for all autocomplete events inside the bot. */ + autocompletes: ODAutocompleteManager constructor(debug:ODDebugger){ this.#debug = debug @@ -85,6 +87,7 @@ export class ODClientManager { this.slashCommands = new ODSlashCommandManager(this.#debug,this) this.textCommands = new ODTextCommandManager(this.#debug,this) this.contextMenus = new ODContextMenuManager(this.#debug,this) + this.autocompletes = new ODAutocompleteManager(this.#debug,this) } /**Initiate the `client` variable & add the intents & partials to the bot. */ @@ -152,6 +155,7 @@ export class ODClientManager { if (!this.client.application) throw new ODSystemError("Couldn't get client application for slashCommand & contextMenu managers!") this.slashCommands.commandManager = this.client.application.commands this.contextMenus.commandManager = this.client.application.commands + this.autocompletes.commandManager = this.client.application.commands if (this.readyListener) await this.readyListener() resolve(true) @@ -1920,7 +1924,7 @@ export class ODContextMenuComparator { } /**## ODContextMenuInteractionCallback `type` - * Callback for the message context menu interaction listener. + * Callback for the context menu interaction listener. */ export type ODContextMenuInteractionCallback = (interaction:discord.ContextMenuCommandInteraction,cmd:ODContextMenu) => void @@ -1928,29 +1932,29 @@ export type ODContextMenuInteractionCallback = (interaction:discord.ContextMenuC * The result which will be returned when getting all (un)registered user context menu's from the manager. */ export type ODContextMenuRegisteredResult = { - /**A list of all registered message context menus. */ + /**A list of all registered context menus. */ registered:{ - /**The instance (`ODContextMenu`) from this message context menu. */ + /**The instance (`ODContextMenu`) from this context menu. */ instance:ODContextMenu, - /**The universal object/template/builder of this message context menu. */ + /**The universal object/template/builder of this context menu. */ menu:ODContextMenuUniversalMenu, - /**Does this message context menu require an update? */ + /**Does this context menu require an update? */ requiresUpdate:boolean }[], - /**A list of all unregistered message context menus. */ + /**A list of all unregistered context menus. */ unregistered:{ - /**The instance (`ODContextMenu`) from this message context menu. */ + /**The instance (`ODContextMenu`) from this context menu. */ instance:ODContextMenu, - /**The universal object/template/builder of this message context menu. */ + /**The universal object/template/builder of this context menu. */ menu:null, - /**Does this message context menu require an update? */ + /**Does this context menu require an update? */ requiresUpdate:true }[], - /**A list of all unused message context menus (not found in `ODContextMenuManager`). */ + /**A list of all unused context menus (not found in `ODContextMenuManager`). */ unused:{ - /**The instance (`ODContextMenu`) from this message context menu. */ + /**The instance (`ODContextMenu`) from this context menu. */ instance:null, - /**The universal object/template/builder of this message context menu. */ + /**The universal object/template/builder of this context menu. */ menu:ODContextMenuUniversalMenu, /**Does this context menu require an update? */ requiresUpdate:false @@ -1958,11 +1962,11 @@ export type ODContextMenuRegisteredResult = { } /**## ODContextMenuManager `class` - * This is an Open Ticket client message context menu manager. + * This is an Open Ticket client context menu manager. * - * It's responsible for managing all the message context interactions from the client. + * It's responsible for managing all the context interactions from the client. * - * Here, you can add & remove message context interactions & the bot will do the (de)registering. + * Here, you can add & remove context interactions & the bot will do the (de)registering. */ export class ODContextMenuManager extends ODManager { /**Alias to Open Ticket debugger. */ @@ -2175,4 +2179,66 @@ export class ODContextMenu extends ODManagerData { set name(name:string){ this.builder.name = name } +} + +/**## ODAutocompleteInteractionCallback `type` + * Callback for the autocomplete interaction listener. + */ +export type ODAutocompleteInteractionCallback = (interaction:discord.AutocompleteInteraction) => void + +/**## ODAutocompleteManager `class` + * This is an Open Ticket client autocomplete interaction manager. + * + * It's responsible for managing all the autocomplete interactions from the client. + */ +export class ODAutocompleteManager { + /**Alias to Open Ticket debugger. */ + #debug: ODDebugger + + /**Refrerence to discord.js client. */ + manager: ODClientManager + /**Discord.js application commands manager. */ + commandManager: discord.ApplicationCommandManager|null + /**Collection of all interaction listeners. */ + #interactionListeners: {cmdName:string|RegExp, optName:string|RegExp, callback:ODAutocompleteInteractionCallback}[] = [] + /**Set the soft limit for maximum amount of listeners. A warning will be shown when there are more listeners than this limit. */ + listenerLimit: number = 100 + + constructor(debug:ODDebugger, manager:ODClientManager){ + this.#debug = debug + this.manager = manager + this.commandManager = (manager.client.application) ? manager.client.application.commands : null + } + + /**Start listening to the discord.js client `interactionCreate` event. */ + startListeningToInteractions(){ + this.manager.client.on("interactionCreate",(interaction) => { + //return when not in main server or DM + if (!this.manager.mainServer || (interaction.guild && interaction.guild.id != this.manager.mainServer.id)) return + + if (!interaction.isAutocomplete()) return + this.#interactionListeners.forEach((listener) => { + + if (typeof listener.cmdName == "string" && (interaction.commandName != listener.cmdName)) return + else if (listener.cmdName instanceof RegExp && !listener.cmdName.test(interaction.commandName)) return + if (typeof listener.optName == "string" && (interaction.options.getFocused(true).name != listener.optName)) return + else if (listener.optName instanceof RegExp && !listener.optName.test(interaction.options.getFocused(true).name)) return + + //this is a valid listener + listener.callback(interaction) + }) + }) + } + /**Callback on interaction from one or multiple autocompletes. */ + onInteraction(cmdName:string|RegExp,optName:string|RegExp,callback:ODAutocompleteInteractionCallback){ + this.#interactionListeners.push({ + cmdName,optName,callback + }) + + if (this.#interactionListeners.length > this.listenerLimit){ + this.#debug.console.log("Possible autocomplete interaction memory leak detected!","warning",[ + {key:"listeners",value:this.#interactionListeners.length.toString()} + ]) + } + } } \ No newline at end of file diff --git a/src/core/startup/manageMigration.ts b/src/core/startup/manageMigration.ts index 8aeae49..82499c1 100644 --- a/src/core/startup/manageMigration.ts +++ b/src/core/startup/manageMigration.ts @@ -47,7 +47,10 @@ export const loadVersionMigrationSystem = async () => { if (opendiscord.flags.exists("opendiscord:no-plugins") && opendiscord.flags.get("opendiscord:no-plugins").value) opendiscord.defaults.setDefault("pluginLoading",false) if (opendiscord.flags.exists("opendiscord:soft-plugins") && opendiscord.flags.get("opendiscord:soft-plugins").value) opendiscord.defaults.setDefault("softPluginLoading",true) if (opendiscord.flags.exists("opendiscord:crash") && opendiscord.flags.get("opendiscord:crash").value) opendiscord.defaults.setDefault("crashOnError",true) - if (opendiscord.flags.exists("opendiscord:force-slash-update") && opendiscord.flags.get("opendiscord:force-slash-update").value) opendiscord.defaults.setDefault("forceSlashCommandRegistration",true) + if (opendiscord.flags.exists("opendiscord:force-slash-update") && opendiscord.flags.get("opendiscord:force-slash-update").value){ + opendiscord.defaults.setDefault("forceSlashCommandRegistration",true) + opendiscord.defaults.setDefault("forceContextMenuRegistration",true) + } if (opendiscord.flags.exists("opendiscord:silent") && opendiscord.flags.get("opendiscord:silent").value) opendiscord.console.silent = true diff --git a/src/data/framework/codeLoader.ts b/src/data/framework/codeLoader.ts index 4d7051e..da79601 100644 --- a/src/data/framework/codeLoader.ts +++ b/src/data/framework/codeLoader.ts @@ -62,6 +62,8 @@ export const loadStartListeningInteractionsCode = async () => { opendiscord.code.add(new api.ODCode("opendiscord:start-listening-interactions",13,() => { opendiscord.client.slashCommands.startListeningToInteractions() opendiscord.client.textCommands.startListeningToInteractions() + opendiscord.client.contextMenus.startListeningToInteractions() + opendiscord.client.autocompletes.startListeningToInteractions() })) } diff --git a/src/index.ts b/src/index.ts index dbd42eb..be21530 100644 --- a/src/index.ts +++ b/src/index.ts @@ -384,7 +384,7 @@ const main = async () => { if (!mainServer || !client.checkBotInGuild(mainServer)){ console.log("\n") opendiscord.log("The bot isn't a member of the server provided in the config!","error") - opendiscord.log("Please invite your bot to the server!","info") + opendiscord.log("Please invite your bot to this server!","info") console.log("\n") process.exit(1) } @@ -399,8 +399,8 @@ const main = async () => { if (opendiscord.defaults.getDefault("clientMultiGuildWarning")){ //warn if bot is in multiple servers if (botServers.length > 1){ - opendiscord.log("This bot is part of multiple servers, but Open Ticket doesn't have support for it!","warning") - opendiscord.log("It may result in the bot crashing & glitching when used in these servers!","info") + opendiscord.log("This bot is part of multiple servers, but Open Ticket doesn't provide support for this!","warning") + opendiscord.log("As a result, the bot may crash & glitch when used in the additional servers!","info") } botServers.forEach((server) => { //warn if bot doesn't have permissions in multiple servers