(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
+67 -81
View File
@@ -96,6 +96,21 @@ export interface ODRoleOptionIdMappings extends ODOptionIdConstraint {
"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`
* This is an Open Ticket option manager.
*
@@ -149,21 +164,41 @@ export interface ODOptionJson {
*
* 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) */
id:api.ODId
/**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()
this.id = new api.ODId(id)
this.type = type
data.forEach((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. */
toJson(version:api.ODVersion): ODOptionJson {
const data = this.getAll().map((data) => {
@@ -180,11 +215,6 @@ export class ODOption extends api.ODManager<ODOptionData<api.ODValidJsonType>> {
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`
@@ -224,32 +254,11 @@ export class ODOptionData<DataType extends api.ODValidJsonType> extends api.ODMa
*
* Use this option to create a new ticket!
*/
export class ODTicketOption extends ODOption {
type: "opendiscord:ticket" = "opendiscord:ticket"
export class ODTicketOption extends ODOption<ODTicketOptionIdMappings> {
readonly type: "opendiscord:ticket" = "opendiscord:ticket"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:ticket",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)
super(id,data)
}
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!
*/
export class ODWebsiteOption extends ODOption {
type: "opendiscord:website" = "opendiscord:website"
export class ODWebsiteOption extends ODOption<ODWebsiteOptionIdMappings> {
readonly type: "opendiscord:website" = "opendiscord:website"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:website",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)
super(id,data)
}
static fromJson(json: ODOptionJson): ODWebsiteOption {
@@ -304,32 +292,11 @@ export class ODWebsiteOption extends ODOption {
*
* Use this option to create a button for reaction roles!
*/
export class ODRoleOption extends ODOption {
type: "opendiscord:role" = "opendiscord:role"
export class ODRoleOption extends ODOption<ODRoleOptionIdMappings> {
readonly type: "opendiscord:role" = "opendiscord:role"
constructor(id:api.ODValidId, data:ODOptionData<api.ODValidJsonType>[]){
super(id,"opendiscord:role",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)
super(id,data)
}
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`
* 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 { ODPermissionEmbedType } from "./permission.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 { ODRole, ODRoleUpdateResult } from "../api/role.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-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: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: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: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"|"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"|"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"},
@@ -51,7 +52,7 @@ export interface ODButtonManagerIdMappings extends api.ODButtonManagerIdConstrai
* It's used to generate typescript declarations for this class.
*/
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`
@@ -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-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-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"},
@@ -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-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: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: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: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<{
/**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. */
panelId:string,
/**A list of options available in this panel. (buttons or dropdown) */
panelOptionIds:string[]
/**Should this panel be auto-updated on restart? */
panelAutoUpdate:boolean
panelAutoUpdate:boolean,
/**Is this panel a sub-panel? */
isSubPanel:boolean
},false,false> {
constructor(id:api.ODValidId,client:api.ODClientManager,database:api.ODDatabase){
super(id,client,database,{})
+30 -3
View File
@@ -378,15 +378,42 @@ export const migrations = [
await globalDatabase.delete("opendiscord:panel-update",panelMessage.key)
}
})
opendiscord.events.get("afterStatesInitiated").listen(async (states) => {
const panelMsgState = states.get("opendiscord:panel-message")
opendiscord.events.get("beforeReadyForUsage").listen(async () => {
const panelMsgState = opendiscord.states.get("opendiscord:panel-message")
for (const {channelId,messageId,panelId,autoUpdate} of migratedPanelStates){
await panelMsgState.setMsgState({channel:channelId,message:messageId},{
messageOrigin:"auto-update",
panelId,
panelOptionIds:[],
panelAutoUpdate:autoUpdate
panelAutoUpdate:autoUpdate,
isSubPanel: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}
])
}
}
})
}