(v4.2.0) Added sub-panels

This commit is contained in:
DJj123dj
2026-05-23 15:53:52 +02:00
parent 7f02dd5848
commit c64220cad4
14 changed files with 244 additions and 132 deletions
+17 -2
View File
@@ -123,7 +123,7 @@ const panelButtons = () => {
new api.ODWorker("opendiscord:ticket-option",0,async (instance,params) => { new api.ODWorker("opendiscord:ticket-option",0,async (instance,params) => {
const {panel,option} = params const {panel,option} = params
instance.setCustomId("od:ticket-option_"+panel.id.value+"_"+option.id.value) instance.setCustomId("od:ticket-option|"+option.id.value)
instance.setMode("button") instance.setMode("button")
instance.setColor(option.get("opendiscord:button-color").value) instance.setColor(option.get("opendiscord:button-color").value)
if (option.get("opendiscord:button-emoji").value) instance.setEmoji(option.get("opendiscord:button-emoji").value) if (option.get("opendiscord:button-emoji").value) instance.setEmoji(option.get("opendiscord:button-emoji").value)
@@ -152,7 +152,22 @@ const panelButtons = () => {
new api.ODWorker("opendiscord:role-option",0,async (instance,params) => { new api.ODWorker("opendiscord:role-option",0,async (instance,params) => {
const {panel,option} = params const {panel,option} = params
instance.setCustomId("od:role-option_"+panel.id.value+"_"+option.id.value) instance.setCustomId("od:role-option|"+option.id.value)
instance.setMode("button")
instance.setColor(option.get("opendiscord:button-color").value)
if (option.get("opendiscord:button-emoji").value) instance.setEmoji(option.get("opendiscord:button-emoji").value)
if (option.get("opendiscord:button-label").value) instance.setLabel(option.get("opendiscord:button-label").value)
if (!option.get("opendiscord:button-emoji").value && !option.get("opendiscord:button-label").value) instance.setLabel("<"+option.id.value+">")
})
)
//SUB-PANEL OPTION
buttons.add(new api.ODButton("opendiscord:subpanel-option"))
buttons.get("opendiscord:subpanel-option").workers.add(
new api.ODWorker("opendiscord:subpanel-option",0,async (instance,params) => {
const {panel,option} = params
instance.setCustomId("od:subpanel-option|"+option.id.value)
instance.setMode("button") instance.setMode("button")
instance.setColor(option.get("opendiscord:button-color").value) instance.setColor(option.get("opendiscord:button-color").value)
if (option.get("opendiscord:button-emoji").value) instance.setEmoji(option.get("opendiscord:button-emoji").value) if (option.get("opendiscord:button-emoji").value) instance.setEmoji(option.get("opendiscord:button-emoji").value)
+2 -2
View File
@@ -24,14 +24,14 @@ const panelDropdowns = () => {
return { return {
label:(label.length > 0) ? label : "<no-label-provided>", label:(label.length > 0) ? label : "<no-label-provided>",
value:"od:ticket-option_"+panel.id.value+"_"+option.id.value, value:"od:ticket-option|"+option.id.value,
emoji:(emoji.length > 0) ? emoji : undefined, emoji:(emoji.length > 0) ? emoji : undefined,
description:(desc.length > 0) ? desc : undefined, description:(desc.length > 0) ? desc : undefined,
default:false default:false
} }
}) })
instance.setCustomId("od:panel-dropdown_"+panel.id.value) instance.setCustomId("od:panel-dropdown")
instance.setType("string") instance.setType("string")
instance.setMaxValues(1) instance.setMaxValues(1)
instance.setMinValues(0) instance.setMinValues(0)
+6 -2
View File
@@ -274,7 +274,10 @@ const panelMessages = () => {
messages.add(new api.ODMessage("opendiscord:panel")) messages.add(new api.ODMessage("opendiscord:panel"))
messages.get("opendiscord:panel").workers.add([ messages.get("opendiscord:panel").workers.add([
new api.ODWorker("opendiscord:panel-layout",1,async (instance,params,origin) => { new api.ODWorker("opendiscord:panel-layout",1,async (instance,params,origin) => {
const {guild,channel,user,panel} = params const {guild,channel,user,panel,isSubPanel} = params
//ephemeral if sub-panel
if (isSubPanel) instance.setEphemeral(true)
//add text //add text
const text = panel.get("opendiscord:text").value const text = panel.get("opendiscord:text").value
@@ -292,7 +295,7 @@ const panelMessages = () => {
//add embed //add embed
const embedOptions = panel.get("opendiscord:embed").value const embedOptions = panel.get("opendiscord:embed").value
if (embedOptions.enabled) instance.addEmbed(await embeds.getSafe("opendiscord:panel").build(origin,{guild,channel,user,panel})) if (embedOptions.enabled) instance.addEmbed(await embeds.getSafe("opendiscord:panel").build(origin,{guild,channel,user,panel,isSubPanel}))
}), }),
new api.ODWorker("opendiscord:panel-components",0,async (instance,params,origin) => { new api.ODWorker("opendiscord:panel-components",0,async (instance,params,origin) => {
const {guild,channel,user,panel} = params const {guild,channel,user,panel} = params
@@ -315,6 +318,7 @@ const panelMessages = () => {
if (option instanceof api.ODTicketOption) instance.addComponent(await buttons.getSafe("opendiscord:ticket-option").build(origin,{guild,channel,user,panel,option})) if (option instanceof api.ODTicketOption) instance.addComponent(await buttons.getSafe("opendiscord:ticket-option").build(origin,{guild,channel,user,panel,option}))
else if (option instanceof api.ODWebsiteOption) instance.addComponent(await buttons.getSafe("opendiscord:website-option").build(origin,{guild,channel,user,panel,option})) else if (option instanceof api.ODWebsiteOption) instance.addComponent(await buttons.getSafe("opendiscord:website-option").build(origin,{guild,channel,user,panel,option}))
else if (option instanceof api.ODRoleOption) instance.addComponent(await buttons.getSafe("opendiscord:role-option").build(origin,{guild,channel,user,panel,option})) else if (option instanceof api.ODRoleOption) instance.addComponent(await buttons.getSafe("opendiscord:role-option").build(origin,{guild,channel,user,panel,option}))
else if (option instanceof api.ODSubPanelOption) instance.addComponent(await buttons.getSafe("opendiscord:subpanel-option").build(origin,{guild,channel,user,panel,option}))
} }
} }
}) })
+60 -2
View File
@@ -36,12 +36,13 @@ export async function registerCommandResponders(){
} }
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:panel-ready").build(origin,{guild,channel,user,panel})) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:panel-ready").build(origin,{guild,channel,user,panel}))
const panelMessage = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:panel").build(origin,{guild,channel,user,panel})).message) const panelMessage = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:panel").build(origin,{guild,channel,user,panel,isSubPanel:false})).message)
if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{ if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{
messageOrigin:origin, messageOrigin:origin,
panelId:panel.id.value, panelId:panel.id.value,
panelOptionIds:panel.get("opendiscord:options").value, panelOptionIds:panel.get("opendiscord:options").value,
panelAutoUpdate:(instance.options.getBoolean("auto-update",false) ?? false) panelAutoUpdate:(instance.options.getBoolean("auto-update",false) ?? false),
isSubPanel:false
},panelMessage.flags.has("Ephemeral")) },panelMessage.flags.has("Ephemeral"))
}), }),
new api.ODWorker("opendiscord:logs",-1,(instance,params,origin,cancel) => { new api.ODWorker("opendiscord:logs",-1,(instance,params,origin,cancel) => {
@@ -53,4 +54,61 @@ export async function registerCommandResponders(){
]) ])
}) })
]) ])
}
export async function registerButtonResponders(){
//SUBPANEL OPTION BUTTON RESPONDER
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:subpanel-option",/^od:subpanel-option\|([^|]+)/))
opendiscord.responders.buttons.get("opendiscord:subpanel-option").workers.add(
new api.ODWorker("opendiscord:subpanel-option",0,async (instance,params,origin,cancel) => {
const {guild,channel,user,message} = instance
const match = /^od:subpanel-option\|([^|]+)/.exec(instance.interaction.customId)
if (!match) return cancel()
const optionId = match[1]
//check message state
const state = await panelMsgState.getMsgState({channel,message})
if (!state){
//TODO TRANSLATION!!!
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:"This panel is no longer valid or has expired. Create a new panel using `{0}` to solve the issue. It is normal to receive this error after a major Open Ticket update.".replace("{0}","/panel"),layout:"simple",customTitle:"Message State Expired"}))
return cancel()
}
//responder checks
const isInGuild = await openticketUtils.replyIsInGuild(instance,origin)
if (!isInGuild || !guild || channel.isDMBased()) return cancel()
//get option data
const option = opendiscord.options.get(optionId)
if (!option || !(option instanceof api.ODSubPanelOption)){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user}))
return cancel()
}
//get panel data
const subPanel = opendiscord.panels.get(option.get("opendiscord:panel-id").value)
if (!subPanel){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-panel-unknown").build(origin,{guild,channel,user}))
return cancel()
}
//send sub-panel message (ephemeral)
const panelMessage = await instance.reply((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("sub-panel",{guild,channel,user,panel:subPanel,isSubPanel:true})))
if (panelMessage.success) await panelMsgState.setMsgState({channel,message:panelMessage.message},{
messageOrigin:"sub-panel",
panelId:subPanel.id.value,
panelOptionIds:subPanel.get("opendiscord:options").value,
panelAutoUpdate:false,
isSubPanel:true
},panelMessage.ephemeral)
opendiscord.log(instance.user.displayName+" created a sub-panel!","info",[
{key:"user",value:instance.user.username},
{key:"userid",value:instance.user.id,hidden:true},
{key:"channelid",value:instance.channel.id,hidden:true},
{key:"method",value:origin}
])
})
)
} }
+5 -2
View File
@@ -8,17 +8,20 @@ const generalConfig = opendiscord.configs.get("opendiscord:general")
export async function registerButtonResponders(){ export async function registerButtonResponders(){
//ROLE OPTION BUTTON RESPONDER //ROLE OPTION BUTTON RESPONDER
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:role-option",/^od:role-option_/)) opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:role-option",/^od:role-option\|([^|]+)/))
opendiscord.responders.buttons.get("opendiscord:role-option").workers.add( opendiscord.responders.buttons.get("opendiscord:role-option").workers.add(
new api.ODWorker("opendiscord:role-option",0,async (instance,params,origin,cancel) => { new api.ODWorker("opendiscord:role-option",0,async (instance,params,origin,cancel) => {
const {guild,channel,user} = instance const {guild,channel,user} = instance
const match = /^od:role-option\|([^|]+)/.exec(instance.interaction.customId)
if (!match) return cancel()
const optionId = match[1]
//responder checks //responder checks
const isInGuild = await openticketUtils.replyIsInGuild(instance,origin) const isInGuild = await openticketUtils.replyIsInGuild(instance,origin)
if (!isInGuild || !guild || channel.isDMBased()) return cancel() if (!isInGuild || !guild || channel.isDMBased()) return cancel()
//get option data //get option data
const optionId = instance.interaction.customId.split("_")[2]
const option = opendiscord.options.get(optionId) const option = opendiscord.options.get(optionId)
if (!option || !(option instanceof api.ODRoleOption)){ if (!option || !(option instanceof api.ODRoleOption)){
//error //error
+12 -6
View File
@@ -80,11 +80,15 @@ export async function registerCommandResponders(){
export async function registerButtonResponders(){ export async function registerButtonResponders(){
//TICKET OPTION BUTTON RESPONDER //TICKET OPTION BUTTON RESPONDER
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:ticket-option",/^od:ticket-option_/)) opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:ticket-option",/^od:ticket-option\|([^|]+)/))
opendiscord.responders.buttons.get("opendiscord:ticket-option").workers.add( opendiscord.responders.buttons.get("opendiscord:ticket-option").workers.add(
new api.ODWorker("opendiscord:ticket-option",0,async (instance,params,origin,cancel) => { new api.ODWorker("opendiscord:ticket-option",0,async (instance,params,origin,cancel) => {
const {guild,channel,user,message} = instance const {guild,channel,user,message} = instance
const match = /^od:ticket-option\|([^|]+)/.exec(instance.interaction.customId)
if (!match) return cancel()
const optionId = match[1]
//check message state //check message state
const state = await panelMsgState.getMsgState({channel,message}) const state = await panelMsgState.getMsgState({channel,message})
if (!state){ if (!state){
@@ -98,7 +102,6 @@ export async function registerButtonResponders(){
if (!isInGuild || !guild || channel.isDMBased()) return cancel() if (!isInGuild || !guild || channel.isDMBased()) return cancel()
//get option data //get option data
const optionId = instance.interaction.customId.split("_")[2]
const option = opendiscord.options.get(optionId) const option = opendiscord.options.get(optionId)
if (!option || !(option instanceof api.ODTicketOption)){ if (!option || !(option instanceof api.ODTicketOption)){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user})) instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user}))
@@ -130,7 +133,7 @@ export async function registerButtonResponders(){
export async function registerDropdownResponders(){ export async function registerDropdownResponders(){
//PANEL DROPDOWN TICKETS DROPDOWN RESPONDER //PANEL DROPDOWN TICKETS DROPDOWN RESPONDER
opendiscord.responders.dropdowns.add(new api.ODDropdownResponder("opendiscord:panel-dropdown-tickets",/^od:panel-dropdown_/)) opendiscord.responders.dropdowns.add(new api.ODDropdownResponder("opendiscord:panel-dropdown-tickets",/^od:panel-dropdown/))
opendiscord.responders.dropdowns.get("opendiscord:panel-dropdown-tickets").workers.add( opendiscord.responders.dropdowns.get("opendiscord:panel-dropdown-tickets").workers.add(
new api.ODWorker("opendiscord:panel-dropdown-tickets",0,async (instance,params,origin,cancel) => { new api.ODWorker("opendiscord:panel-dropdown-tickets",0,async (instance,params,origin,cancel) => {
const {guild,channel,user,message} = instance const {guild,channel,user,message} = instance
@@ -148,7 +151,9 @@ export async function registerDropdownResponders(){
if (!isInGuild || !guild || channel.isDMBased()) return cancel() if (!isInGuild || !guild || channel.isDMBased()) return cancel()
//get option data //get option data
const optionId = instance.values.getStringValues()[0].split("_")[2] const match = /^od:ticket-option\|([^|]+)/.exec(instance.values.getStringValues()[0])
if (!match) return cancel()
const optionId = match[1]
const option = opendiscord.options.get(optionId) const option = opendiscord.options.get(optionId)
if (!option || !(option instanceof api.ODTicketOption)){ if (!option || !(option instanceof api.ODTicketOption)){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user})) instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-option-unknown").build(origin,{guild:instance.guild,channel:instance.channel,user:instance.user}))
@@ -178,12 +183,13 @@ export async function registerDropdownResponders(){
//update panel after dropdown usage (reset panel choice) //update panel after dropdown usage (reset panel choice)
const panel = opendiscord.panels.get(state.data.panelId) const panel = opendiscord.panels.get(state.data.panelId)
if (panel){ if (panel){
const panelMessage = await instance.message.edit((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("auto-update",{guild,channel,user,panel})).message) const panelMessage = await instance.message.edit((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("auto-update",{guild,channel,user,panel,isSubPanel:state.data.isSubPanel})).message)
if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{ if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{
messageOrigin:"auto-update", messageOrigin:"auto-update",
panelId:panel.id.value, panelId:panel.id.value,
panelOptionIds:panel.get("opendiscord:options").value, panelOptionIds:panel.get("opendiscord:options").value,
panelAutoUpdate:state.data.panelAutoUpdate //same value panelAutoUpdate:state.data.panelAutoUpdate, //same value
isSubPanel:state.data.isSubPanel //same value
},panelMessage.flags.has("Ephemeral")) },panelMessage.flags.has("Ephemeral"))
} }
}) })
+67 -81
View File
@@ -96,6 +96,21 @@ export interface ODRoleOptionIdMappings extends ODOptionIdConstraint {
"opendiscord:add-on-join":ODOptionData<boolean> "opendiscord:add-on-join":ODOptionData<boolean>
} }
/**## ODSubPanelOptionIdMappings `interface`
* A list of all available IDs in the default `ODSubPanelOption` class in `opendiscord`.
* It's used to generate typescript declarations for this class.
*/
export interface ODSubPanelOptionIdMappings extends ODOptionIdConstraint {
"opendiscord:name":ODOptionData<string>,
"opendiscord:description":ODOptionData<string>,
"opendiscord:button-emoji":ODOptionData<string>,
"opendiscord:button-label":ODOptionData<string>,
"opendiscord:button-color":ODOptionData<api.ODValidButtonColor>,
"opendiscord:panel-id":ODOptionData<string>
}
/**## ODOptionManager `class` /**## ODOptionManager `class`
* This is an Open Ticket option manager. * This is an Open Ticket option manager.
* *
@@ -149,21 +164,41 @@ export interface ODOptionJson {
* *
* It's recommended to use `ODTicketOption`, `ODWebsiteOption` or `ODRoleOption` instead! * It's recommended to use `ODTicketOption`, `ODWebsiteOption` or `ODRoleOption` instead!
*/ */
export class ODOption extends api.ODManager<ODOptionData<api.ODValidJsonType>> { export abstract class ODOption<IdList extends ODOptionIdConstraint = ODOptionIdConstraint> extends api.ODManager<ODOptionData<api.ODValidJsonType>> {
/**The id of this option. (from the config) */ /**The id of this option. (from the config) */
id:api.ODId id:api.ODId
/**The type of this option. (e.g. `opendiscord:ticket`, `opendiscord:website`, `opendiscord:role`) */ /**The type of this option. (e.g. `opendiscord:ticket`, `opendiscord:website`, `opendiscord:role`) */
type: string abstract readonly type: string
constructor(id:api.ODValidId, type:string, data:ODOptionData<api.ODValidJsonType>[]){ constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super() super()
this.id = new api.ODId(id) this.id = new api.ODId(id)
this.type = type
data.forEach((data) => { data.forEach((data) => {
this.add(data) this.add(data)
}) })
} }
get<OptionId extends keyof api.ODNoGeneric<IdList>>(id:OptionId): IdList[OptionId]
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<OptionId extends keyof api.ODNoGeneric<IdList>>(id:OptionId): IdList[OptionId]
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<IdList>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
}
/**Convert this option to a JSON object for storing this option in the database. */ /**Convert this option to a JSON object for storing this option in the database. */
toJson(version:api.ODVersion): ODOptionJson { toJson(version:api.ODVersion): ODOptionJson {
const data = this.getAll().map((data) => { const data = this.getAll().map((data) => {
@@ -180,11 +215,6 @@ export class ODOption extends api.ODManager<ODOptionData<api.ODValidJsonType>> {
data data
} }
} }
/**Create an option from a JSON object in the database. */
static fromJson(json:ODOptionJson): ODOption {
return new ODOption(json.id,json.type,json.data.map((data) => new ODOptionData(data.id,data.value)))
}
} }
/**## ODOptionData `class` /**## ODOptionData `class`
@@ -224,32 +254,11 @@ export class ODOptionData<DataType extends api.ODValidJsonType> extends api.ODMa
* *
* Use this option to create a new ticket! * Use this option to create a new ticket!
*/ */
export class ODTicketOption extends ODOption { export class ODTicketOption extends ODOption<ODTicketOptionIdMappings> {
type: "opendiscord:ticket" = "opendiscord:ticket" readonly type: "opendiscord:ticket" = "opendiscord:ticket"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){ constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:ticket",data) super(id,data)
}
get<OptionId extends keyof api.ODNoGeneric<ODTicketOptionIdMappings>>(id:OptionId): ODTicketOptionIdMappings[OptionId]
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<OptionId extends keyof api.ODNoGeneric<ODTicketOptionIdMappings>>(id:OptionId): ODTicketOptionIdMappings[OptionId]
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<ODTicketOptionIdMappings>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
} }
static fromJson(json: ODOptionJson): ODTicketOption { static fromJson(json: ODOptionJson): ODTicketOption {
@@ -264,32 +273,11 @@ export class ODTicketOption extends ODOption {
* *
* Use this option to create a button which links to a website! * Use this option to create a button which links to a website!
*/ */
export class ODWebsiteOption extends ODOption { export class ODWebsiteOption extends ODOption<ODWebsiteOptionIdMappings> {
type: "opendiscord:website" = "opendiscord:website" readonly type: "opendiscord:website" = "opendiscord:website"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){ constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:website",data) super(id,data)
}
get<OptionId extends keyof api.ODNoGeneric<ODWebsiteOptionIdMappings>>(id:OptionId): ODWebsiteOptionIdMappings[OptionId]
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<OptionId extends keyof api.ODNoGeneric<ODWebsiteOptionIdMappings>>(id:OptionId): ODWebsiteOptionIdMappings[OptionId]
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<ODWebsiteOptionIdMappings>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
} }
static fromJson(json: ODOptionJson): ODWebsiteOption { static fromJson(json: ODOptionJson): ODWebsiteOption {
@@ -304,32 +292,11 @@ export class ODWebsiteOption extends ODOption {
* *
* Use this option to create a button for reaction roles! * Use this option to create a button for reaction roles!
*/ */
export class ODRoleOption extends ODOption { export class ODRoleOption extends ODOption<ODRoleOptionIdMappings> {
type: "opendiscord:role" = "opendiscord:role" readonly type: "opendiscord:role" = "opendiscord:role"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){ constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:role",data) super(id,data)
}
get<OptionId extends keyof api.ODNoGeneric<ODRoleOptionIdMappings>>(id:OptionId): ODRoleOptionIdMappings[OptionId]
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
get(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.get(id)
}
remove<OptionId extends keyof api.ODNoGeneric<ODRoleOptionIdMappings>>(id:OptionId): ODRoleOptionIdMappings[OptionId]
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null
remove(id:api.ODValidId): ODOptionData<api.ODValidJsonType>|null {
return super.remove(id)
}
exists(id:keyof api.ODNoGeneric<ODRoleOptionIdMappings>): boolean
exists(id:api.ODValidId): boolean
exists(id:api.ODValidId): boolean {
return super.exists(id)
} }
static fromJson(json:ODOptionJson): ODRoleOption { static fromJson(json:ODOptionJson): ODRoleOption {
@@ -337,6 +304,25 @@ export class ODRoleOption extends ODOption {
} }
} }
/**## ODSubPanelOption `class`
* This is an Open Ticket sub-panel option.
*
* This class contains all data related to an Open Ticket sub-panel option (parsed from the config).
*
* Use this option to create a button for sub-panels!
*/
export class ODSubPanelOption extends ODOption<ODSubPanelOptionIdMappings> {
readonly type: "opendiscord:sub-panel" = "opendiscord:sub-panel"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,data)
}
static fromJson(json:ODOptionJson): ODSubPanelOption {
return new ODSubPanelOption(json.id,json.data.map((data) => new ODOptionData(data.id,data.value)))
}
}
/**## ODOptionSuffixManager `class` /**## ODOptionSuffixManager `class`
* This is an Open Ticket option suffix manager. * This is an Open Ticket option suffix manager.
* *
+8 -7
View File
@@ -4,7 +4,7 @@
import * as api from "@open-discord-bots/framework/api" import * as api from "@open-discord-bots/framework/api"
import { ODPermissionEmbedType } from "./permission.js" import { ODPermissionEmbedType } from "./permission.js"
import { ODTranscriptCompiler, ODTranscriptCompilerCompileResult } from "../api/transcript.js" import { ODTranscriptCompiler, ODTranscriptCompilerCompileResult } from "../api/transcript.js"
import { ODRoleOption, ODTicketOption, ODWebsiteOption } from "../api/option.js" import { ODRoleOption, ODSubPanelOption, ODTicketOption, ODWebsiteOption } from "../api/option.js"
import { ODTicket, ODTicketClearFilter } from "../api/ticket.js" import { ODTicket, ODTicketClearFilter } from "../api/ticket.js"
import { ODRole, ODRoleUpdateResult } from "../api/role.js" import { ODRole, ODRoleUpdateResult } from "../api/role.js"
import { ODPriorityLevel } from "../api/priority.js" import { ODPriorityLevel } from "../api/priority.js"
@@ -25,9 +25,10 @@ export interface ODButtonManagerIdMappings extends api.ODButtonManagerIdConstrai
"opendiscord:help-menu-page":{origin:"text"|"slash"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu-page"} "opendiscord:help-menu-page":{origin:"text"|"slash"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu-page"}
"opendiscord:help-menu-switch":{origin:"text"|"slash"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu-switch"}, "opendiscord:help-menu-switch":{origin:"text"|"slash"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu-switch"},
"opendiscord:ticket-option":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODTicketOption},workers:"opendiscord:ticket-option"}, "opendiscord:ticket-option":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODTicketOption},workers:"opendiscord:ticket-option"},
"opendiscord:website-option":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODWebsiteOption},workers:"opendiscord:website-option"}, "opendiscord:website-option":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODWebsiteOption},workers:"opendiscord:website-option"},
"opendiscord:role-option":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODRoleOption},workers:"opendiscord:role-option"} "opendiscord:role-option":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODRoleOption},workers:"opendiscord:role-option"}
"opendiscord:subpanel-option":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,option:ODSubPanelOption},workers:"opendiscord:subpanel-option"}
"opendiscord:visit-ticket":{origin:"ticket-created"|"dm"|"logs"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:visit-ticket"}, "opendiscord:visit-ticket":{origin:"ticket-created"|"dm"|"logs"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:visit-ticket"},
@@ -51,7 +52,7 @@ export interface ODButtonManagerIdMappings extends api.ODButtonManagerIdConstrai
* It's used to generate typescript declarations for this class. * It's used to generate typescript declarations for this class.
*/ */
export interface ODDropdownManagerIdMappings extends api.ODDropdownManagerIdConstraint { export interface ODDropdownManagerIdMappings extends api.ODDropdownManagerIdConstraint {
"opendiscord:panel-dropdown-tickets":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,options:ODTicketOption[]},workers:"opendiscord:panel-dropdown-tickets"} "opendiscord:panel-dropdown-tickets":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,options:ODTicketOption[]},workers:"opendiscord:panel-dropdown-tickets"}
} }
/**## ODFileManagerIdMappings `interface` /**## ODFileManagerIdMappings `interface`
@@ -93,7 +94,7 @@ export interface ODEmbedManagerIdMappings extends api.ODEmbedManagerIdConstraint
"opendiscord:stats-reset":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,reason:string|null},workers:"opendiscord:stats-reset"}, "opendiscord:stats-reset":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,reason:string|null},workers:"opendiscord:stats-reset"},
"opendiscord:stats-ticket-unknown":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,id:string},workers:"opendiscord:stats-ticket-unknown"}, "opendiscord:stats-ticket-unknown":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,id:string},workers:"opendiscord:stats-ticket-unknown"},
"opendiscord:panel":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel},workers:"opendiscord:panel"}, "opendiscord:panel":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,isSubPanel:boolean},workers:"opendiscord:panel"},
"opendiscord:ticket-created":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created"}, "opendiscord:ticket-created":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created"},
"opendiscord:ticket-created-dm":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created-dm"}, "opendiscord:ticket-created-dm":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created-dm"},
"opendiscord:ticket-created-logs":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created-logs"}, "opendiscord:ticket-created-logs":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created-logs"},
@@ -176,7 +177,7 @@ export interface ODMessageManagerIdMappings extends api.ODMessageManagerIdConstr
"opendiscord:stats-reset":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,reason:string|null},workers:"opendiscord:stats-reset"}, "opendiscord:stats-reset":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,reason:string|null},workers:"opendiscord:stats-reset"},
"opendiscord:stats-ticket-unknown":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,id:string},workers:"opendiscord:stats-ticket-unknown"}, "opendiscord:stats-ticket-unknown":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,id:string},workers:"opendiscord:stats-ticket-unknown"},
"opendiscord:panel":{origin:"slash"|"text"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel},workers:"opendiscord:panel-layout"|"opendiscord:panel-components"}, "opendiscord:panel":{origin:"slash"|"text"|"sub-panel"|"auto-update"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel,isSubPanel:boolean},workers:"opendiscord:panel-layout"|"opendiscord:panel-components"},
"opendiscord:panel-ready":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel},workers:"opendiscord:panel-ready"}, "opendiscord:panel-ready":{origin:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,panel:ODPanel},workers:"opendiscord:panel-ready"},
"opendiscord:ticket-created":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created"}, "opendiscord:ticket-created":{origin:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:ODTicket},workers:"opendiscord:ticket-created"},
+1
View File
@@ -48,6 +48,7 @@ export interface ODButtonResponderManagerIdMappings extends api.ODButtonResponde
"opendiscord:ticket-option":{origin:"button",params:{},workers:"opendiscord:ticket-option"}, "opendiscord:ticket-option":{origin:"button",params:{},workers:"opendiscord:ticket-option"},
"opendiscord:role-option":{origin:"button",params:{},workers:"opendiscord:role-option"}, "opendiscord:role-option":{origin:"button",params:{},workers:"opendiscord:role-option"},
"opendiscord:subpanel-option":{origin:"button",params:{},workers:"opendiscord:subpanel-option"},
"opendiscord:claim-ticket":{origin:"button",params:{},workers:"opendiscord:claim-ticket"}, "opendiscord:claim-ticket":{origin:"button",params:{},workers:"opendiscord:claim-ticket"},
"opendiscord:unclaim-ticket":{origin:"button",params:{},workers:"opendiscord:unclaim-ticket"}, "opendiscord:unclaim-ticket":{origin:"button",params:{},workers:"opendiscord:unclaim-ticket"},
+4 -2
View File
@@ -67,13 +67,15 @@ export class ODClearMessageState extends api.ODState<{
*/ */
export class ODPanelMessageState extends api.ODState<{ export class ODPanelMessageState extends api.ODState<{
/**The method this message was generated with. */ /**The method this message was generated with. */
messageOrigin:"slash"|"text"|"auto-update"|"other", messageOrigin:"slash"|"text"|"sub-panel"|"auto-update"|"other",
/**The Id of the panel associated with this message. */ /**The Id of the panel associated with this message. */
panelId:string, panelId:string,
/**A list of options available in this panel. (buttons or dropdown) */ /**A list of options available in this panel. (buttons or dropdown) */
panelOptionIds:string[] panelOptionIds:string[]
/**Should this panel be auto-updated on restart? */ /**Should this panel be auto-updated on restart? */
panelAutoUpdate:boolean panelAutoUpdate:boolean,
/**Is this panel a sub-panel? */
isSubPanel:boolean
},false,false> { },false,false> {
constructor(id:api.ODValidId,client:api.ODClientManager,database:api.ODDatabase){ constructor(id:api.ODValidId,client:api.ODClientManager,database:api.ODDatabase){
super(id,client,database,{}) super(id,client,database,{})
+30 -3
View File
@@ -378,15 +378,42 @@ export const migrations = [
await globalDatabase.delete("opendiscord:panel-update",panelMessage.key) await globalDatabase.delete("opendiscord:panel-update",panelMessage.key)
} }
}) })
opendiscord.events.get("afterStatesInitiated").listen(async (states) => { opendiscord.events.get("beforeReadyForUsage").listen(async () => {
const panelMsgState = states.get("opendiscord:panel-message") const panelMsgState = opendiscord.states.get("opendiscord:panel-message")
for (const {channelId,messageId,panelId,autoUpdate} of migratedPanelStates){ for (const {channelId,messageId,panelId,autoUpdate} of migratedPanelStates){
await panelMsgState.setMsgState({channel:channelId,message:messageId},{ await panelMsgState.setMsgState({channel:channelId,message:messageId},{
messageOrigin:"auto-update", messageOrigin:"auto-update",
panelId, panelId,
panelOptionIds:[], panelOptionIds:[],
panelAutoUpdate:autoUpdate panelAutoUpdate:autoUpdate,
isSubPanel:false
},false) },false)
if (!autoUpdate){
//auto update ALL non auto-update panels once to update button changes from v4.1 -> v4.2
const panel = opendiscord.panels.get(panelId)
if (!panel) continue
//fetch panel message
const mainServer = opendiscord.client.mainServer
const message = await opendiscord.client.fetchChannelMessage(channelId,messageId)
if (!message || !mainServer || !message.editable || message.flags.has("Ephemeral")) continue
const panelMessage = await message.edit((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("auto-update",{guild:mainServer,channel:message.channel,user:opendiscord.client.client.user,panel,isSubPanel:false})).message)
if (panelMessage) await panelMsgState.setMsgState({channel:message.channel,message:panelMessage},{
messageOrigin:"auto-update",
panelId:panel.id.value,
panelOptionIds:panel.get("opendiscord:options").value,
panelAutoUpdate:false,
isSubPanel:false
},panelMessage.flags.has("Ephemeral"))
opendiscord.log("Panel in server got updated to v4.2!","info",[
{key:"channelid",value:channelId},
{key:"messageid",value:messageId},
{key:"panel",value:panel.id.value}
])
}
} }
}) })
} }
+3 -2
View File
@@ -270,12 +270,13 @@ export async function loadPanelAutoUpdateCode(){
const message = await opendiscord.client.fetchChannelMessage(channel,panelState.messageId) const message = await opendiscord.client.fetchChannelMessage(channel,panelState.messageId)
if (!message || !message.editable || message.flags.has("Ephemeral")) continue if (!message || !message.editable || message.flags.has("Ephemeral")) continue
const panelMessage = await message.edit((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("auto-update",{guild:mainServer,channel,user:opendiscord.client.client.user,panel})).message) const panelMessage = await message.edit((await opendiscord.builders.messages.getSafe("opendiscord:panel").build("auto-update",{guild:mainServer,channel,user:opendiscord.client.client.user,panel,isSubPanel:false})).message)
if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{ if (panelMessage) await panelMsgState.setMsgState({channel,message:panelMessage},{
messageOrigin:"auto-update", messageOrigin:"auto-update",
panelId:panel.id.value, panelId:panel.id.value,
panelOptionIds:panel.get("opendiscord:options").value, panelOptionIds:panel.get("opendiscord:options").value,
panelAutoUpdate:true panelAutoUpdate:true,
isSubPanel:false
},panelMessage.flags.has("Ephemeral")) },panelMessage.flags.has("Ephemeral"))
opendiscord.log("Panel in server got auto-updated!","info",[ opendiscord.log("Panel in server got auto-updated!","info",[
+21 -12
View File
@@ -9,11 +9,9 @@ export async function loadAllOptions(){
const loadedOption = loadTicketOption(option) const loadedOption = loadTicketOption(option)
opendiscord.options.add(loadedOption) opendiscord.options.add(loadedOption)
opendiscord.options.suffix.add(loadTicketOptionSuffix(loadedOption)) opendiscord.options.suffix.add(loadTicketOptionSuffix(loadedOption))
}else if (option.type == "website"){ }else if (option.type == "website") opendiscord.options.add(loadWebsiteOption(option))
opendiscord.options.add(loadWebsiteOption(option)) else if (option.type == "role") opendiscord.options.add(loadRoleOption(option))
}else if (option.type == "role"){ else if (option.type == "sub-panel") opendiscord.options.add(loadSubPanelOption(option))
opendiscord.options.add(loadRoleOption(option))
}
}) })
//update options on config reload //update options on config reload
@@ -23,17 +21,15 @@ export async function loadAllOptions(){
await opendiscord.options.suffix.loopAll((data,id) => {opendiscord.options.suffix.remove(id)}) await opendiscord.options.suffix.loopAll((data,id) => {opendiscord.options.suffix.remove(id)})
//add new options //add new options
optionConfig.data.forEach((option) => { for (const option of optionConfig.data){
if (option.type == "ticket"){ if (option.type == "ticket"){
const loadedOption = loadTicketOption(option) const loadedOption = loadTicketOption(option)
opendiscord.options.add(loadedOption) opendiscord.options.add(loadedOption)
opendiscord.options.suffix.add(loadTicketOptionSuffix(loadedOption)) opendiscord.options.suffix.add(loadTicketOptionSuffix(loadedOption))
}else if (option.type == "website"){ }else if (option.type == "website") opendiscord.options.add(loadWebsiteOption(option))
opendiscord.options.add(loadWebsiteOption(option)) else if (option.type == "role") opendiscord.options.add(loadRoleOption(option))
}else if (option.type == "role"){ else if (option.type == "sub-panel") opendiscord.options.add(loadSubPanelOption(option))
opendiscord.options.add(loadRoleOption(option)) }
}
})
//update options in tickets //update options in tickets
await opendiscord.tickets.loopAll((ticket) => { await opendiscord.tickets.loopAll((ticket) => {
@@ -132,6 +128,19 @@ export const loadRoleOption = (opt:api.ODOptionsJsonConfig_RoleOption): api.ODRo
]) ])
} }
export const loadSubPanelOption = (opt:api.ODOptionsJsonConfig_SubPanelOption): api.ODSubPanelOption => {
return new api.ODSubPanelOption(opt.id,[
new api.ODOptionData("opendiscord:name",opt.name),
new api.ODOptionData("opendiscord:description",opt.description),
new api.ODOptionData("opendiscord:button-emoji",opt.button.emoji),
new api.ODOptionData("opendiscord:button-label",opt.button.label),
new api.ODOptionData("opendiscord:button-color",opt.button.color),
new api.ODOptionData("opendiscord:panel-id",opt.subPanelId)
])
}
export const loadTicketOptionSuffix = (option:api.ODTicketOption): api.ODOptionSuffix => { export const loadTicketOptionSuffix = (option:api.ODTicketOption): api.ODOptionSuffix => {
const mode = option.get("opendiscord:channel-suffix").value const mode = option.get("opendiscord:channel-suffix").value
const globalDatabase = opendiscord.databases.get("opendiscord:global") const globalDatabase = opendiscord.databases.get("opendiscord:global")
+8 -9
View File
@@ -526,15 +526,13 @@ const main = async () => {
await opendiscord.events.get("afterStatesLoaded").emit([opendiscord.states]) await opendiscord.events.get("afterStatesLoaded").emit([opendiscord.states])
//init states (async to prevent blocking startup) //init states (async to prevent blocking startup)
utilities.runAsync(async () => { opendiscord.log("Initiating states... (may take a while for many tickets)","system")
opendiscord.log("Initiating states...","system") await opendiscord.events.get("onStateInit").emit([opendiscord.states])
await opendiscord.events.get("onStateInit").emit([opendiscord.states]) if (opendiscord.sharedFuses.getFuse("stateInitiating")){
if (opendiscord.sharedFuses.getFuse("stateInitiating")){ await opendiscord.states.init()
await opendiscord.states.init() await opendiscord.events.get("afterStatesInitiated").emit([opendiscord.states])
await opendiscord.events.get("afterStatesInitiated").emit([opendiscord.states]) }
} opendiscord.log("Message states ready!","info")
opendiscord.log("Message states ready!","info")
})
//plugin loading before managers //plugin loading before managers
await opendiscord.events.get("onPluginBeforeManagerLoad").emit([]) await opendiscord.events.get("onPluginBeforeManagerLoad").emit([])
@@ -727,6 +725,7 @@ const main = async () => {
await (await import("./actions/handleVerifyBar.js")).registerButtonResponders() await (await import("./actions/handleVerifyBar.js")).registerButtonResponders()
await (await import("./actions/handleTranscriptErrors.js")).registerButtonResponders() await (await import("./actions/handleTranscriptErrors.js")).registerButtonResponders()
await (await import("./commands/help.js")).registerButtonResponders() await (await import("./commands/help.js")).registerButtonResponders()
await (await import("./commands/panel.js")).registerButtonResponders()
await (await import("./commands/ticket.js")).registerButtonResponders() await (await import("./commands/ticket.js")).registerButtonResponders()
await (await import("./commands/close.js")).registerButtonResponders() await (await import("./commands/close.js")).registerButtonResponders()
await (await import("./commands/reopen.js")).registerButtonResponders() await (await import("./commands/reopen.js")).registerButtonResponders()