Finished autocomplete & context menu interactions
Autocomplete & context menu interactions have been fully implemented in the Open Discord framework. They have not yet been implemented in the Open Ticket bot code though.
This commit is contained in:
@@ -160,7 +160,7 @@ export class ODTextCommandManager_Default extends ODTextCommandManager {
|
|||||||
* It's used to generate typescript declarations for this class.
|
* It's used to generate typescript declarations for this class.
|
||||||
*/
|
*/
|
||||||
export interface ODContextMenuManagerIds_Default {
|
export interface ODContextMenuManagerIds_Default {
|
||||||
"opendiscord:test-menu":ODContextMenu
|
//"opendiscord:test-menu":ODContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
/**## ODContextMenuManager_Default `default_class`
|
/**## ODContextMenuManager_Default `default_class`
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { ODLanguageManager_Default } from "./language"
|
|||||||
import { ODCheckerFunctionManager_Default, ODCheckerManager_Default, ODCheckerRenderer_Default, ODCheckerTranslationRegister_Default } from "./checker"
|
import { ODCheckerFunctionManager_Default, ODCheckerManager_Default, ODCheckerRenderer_Default, ODCheckerTranslationRegister_Default } from "./checker"
|
||||||
import { ODClientManager_Default, ODContextMenuManager_Default, ODSlashCommandManager_Default, ODTextCommandManager_Default } from "./client"
|
import { ODClientManager_Default, ODContextMenuManager_Default, ODSlashCommandManager_Default, ODTextCommandManager_Default } from "./client"
|
||||||
import { ODBuilderManager_Default, ODButtonManager_Default, ODDropdownManager_Default, ODEmbedManager_Default, ODFileManager_Default, ODMessageManager_Default, ODModalManager_Default } from "./builder"
|
import { ODBuilderManager_Default, ODButtonManager_Default, ODDropdownManager_Default, ODEmbedManager_Default, ODFileManager_Default, ODMessageManager_Default, ODModalManager_Default } from "./builder"
|
||||||
import { ODButtonResponderManager_Default, ODCommandResponderManager_Default, ODDropdownResponderManager_Default, ODModalResponderManager_Default, ODResponderManager_Default } from "./responder"
|
import { ODAutocompleteResponderManager_Default, ODButtonResponderManager_Default, ODCommandResponderManager_Default, ODContextMenuResponderManager_Default, ODDropdownResponderManager_Default, ODModalResponderManager_Default, ODResponderManager_Default } from "./responder"
|
||||||
import { ODActionManager_Default } from "./action"
|
import { ODActionManager_Default } from "./action"
|
||||||
import { ODPermissionManager_Default } from "./permission"
|
import { ODPermissionManager_Default } from "./permission"
|
||||||
import { ODHelpMenuManager_Default } from "./helpmenu"
|
import { ODHelpMenuManager_Default } from "./helpmenu"
|
||||||
@@ -255,6 +255,10 @@ export interface ODEventIds_Default {
|
|||||||
"afterDropdownRespondersLoaded": ODEvent_Default<(dropdowns:ODDropdownResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
"afterDropdownRespondersLoaded": ODEvent_Default<(dropdowns:ODDropdownResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
"onModalResponderLoad": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
"onModalResponderLoad": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
"afterModalRespondersLoaded": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
"afterModalRespondersLoaded": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
|
"onContextMenuResponderLoad": ODEvent_Default<(menus:ODContextMenuResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
|
"afterContextMenuRespondersLoaded": ODEvent_Default<(menus:ODContextMenuResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
|
"onAutocompleteResponderLoad": ODEvent_Default<(autocomplete:ODAutocompleteResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
|
"afterAutocompleteRespondersLoaded": ODEvent_Default<(autocomplete:ODAutocompleteResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
|
||||||
|
|
||||||
//plugin loading before finalizations
|
//plugin loading before finalizations
|
||||||
"onPluginBeforeFinalizationLoad": ODEvent_Default<() => ODPromiseVoid>,
|
"onPluginBeforeFinalizationLoad": ODEvent_Default<() => ODPromiseVoid>,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//DEFAULT RESPONDER MODULE
|
//DEFAULT RESPONDER MODULE
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
import { ODValidId } from "../modules/base"
|
import { ODValidId } from "../modules/base"
|
||||||
import { ODButtonResponder, ODButtonResponderInstance, ODButtonResponderManager, ODCommandResponder, ODCommandResponderInstance, ODCommandResponderManager, ODDropdownResponder, ODDropdownResponderInstance, ODDropdownResponderManager, ODModalResponder, ODModalResponderInstance, ODModalResponderManager, ODResponderManager } from "../modules/responder"
|
import { ODAutocompleteResponder, ODAutocompleteResponderInstance, ODAutocompleteResponderManager, ODButtonResponder, ODButtonResponderInstance, ODButtonResponderManager, ODCommandResponder, ODCommandResponderInstance, ODCommandResponderManager, ODContextMenuResponder, ODContextMenuResponderInstance, ODContextMenuResponderManager, ODDropdownResponder, ODDropdownResponderInstance, ODDropdownResponderManager, ODModalResponder, ODModalResponderInstance, ODModalResponderManager, ODResponderManager } from "../modules/responder"
|
||||||
import { ODWorkerManager_Default } from "./worker"
|
import { ODWorkerManager_Default } from "./worker"
|
||||||
|
|
||||||
/**## ODResponderManager_Default `default_class`
|
/**## ODResponderManager_Default `default_class`
|
||||||
@@ -16,6 +16,8 @@ export class ODResponderManager_Default extends ODResponderManager {
|
|||||||
declare buttons: ODButtonResponderManager_Default
|
declare buttons: ODButtonResponderManager_Default
|
||||||
declare dropdowns: ODDropdownResponderManager_Default
|
declare dropdowns: ODDropdownResponderManager_Default
|
||||||
declare modals: ODModalResponderManager_Default
|
declare modals: ODModalResponderManager_Default
|
||||||
|
declare contextMenus: ODContextMenuResponderManager_Default
|
||||||
|
declare autocomplete: ODAutocompleteResponderManager_Default
|
||||||
}
|
}
|
||||||
|
|
||||||
/**## ODCommandResponderManagerIds_Default `interface`
|
/**## ODCommandResponderManagerIds_Default `interface`
|
||||||
@@ -253,3 +255,97 @@ export class ODModalResponderManager_Default extends ODModalResponderManager {
|
|||||||
export class ODModalResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODModalResponder<Source,Params> {
|
export class ODModalResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODModalResponder<Source,Params> {
|
||||||
declare workers: ODWorkerManager_Default<ODModalResponderInstance,Source,Params,WorkerIds>
|
declare workers: ODWorkerManager_Default<ODModalResponderInstance,Source,Params,WorkerIds>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponderManagerIds_Default `interface`
|
||||||
|
* This interface is a list of ids available in the `ODContextMenuResponderManager_Default` class.
|
||||||
|
* It's used to generate typescript declarations for this class.
|
||||||
|
*/
|
||||||
|
export interface ODContextMenuResponderManagerIds_Default {
|
||||||
|
//"opendiscord:example":{source:"context-menu",params:{},workers:"opendiscord:example"},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponderManager_Default `default_class`
|
||||||
|
* This is a special class that adds type definitions & typescript to the ODContextMenuResponderManager class.
|
||||||
|
* It doesn't add any extra features!
|
||||||
|
*
|
||||||
|
* This default class is made for the global variable `opendiscord.responders.contextMenus`!
|
||||||
|
*/
|
||||||
|
export class ODContextMenuResponderManager_Default extends ODContextMenuResponderManager {
|
||||||
|
get<ModalResponderId extends keyof ODContextMenuResponderManagerIds_Default>(id:ModalResponderId): ODContextMenuResponder_Default<ODContextMenuResponderManagerIds_Default[ModalResponderId]["source"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["params"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["workers"]>
|
||||||
|
get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
|
||||||
|
|
||||||
|
get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
|
||||||
|
return super.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove<ModalResponderId extends keyof ODContextMenuResponderManagerIds_Default>(id:ModalResponderId): ODContextMenuResponder_Default<ODContextMenuResponderManagerIds_Default[ModalResponderId]["source"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["params"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["workers"]>
|
||||||
|
remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
|
||||||
|
|
||||||
|
remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
|
||||||
|
return super.remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(id:keyof ODContextMenuResponderManagerIds_Default): boolean
|
||||||
|
exists(id:ODValidId): boolean
|
||||||
|
|
||||||
|
exists(id:ODValidId): boolean {
|
||||||
|
return super.exists(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponder_Default `default_class`
|
||||||
|
* This is a special class that adds type definitions & typescript to the ODContextMenuResponder class.
|
||||||
|
* It doesn't add any extra features!
|
||||||
|
*
|
||||||
|
* This default class is made for the default `ODContextMenuResponder`'s!
|
||||||
|
*/
|
||||||
|
export class ODContextMenuResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODContextMenuResponder<Source,Params> {
|
||||||
|
declare workers: ODWorkerManager_Default<ODContextMenuResponderInstance,Source,Params,WorkerIds>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponderManagerIds_Default `interface`
|
||||||
|
* This interface is a list of ids available in the `ODAutocompleteResponderManager_Default` class.
|
||||||
|
* It's used to generate typescript declarations for this class.
|
||||||
|
*/
|
||||||
|
export interface ODAutocompleteResponderManagerIds_Default {
|
||||||
|
//"opendiscord:example":{source:"autocomplete",params:{},workers:"opendiscord:example"},
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponderManager_Default `default_class`
|
||||||
|
* This is a special class that adds type definitions & typescript to the ODAutocompleteResponderManager class.
|
||||||
|
* It doesn't add any extra features!
|
||||||
|
*
|
||||||
|
* This default class is made for the global variable `opendiscord.responders.autocomplete`!
|
||||||
|
*/
|
||||||
|
export class ODAutocompleteResponderManager_Default extends ODAutocompleteResponderManager {
|
||||||
|
get<ModalResponderId extends keyof ODAutocompleteResponderManagerIds_Default>(id:ModalResponderId): ODAutocompleteResponder_Default<ODAutocompleteResponderManagerIds_Default[ModalResponderId]["source"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["params"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["workers"]>
|
||||||
|
get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
|
||||||
|
|
||||||
|
get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
|
||||||
|
return super.get(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
remove<ModalResponderId extends keyof ODAutocompleteResponderManagerIds_Default>(id:ModalResponderId): ODAutocompleteResponder_Default<ODAutocompleteResponderManagerIds_Default[ModalResponderId]["source"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["params"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["workers"]>
|
||||||
|
remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
|
||||||
|
|
||||||
|
remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
|
||||||
|
return super.remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
exists(id:keyof ODAutocompleteResponderManagerIds_Default): boolean
|
||||||
|
exists(id:ODValidId): boolean
|
||||||
|
|
||||||
|
exists(id:ODValidId): boolean {
|
||||||
|
return super.exists(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponder_Default `default_class`
|
||||||
|
* This is a special class that adds type definitions & typescript to the ODAutocompleteResponder class.
|
||||||
|
* It doesn't add any extra features!
|
||||||
|
*
|
||||||
|
* This default class is made for the default `ODAutocompleteResponder`'s!
|
||||||
|
*/
|
||||||
|
export class ODAutocompleteResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODAutocompleteResponder<Source,Params> {
|
||||||
|
declare workers: ODWorkerManager_Default<ODAutocompleteResponderInstance,Source,Params,WorkerIds>
|
||||||
|
}
|
||||||
@@ -140,6 +140,10 @@ export interface ODDefaults {
|
|||||||
dropdownRespondersLoading:boolean,
|
dropdownRespondersLoading:boolean,
|
||||||
/**Load the default Open Ticket modal responders. */
|
/**Load the default Open Ticket modal responders. */
|
||||||
modalRespondersLoading:boolean,
|
modalRespondersLoading:boolean,
|
||||||
|
/**Load the default Open Ticket context menu responders. */
|
||||||
|
contextMenuRespondersLoading:boolean,
|
||||||
|
/**Load the default Open Ticket autocomplete responders. */
|
||||||
|
autocompleteRespondersLoading:boolean,
|
||||||
/**Set the time (in ms) before Open Ticket sends an error message when no reply is sent in a responder. */
|
/**Set the time (in ms) before Open Ticket sends an error message when no reply is sent in a responder. */
|
||||||
responderTimeoutMs:number,
|
responderTimeoutMs:number,
|
||||||
|
|
||||||
@@ -311,6 +315,8 @@ export class ODDefaultsManager {
|
|||||||
buttonRespondersLoading:true,
|
buttonRespondersLoading:true,
|
||||||
dropdownRespondersLoading:true,
|
dropdownRespondersLoading:true,
|
||||||
modalRespondersLoading:true,
|
modalRespondersLoading:true,
|
||||||
|
contextMenuRespondersLoading:true,
|
||||||
|
autocompleteRespondersLoading:true,
|
||||||
responderTimeoutMs:2500,
|
responderTimeoutMs:2500,
|
||||||
|
|
||||||
actionsLoading:true,
|
actionsLoading:true,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ODId, ODManager, ODValidId, ODSystemError, ODManagerData } from "./base
|
|||||||
import * as discord from "discord.js"
|
import * as discord from "discord.js"
|
||||||
import { ODWorkerManager, ODWorkerCallback, ODWorker } from "./worker"
|
import { ODWorkerManager, ODWorkerCallback, ODWorker } from "./worker"
|
||||||
import { ODDebugger } from "./console"
|
import { ODDebugger } from "./console"
|
||||||
import { ODClientManager, ODSlashCommand, ODTextCommand, ODTextCommandInteractionOption } from "./client"
|
import { ODClientManager, ODContextMenu, ODSlashCommand, ODTextCommand, ODTextCommandInteractionOption } from "./client"
|
||||||
import { ODDropdownData, ODMessageBuildResult, ODMessageBuildSentResult, ODModalBuildResult } from "./builder"
|
import { ODDropdownData, ODMessageBuildResult, ODMessageBuildSentResult, ODModalBuildResult } from "./builder"
|
||||||
|
|
||||||
/**## ODResponderImplementation `class`
|
/**## ODResponderImplementation `class`
|
||||||
@@ -36,7 +36,7 @@ export class ODResponderImplementation<Instance,Source extends string,Params> ex
|
|||||||
/**## ODResponderTimeoutErrorCallback `type`
|
/**## ODResponderTimeoutErrorCallback `type`
|
||||||
* This is the callback for the responder timeout function. It will be executed when something went wrong or the action takes too much time.
|
* This is the callback for the responder timeout function. It will be executed when something went wrong or the action takes too much time.
|
||||||
*/
|
*/
|
||||||
export type ODResponderTimeoutErrorCallback<Instance, Source extends "slash"|"text"|"button"|"dropdown"|"modal"|"other"> = (instance:Instance, source:Source) => void|Promise<void>
|
export type ODResponderTimeoutErrorCallback<Instance, Source extends "slash"|"text"|"button"|"dropdown"|"modal"|"other"|"context-menu"|"autocomplete"> = (instance:Instance, source:Source) => void|Promise<void>
|
||||||
|
|
||||||
/**## ODResponderManager `class`
|
/**## ODResponderManager `class`
|
||||||
* This is an Open Ticket responder manager.
|
* This is an Open Ticket responder manager.
|
||||||
@@ -61,12 +61,18 @@ export class ODResponderManager {
|
|||||||
dropdowns: ODDropdownResponderManager
|
dropdowns: ODDropdownResponderManager
|
||||||
/**A manager for all modal responders. */
|
/**A manager for all modal responders. */
|
||||||
modals: ODModalResponderManager
|
modals: ODModalResponderManager
|
||||||
|
/**A manager for all context menu responders. */
|
||||||
|
contextMenus: ODContextMenuResponderManager
|
||||||
|
/**A manager for all autocomplete responders. */
|
||||||
|
autocomplete: ODAutocompleteResponderManager
|
||||||
|
|
||||||
constructor(debug:ODDebugger, client:ODClientManager){
|
constructor(debug:ODDebugger, client:ODClientManager){
|
||||||
this.commands = new ODCommandResponderManager(debug,"command responder",client)
|
this.commands = new ODCommandResponderManager(debug,"command responder",client)
|
||||||
this.buttons = new ODButtonResponderManager(debug,"button responder",client)
|
this.buttons = new ODButtonResponderManager(debug,"button responder",client)
|
||||||
this.dropdowns = new ODDropdownResponderManager(debug,"dropdown responder",client)
|
this.dropdowns = new ODDropdownResponderManager(debug,"dropdown responder",client)
|
||||||
this.modals = new ODModalResponderManager(debug,"modal responder",client)
|
this.modals = new ODModalResponderManager(debug,"modal responder",client)
|
||||||
|
this.contextMenus = new ODContextMenuResponderManager(debug,"context menu responder",client)
|
||||||
|
this.autocomplete = new ODAutocompleteResponderManager(debug,"autocomplete responder",client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1083,7 +1089,7 @@ export class ODModalResponderInstance {
|
|||||||
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
|
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
|
||||||
this.didReply = true
|
this.didReply = true
|
||||||
return {success:true,message:await sent.fetch()}
|
return {success:true,message:await sent.fetch()}
|
||||||
}else throw new ODSystemError()
|
}else throw new ODSystemError("Unable to update modal interaction!")
|
||||||
}catch{
|
}catch{
|
||||||
return {success:false,message:null}
|
return {success:false,message:null}
|
||||||
}
|
}
|
||||||
@@ -1114,3 +1120,296 @@ export class ODModalResponder<Source extends string,Params> extends ODResponderI
|
|||||||
await this.workers.executeWorkers(instance,source,params)
|
await this.workers.executeWorkers(instance,source,params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponderManager `class`
|
||||||
|
* This is an Open Ticket context menu responder manager.
|
||||||
|
*
|
||||||
|
* It contains all Open Ticket context menu responders. These can respond to user/message context menu interactions.
|
||||||
|
*
|
||||||
|
* Using the Open Ticket responder system has a few advantages compared to vanilla discord.js:
|
||||||
|
* - plugins can extend/edit replies
|
||||||
|
* - automatically reply on error
|
||||||
|
* - independent workers (with priority)
|
||||||
|
* - fail-safe design using try-catch
|
||||||
|
* - know where the request came from!
|
||||||
|
* - And so much more!
|
||||||
|
*/
|
||||||
|
export class ODContextMenuResponderManager extends ODManager<ODContextMenuResponder<"context-menu",any>> {
|
||||||
|
/**An alias to the Open Ticket client manager. */
|
||||||
|
#client: ODClientManager
|
||||||
|
/**The callback executed when the default workers take too much time to reply. */
|
||||||
|
#timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null = null
|
||||||
|
/**The amount of milliseconds before the timeout error callback is executed. */
|
||||||
|
#timeoutMs: number|null = null
|
||||||
|
|
||||||
|
constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
|
||||||
|
super(debug,debugname)
|
||||||
|
this.#client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Set the message to send when the response times out! */
|
||||||
|
setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, ms:number|null){
|
||||||
|
this.#timeoutErrorCallback = callback
|
||||||
|
this.#timeoutMs = ms
|
||||||
|
}
|
||||||
|
|
||||||
|
add(data:ODContextMenuResponder<"context-menu",any>, overwrite?:boolean){
|
||||||
|
const res = super.add(data,overwrite)
|
||||||
|
|
||||||
|
this.#client.contextMenus.onInteraction(data.match,(interaction,cmd) => {
|
||||||
|
const newData = this.get(data.id)
|
||||||
|
if (!newData) return
|
||||||
|
newData.respond(new ODContextMenuResponderInstance(interaction,cmd,this.#timeoutErrorCallback,this.#timeoutMs),"context-menu",{})
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponderInstance `class`
|
||||||
|
* This is an Open Ticket context menu responder instance.
|
||||||
|
*
|
||||||
|
* An instance is an active context menu interaction. You can reply to the context menu using `reply()`.
|
||||||
|
*/
|
||||||
|
export class ODContextMenuResponderInstance {
|
||||||
|
/**The interaction which is the source of this instance. */
|
||||||
|
interaction: discord.ContextMenuCommandInteraction
|
||||||
|
/**Did a worker already reply to this instance/interaction? */
|
||||||
|
didReply: boolean = false
|
||||||
|
/**The context menu wich is the source of this instance. */
|
||||||
|
menu:ODContextMenu
|
||||||
|
/**The user who triggered this context menu. */
|
||||||
|
user: discord.User
|
||||||
|
/**The guild member who triggered this context menu. */
|
||||||
|
member: discord.GuildMember|null
|
||||||
|
/**The guild where this context menu was triggered. */
|
||||||
|
guild: discord.Guild|null
|
||||||
|
/**The channel where this context menu was triggered. */
|
||||||
|
channel: discord.TextBasedChannel
|
||||||
|
/**The target of this context menu (user or message). */
|
||||||
|
target: discord.Message|discord.User
|
||||||
|
|
||||||
|
constructor(interaction:discord.ContextMenuCommandInteraction, menu:ODContextMenu, errorCallback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, timeoutMs:number|null){
|
||||||
|
if (!interaction.channel) throw new ODSystemError("ODContextMenuResponderInstance: Unable to find interaction channel!")
|
||||||
|
this.interaction = interaction
|
||||||
|
this.menu = menu
|
||||||
|
this.user = interaction.user
|
||||||
|
this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
|
||||||
|
this.guild = interaction.guild
|
||||||
|
this.channel = interaction.channel
|
||||||
|
if (interaction.isMessageContextMenuCommand()) this.target = interaction.targetMessage
|
||||||
|
else if (interaction.isUserContextMenuCommand()) this.target = interaction.targetUser
|
||||||
|
else throw new ODSystemError("ODContextMenuResponderInstance: Invalid context menu type. Should be of the type User/Message!")
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
if (!this.didReply){
|
||||||
|
try {
|
||||||
|
if (!errorCallback){
|
||||||
|
this.reply({id:new ODId("looks-like-we-got-an-error-here"), ephemeral:true, message:{
|
||||||
|
content:":x: **Something went wrong while replying to this context menu!**"
|
||||||
|
}})
|
||||||
|
}else{
|
||||||
|
await errorCallback(this,"context-menu")
|
||||||
|
}
|
||||||
|
|
||||||
|
}catch(err){
|
||||||
|
process.emit("uncaughtException",err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},timeoutMs ?? 2500)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Reply to this context menu. */
|
||||||
|
async reply(msg:ODMessageBuildResult): Promise<ODMessageBuildSentResult<boolean>> {
|
||||||
|
try{
|
||||||
|
const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : []
|
||||||
|
if (this.interaction.replied || this.interaction.deferred){
|
||||||
|
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
|
||||||
|
this.didReply = true
|
||||||
|
return {success:true,message:sent}
|
||||||
|
}else{
|
||||||
|
const sent = await this.interaction.reply(Object.assign(msg.message,{flags:msgFlags}))
|
||||||
|
this.didReply = true
|
||||||
|
return {success:true,message:await sent.fetch()}
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
return {success:false,message:null}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**Update the message of this context menu. */
|
||||||
|
async update(msg:ODMessageBuildResult): Promise<ODMessageBuildSentResult<boolean>> {
|
||||||
|
try{
|
||||||
|
const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : []
|
||||||
|
if (this.interaction.replied || this.interaction.deferred){
|
||||||
|
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
|
||||||
|
this.didReply = true
|
||||||
|
return {success:true,message:await sent.fetch()}
|
||||||
|
}else throw new ODSystemError("Unable to update context menu interaction!")
|
||||||
|
}catch{
|
||||||
|
return {success:false,message:null}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**Defer this context menu. */
|
||||||
|
async defer(type:"reply", ephemeral:boolean){
|
||||||
|
if (this.interaction.deferred) return false
|
||||||
|
if (type == "reply"){
|
||||||
|
const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
|
||||||
|
await this.interaction.deferReply({flags:msgFlags})
|
||||||
|
}
|
||||||
|
this.didReply = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
/**Show a modal as reply to this context menu. */
|
||||||
|
async modal(modal:ODModalBuildResult){
|
||||||
|
this.interaction.showModal(modal.modal)
|
||||||
|
this.didReply = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODContextMenuResponder `class`
|
||||||
|
* This is an Open Ticket context menu responder.
|
||||||
|
*
|
||||||
|
* This class manages all workers which are executed when the related context menu is triggered.
|
||||||
|
*/
|
||||||
|
export class ODContextMenuResponder<Source extends string,Params> extends ODResponderImplementation<ODContextMenuResponderInstance,Source,Params> {
|
||||||
|
/**Respond to this button */
|
||||||
|
async respond(instance:ODContextMenuResponderInstance, source:Source, params:Params){
|
||||||
|
//wait for workers to finish
|
||||||
|
await this.workers.executeWorkers(instance,source,params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponderManager `class`
|
||||||
|
* This is an Open Ticket autocomplete responder manager.
|
||||||
|
*
|
||||||
|
* It contains all Open Ticket autocomplete responders. These can respond to autocomplete interactions.
|
||||||
|
*
|
||||||
|
* Using the Open Ticket responder system has a few advantages compared to vanilla discord.js:
|
||||||
|
* - plugins can extend/edit replies
|
||||||
|
* - automatically reply on error
|
||||||
|
* - independent workers (with priority)
|
||||||
|
* - fail-safe design using try-catch
|
||||||
|
* - know where the request came from!
|
||||||
|
* - And so much more!
|
||||||
|
*/
|
||||||
|
export class ODAutocompleteResponderManager extends ODManager<ODAutocompleteResponder<"autocomplete",any>> {
|
||||||
|
/**An alias to the Open Ticket client manager. */
|
||||||
|
#client: ODClientManager
|
||||||
|
/**The callback executed when the default workers take too much time to reply. */
|
||||||
|
#timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null = null
|
||||||
|
/**The amount of milliseconds before the timeout error callback is executed. */
|
||||||
|
#timeoutMs: number|null = null
|
||||||
|
|
||||||
|
constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
|
||||||
|
super(debug,debugname)
|
||||||
|
this.#client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Set the message to send when the response times out! */
|
||||||
|
setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, ms:number|null){
|
||||||
|
this.#timeoutErrorCallback = callback
|
||||||
|
this.#timeoutMs = ms
|
||||||
|
}
|
||||||
|
|
||||||
|
add(data:ODAutocompleteResponder<"autocomplete",any>, overwrite?:boolean){
|
||||||
|
const res = super.add(data,overwrite)
|
||||||
|
|
||||||
|
this.#client.autocompletes.onInteraction(data.cmdMatch,data.match,(interaction) => {
|
||||||
|
const newData = this.get(data.id)
|
||||||
|
if (!newData) return
|
||||||
|
newData.respond(new ODAutocompleteResponderInstance(interaction,this.#timeoutErrorCallback,this.#timeoutMs),"autocomplete",{})
|
||||||
|
})
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponderInstance `class`
|
||||||
|
* This is an Open Ticket autocomplete responder instance.
|
||||||
|
*
|
||||||
|
* An instance is an active autocomplete interaction. You can reply to the autocomplete using `reply()`.
|
||||||
|
*/
|
||||||
|
export class ODAutocompleteResponderInstance {
|
||||||
|
/**The interaction which is the source of this instance. */
|
||||||
|
interaction: discord.AutocompleteInteraction
|
||||||
|
/**Did a worker already reply to this instance/interaction? */
|
||||||
|
didReply: boolean = false
|
||||||
|
/**The user who triggered this autocomplete. */
|
||||||
|
user: discord.User
|
||||||
|
/**The guild member who triggered this autocomplete. */
|
||||||
|
member: discord.GuildMember|null
|
||||||
|
/**The guild where this autocomplete was triggered. */
|
||||||
|
guild: discord.Guild|null
|
||||||
|
/**The channel where this autocomplete was triggered. */
|
||||||
|
channel: discord.TextBasedChannel
|
||||||
|
/**The target slash command option of this autocomplete. */
|
||||||
|
target: discord.AutocompleteFocusedOption
|
||||||
|
|
||||||
|
constructor(interaction:discord.AutocompleteInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, timeoutMs:number|null){
|
||||||
|
if (!interaction.channel) throw new ODSystemError("ODAutocompleteResponderInstance: Unable to find interaction channel!")
|
||||||
|
this.interaction = interaction
|
||||||
|
this.user = interaction.user
|
||||||
|
this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
|
||||||
|
this.guild = interaction.guild
|
||||||
|
this.channel = interaction.channel
|
||||||
|
this.target = interaction.options.getFocused(true)
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
if (!this.didReply){
|
||||||
|
process.emit("uncaughtException",new ODSystemError("Autocomplete responder instance failed to respond widthin 2.5sec!"))
|
||||||
|
}
|
||||||
|
},timeoutMs ?? 2500)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Reply to this autocomplete. */
|
||||||
|
async autocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
|
||||||
|
const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
|
||||||
|
if (typeof raw == "string") return {name:raw,value:raw}
|
||||||
|
else return raw
|
||||||
|
})
|
||||||
|
|
||||||
|
try{
|
||||||
|
if (this.interaction.responded){
|
||||||
|
return {success:false}
|
||||||
|
}else{
|
||||||
|
await this.interaction.respond(newChoices)
|
||||||
|
return {success:true}
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
process.emit("uncaughtException",err)
|
||||||
|
return {success:false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**Reply to this autocomplete, but filter choices based on the input of the user. */
|
||||||
|
async filteredAutocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
|
||||||
|
const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
|
||||||
|
if (typeof raw == "string") return {name:raw,value:raw}
|
||||||
|
else return raw
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredChoices = newChoices.filter((choice) => choice.name.startsWith(this.target.value) || choice.value.toString().startsWith(this.target.value))
|
||||||
|
return await this.autocomplete(filteredChoices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**## ODAutocompleteResponder `class`
|
||||||
|
* This is an Open Ticket autocomplete responder.
|
||||||
|
*
|
||||||
|
* This class manages all workers which are executed when the related autocomplete is triggered.
|
||||||
|
*/
|
||||||
|
export class ODAutocompleteResponder<Source extends string,Params> extends ODResponderImplementation<ODAutocompleteResponderInstance,Source,Params> {
|
||||||
|
/**The slash command of the autocomplete should match the following regex. */
|
||||||
|
cmdMatch: string|RegExp
|
||||||
|
|
||||||
|
constructor(id:ODValidId,cmdMatch:string|RegExp,match:string|RegExp,callback?:ODWorkerCallback<ODAutocompleteResponderInstance,Source,Params>,priority?:number,callbackId?:ODValidId){
|
||||||
|
super(id,match,callback,priority,callbackId)
|
||||||
|
this.cmdMatch = cmdMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Respond to this autocomplete interaction. */
|
||||||
|
async respond(instance:ODAutocompleteResponderInstance, source:Source, params:Params){
|
||||||
|
//wait for workers to finish
|
||||||
|
await this.workers.executeWorkers(instance,source,params)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -209,6 +209,10 @@ export const loadAllEvents = () => {
|
|||||||
"afterDropdownRespondersLoaded",
|
"afterDropdownRespondersLoaded",
|
||||||
"onModalResponderLoad",
|
"onModalResponderLoad",
|
||||||
"afterModalRespondersLoaded",
|
"afterModalRespondersLoaded",
|
||||||
|
"onContextMenuResponderLoad",
|
||||||
|
"afterContextMenuRespondersLoaded",
|
||||||
|
"onAutocompleteResponderLoad",
|
||||||
|
"afterAutocompleteRespondersLoaded",
|
||||||
|
|
||||||
//plugin loading before finalizations
|
//plugin loading before finalizations
|
||||||
"onPluginBeforeFinalizationLoad",
|
"onPluginBeforeFinalizationLoad",
|
||||||
|
|||||||
+17
-1
@@ -710,6 +710,22 @@ const main = async () => {
|
|||||||
await opendiscord.events.get("onModalResponderLoad").emit([opendiscord.responders.modals,opendiscord.responders,opendiscord.actions])
|
await opendiscord.events.get("onModalResponderLoad").emit([opendiscord.responders.modals,opendiscord.responders,opendiscord.actions])
|
||||||
await opendiscord.events.get("afterModalRespondersLoaded").emit([opendiscord.responders.modals,opendiscord.responders,opendiscord.actions])
|
await opendiscord.events.get("afterModalRespondersLoaded").emit([opendiscord.responders.modals,opendiscord.responders,opendiscord.actions])
|
||||||
|
|
||||||
|
//load context menu responders
|
||||||
|
opendiscord.log("Loading context menu responders...","system")
|
||||||
|
if (opendiscord.defaults.getDefault("contextMenuRespondersLoading")){
|
||||||
|
//TODO!!
|
||||||
|
}
|
||||||
|
await opendiscord.events.get("onContextMenuResponderLoad").emit([opendiscord.responders.contextMenus,opendiscord.responders,opendiscord.actions])
|
||||||
|
await opendiscord.events.get("afterContextMenuRespondersLoaded").emit([opendiscord.responders.contextMenus,opendiscord.responders,opendiscord.actions])
|
||||||
|
|
||||||
|
//load autocomplete responders
|
||||||
|
opendiscord.log("Loading autocomplete responders...","system")
|
||||||
|
if (opendiscord.defaults.getDefault("autocompleteRespondersLoading")){
|
||||||
|
//TODO!!
|
||||||
|
}
|
||||||
|
await opendiscord.events.get("onAutocompleteResponderLoad").emit([opendiscord.responders.autocomplete,opendiscord.responders,opendiscord.actions])
|
||||||
|
await opendiscord.events.get("afterAutocompleteRespondersLoaded").emit([opendiscord.responders.autocomplete,opendiscord.responders,opendiscord.actions])
|
||||||
|
|
||||||
//plugin loading before finalizations
|
//plugin loading before finalizations
|
||||||
await opendiscord.events.get("onPluginBeforeFinalizationLoad").emit([])
|
await opendiscord.events.get("onPluginBeforeFinalizationLoad").emit([])
|
||||||
await opendiscord.events.get("afterPluginBeforeFinalizationLoaded").emit([])
|
await opendiscord.events.get("afterPluginBeforeFinalizationLoaded").emit([])
|
||||||
@@ -871,7 +887,7 @@ const main = async () => {
|
|||||||
await opendiscord.startscreen.renderAllComponents()
|
await opendiscord.startscreen.renderAllComponents()
|
||||||
if (opendiscord.languages.getLanguageMetadata(false)?.automated){
|
if (opendiscord.languages.getLanguageMetadata(false)?.automated){
|
||||||
console.log("===================")
|
console.log("===================")
|
||||||
opendiscord.log("You are currently using a language which has been translated by Google Translate!","warning")
|
opendiscord.log("You are using a language which has been translated using Google Translate or AI!","warning")
|
||||||
opendiscord.log("Please help us improve the translation by contributing to our project!","warning")
|
opendiscord.log("Please help us improve the translation by contributing to our project!","warning")
|
||||||
console.log("===================")
|
console.log("===================")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user