Files
open-ticket/src/core/api/modules/helpmenu.ts
T
2024-09-24 19:32:35 +02:00

216 lines
7.6 KiB
TypeScript

///////////////////////////////////////
//HELP MODULE
///////////////////////////////////////
import { ODId, ODManager, ODManagerData, ODValidId } from "./base"
import { ODDebugger } from "./console"
/**## ODHelpMenuComponentRenderer `type`
* This is the callback of the help menu component renderer. It also contains information about how & where it is rendered.
*/
export type ODHelpMenuComponentRenderer = (page:number, category:number, location:number, mode:"slash"|"text") => string|Promise<string>
/**## ODHelpMenuComponent `class`
* This is an open ticket help menu component.
*
* It can render something on the Open Ticket help menu.
*/
export class ODHelpMenuComponent extends ODManagerData {
/**The priority of this component. The higher, the earlier it will appear in the help menu. */
priority: number
/**The render function for this component. */
render: ODHelpMenuComponentRenderer
constructor(id:ODValidId, priority:number, render:ODHelpMenuComponentRenderer){
super(id)
this.priority = priority
this.render = render
}
}
/**## ODHelpMenuTextComponent `class`
* This is an open ticket help menu text component.
*
* It can render a static piece of text on the Open Ticket help menu.
*/
export class ODHelpMenuTextComponent extends ODHelpMenuComponent {
constructor(id:ODValidId, priority:number, text:string){
super(id,priority,() => {
return text
})
}
}
/**## ODHelpMenuCommandComponentOption `interface`
* This interface contains a command option for the `ODHelpMenuCommandComponent`.
*/
export interface ODHelpMenuCommandComponentOption {
/**The name of this option. */
name:string,
/**Is this option optional? */
optional:boolean
}
/**## ODHelpMenuCommandComponentSettings `interface`
* This interface contains the settings for the `ODHelpMenuCommandComponent`.
*/
export interface ODHelpMenuCommandComponentSettings {
/**The name of this text command. */
textName?:string,
/**The name of this slash command. */
slashName?:string,
/**Options available in the text command. */
textOptions?:ODHelpMenuCommandComponentOption[],
/**Options available in the slash command. */
slashOptions?:ODHelpMenuCommandComponentOption[],
/**The description for the text command. */
textDescription?:string,
/**The description for the slash command. */
slashDescription?:string
}
/**## ODHelpMenuCommandComponent `class`
* This is an open ticket help menu command component.
*
* It contains a useful helper to render a command in the Open Ticket help menu.
*/
export class ODHelpMenuCommandComponent extends ODHelpMenuComponent {
constructor(id:ODValidId, priority:number, settings:ODHelpMenuCommandComponentSettings){
super(id,priority,(page,category,location,mode) => {
if (mode == "slash" && settings.slashName){
return `\`${settings.slashName}${(settings.slashOptions) ? this.#renderOptions(settings.slashOptions) : ""}\` ➜ ${settings.slashDescription ?? ""}`
}else if (mode == "text" && settings.textName){
return `\`${settings.textName}${(settings.textOptions) ? this.#renderOptions(settings.textOptions) : ""}\` ➜ ${settings.textDescription ?? ""}`
}else return ""
})
}
/**Utility function to render all command options. */
#renderOptions(options:ODHelpMenuCommandComponentOption[]){
return " "+options.map((opt) => (opt.optional) ? `[${opt.name}]` : `<${opt.name}>`).join(" ")
}
}
/**## ODHelpMenuCategory `class`
* This is an open ticket help menu category.
*
* Every category in the help menu is an embed field by default.
* Try to limit the amount of components per category.
*/
export class ODHelpMenuCategory extends ODManager<ODHelpMenuComponent> {
/**The id of this category. */
id: ODId
/**The priority of this category. The higher, the earlier it will appear in the menu. */
priority: number
/**The name of this category. (can include emoji's) */
name: string
/**When enabled, it automatically starts this category on a new page. */
newPage: boolean
constructor(id:ODValidId, priority:number, name:string, newPage?:boolean){
super()
this.id = new ODId(id)
this.priority = priority
this.name = name
this.newPage = newPage ?? false
}
/**Render this category and it's components. */
async render(page:number, category:number, mode:"slash"|"text"){
//sort from high priority to low
const derefArray = [...this.getAll()]
derefArray.sort((a,b) => {
return b.priority-a.priority
})
const result: string[] = []
let i = 0
for (const component of derefArray){
try {
result.push(await component.render(page,category,i,mode))
}catch(err){
process.emit("uncaughtException",err)
}
i++
}
//only return the non-empty components
return result.filter((component) => component !== "").join("\n\n")
}
}
/**## ODHelpMenuRenderResult `type`
* This is the array returned when the help menu has been rendered successfully.
*
* It contains a list of pages, which contain categories by name & value (content).
*/
export type ODHelpMenuRenderResult = {name:string, value:string}[][]
/**## ODHelpMenuManager `class`
* This is an open ticket help menu manager.
*
* It is responsible for rendering the entire help menu content.
* You are also able to configure the amount of categories per page here.
*
* Fewer Categories == More Clean Menu
*/
export class ODHelpMenuManager extends ODManager<ODHelpMenuCategory> {
/**Alias to open ticket debugger. */
#debug: ODDebugger
/**The amount of categories per-page. */
categoriesPerPage: number = 3
constructor(debug:ODDebugger){
super(debug,"help menu category")
this.#debug = debug
}
add(data:ODHelpMenuCategory, overwrite?:boolean): boolean {
data.useDebug(this.#debug,"help menu component")
return super.add(data,overwrite)
}
/**Render this entire help menu & return a `ODHelpMenuRenderResult`. */
async render(mode:"slash"|"text"): Promise<ODHelpMenuRenderResult> {
//sort from high priority to low
const derefArray = [...this.getAll()]
derefArray.sort((a,b) => {
return b.priority-a.priority
})
const result: {name:string, value:string}[][] = []
let currentPage: {name:string, value:string}[] = []
for (const category of derefArray){
try {
const renderedCategory = await category.render(result.length,currentPage.length,mode)
if (renderedCategory !== ""){
//create new page when category wants to
if (currentPage.length > 0 && category.newPage){
result.push(currentPage)
currentPage = []
}
currentPage.push({
name:category.name,
value:renderedCategory
})
//create new page when page is full
if (currentPage.length >= this.categoriesPerPage){
result.push(currentPage)
currentPage = []
}
}
}catch(err){
process.emit("uncaughtException",err)
}
}
//push current page when not-empty
if (currentPage.length > 0) result.push(currentPage)
return result
}
}