/////////////////////////////////////// //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 /**## 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 { /**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 { /**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 { //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 } }