From 40bd873a335cc13a0005f77ff579c426464cf1f2 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:13:13 +0100 Subject: [PATCH] Added new API comments (part 7) --- src/core/api/modules/responder.ts | 163 ++++++++++++++++++++++++++-- src/core/api/modules/startscreen.ts | 95 ++++++++++++++++ src/core/api/modules/stat.ts | 79 ++++++++++++++ src/core/api/openticket/option.ts | 6 - 4 files changed, 329 insertions(+), 14 deletions(-) diff --git a/src/core/api/modules/responder.ts b/src/core/api/modules/responder.ts index b5d3a2d..600d93a 100644 --- a/src/core/api/modules/responder.ts +++ b/src/core/api/modules/responder.ts @@ -131,7 +131,7 @@ export class ODCommandResponderManager extends ODManager> { try { const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -407,6 +408,7 @@ export class ODCommandResponderInstance { return {success:false,message:null} } } + /**Defer this command. */ async defer(ephemeral:boolean){ if (this.type != "interaction" || !(this.interaction instanceof discord.ChatInputCommandInteraction)) return false if (this.interaction.deferred) return false @@ -415,6 +417,7 @@ export class ODCommandResponderInstance { this.didReply = true return true } + /**Show a modal as reply to this command. */ async modal(modal:ODModalBuildResult){ if (this.type != "interaction" || !(this.interaction instanceof discord.ChatInputCommandInteraction)) return false this.interaction.showModal(modal.modal) @@ -423,6 +426,11 @@ export class ODCommandResponderInstance { } } +/**## ODCommandResponder `class` + * This is an open ticket command responder. + * + * This class manages all workers which are executed when the related command is triggered. + */ export class ODCommandResponder extends ODResponderImplementation { /**The prefix of the text command needs to match this */ prefix: string @@ -439,10 +447,27 @@ export class ODCommandResponder extends OD } } +/**## ODButtonResponderManager `class` + * This is an open ticket button responder manager. + * + * It contains all Open Ticket button responders. These can respond to button 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 ODButtonResponderManager extends ODManager> { + /**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|null = null + /**The amount of milliseconds before the timeout error callback is executed. */ #timeoutMs: number|null = null + /**A list of listeners which will listen to the raw interactionCreate event from discord.js */ #listeners: ((interaction:discord.ButtonInteraction) => void)[] = [] constructor(debug:ODDebugger, debugname:string, client:ODClientManager){ @@ -474,13 +499,25 @@ export class ODButtonResponderManager extends ODManager|null, timeoutMs:number|null){ @@ -510,6 +547,7 @@ export class ODButtonResponderInstance { },timeoutMs ?? 2500) } + /**Reply to this button. */ async reply(msg:ODMessageBuildResult): Promise> { try{ const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -526,6 +564,7 @@ export class ODButtonResponderInstance { return {success:false,message:null} } } + /**Update the message of this button. */ async update(msg:ODMessageBuildResult): Promise> { try{ const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -542,6 +581,7 @@ export class ODButtonResponderInstance { return {success:false,message:null} } } + /**Defer this button. */ async defer(type:"reply"|"update", ephemeral:boolean){ if (this.interaction.deferred) return false if (type == "reply"){ @@ -553,12 +593,14 @@ export class ODButtonResponderInstance { this.didReply = true return true } + /**Show a modal as reply to this button. */ async modal(modal:ODModalBuildResult){ this.interaction.showModal(modal.modal) this.didReply = true return true } + /**Get a component from the original message of this button. */ getMessageComponent(type:"button",id:string|RegExp): discord.ButtonComponent|null getMessageComponent(type:"string-dropdown",id:string|RegExp): discord.StringSelectMenuComponent|null getMessageComponent(type:"user-dropdown",id:string|RegExp): discord.UserSelectMenuComponent|null @@ -582,11 +624,17 @@ export class ODButtonResponderInstance { return result } + /**Get the first embed of the original message if it exists. */ getMessageEmbed(): discord.Embed|null { return this.message.embeds[0] ?? null } } +/**## ODButtonResponder `class` + * This is an open ticket button responder. + * + * This class manages all workers which are executed when the related button is triggered. + */ export class ODButtonResponder extends ODResponderImplementation { /**Respond to this button */ async respond(instance:ODButtonResponderInstance, source:Source, params:Params){ @@ -595,10 +643,27 @@ export class ODButtonResponder extends ODResponder } } +/**## ODDropdownResponderManager `class` + * This is an open ticket dropdown responder manager. + * + * It contains all Open Ticket dropdown responders. These can respond to dropdown 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 ODDropdownResponderManager extends ODManager> { + /**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|null = null + /**The amount of milliseconds before the timeout error callback is executed. */ #timeoutMs: number|null = null + /**A list of listeners which will listen to the raw interactionCreate event from discord.js */ #listeners: ((interaction:discord.AnySelectMenuInteraction) => void)[] = [] constructor(debug:ODDebugger, debugname:string, client:ODClientManager){ @@ -630,8 +695,15 @@ export class ODDropdownResponderManager extends ODManager { if (this.#type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getRoleValues() dropdown type isn't role!") try { @@ -663,6 +738,7 @@ export class ODDropdownResponderInstanceValues { throw new ODSystemError("ODDropdownResponderInstanceValues:getRoleValues() invalid values!") } } + /**Get the selected users. */ async getUserValues(): Promise { if (this.#type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getUserValues() dropdown type isn't user!") try { @@ -676,6 +752,7 @@ export class ODDropdownResponderInstanceValues { throw new ODSystemError("ODDropdownResponderInstanceValues:getUserValues() invalid values!") } } + /**Get the selected channels. */ async getChannelValues(): Promise { if (this.#type != "role") throw new ODSystemError("ODDropdownResponderInstanceValues:getChannelValues() dropdown type isn't channel!") try { @@ -692,15 +769,29 @@ export class ODDropdownResponderInstanceValues { } } +/**## ODDropdownResponderInstance `class` + * This is an open ticket dropdown responder instance. + * + * An instance is an active dropdown interaction. You can reply to the dropdown using `reply()`. + */ export class ODDropdownResponderInstance { - interaction:discord.AnySelectMenuInteraction + /**The interaction which is the source of this instance. */ + interaction: discord.AnySelectMenuInteraction + /**Did a worker already reply to this instance/interaction? */ didReply: boolean = false + /**The dropdown type. */ type: ODDropdownData["type"] + /**The manager for all values of this dropdown. */ values: ODDropdownResponderInstanceValues + /**The user who triggered this dropdown. */ user: discord.User + /**The guild member who triggered this dropdown. */ member: discord.GuildMember|null + /**The guild where this dropdown was triggered. */ guild: discord.Guild|null + /**The channel where this dropdown was triggered. */ channel: discord.TextBasedChannel + /**The message this dropdown originates from. */ message: discord.Message constructor(interaction:discord.AnySelectMenuInteraction, errorCallback:ODResponderTimeoutErrorCallback|null, timeoutMs:number|null){ @@ -743,6 +834,7 @@ export class ODDropdownResponderInstance { },timeoutMs ?? 2500) } + /**Reply to this dropdown. */ async reply(msg:ODMessageBuildResult): Promise> { try { const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -759,6 +851,7 @@ export class ODDropdownResponderInstance { return {success:false,message:null} } } + /**Update the message of this dropdown. */ async update(msg:ODMessageBuildResult): Promise> { try{ const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -775,6 +868,7 @@ export class ODDropdownResponderInstance { return {success:false,message:null} } } + /**Defer this dropdown. */ async defer(type:"reply"|"update", ephemeral:boolean){ if (this.interaction.deferred) return false if (type == "reply"){ @@ -786,12 +880,14 @@ export class ODDropdownResponderInstance { this.didReply = true return true } + /**Show a modal as reply to this dropdown. */ async modal(modal:ODModalBuildResult){ this.interaction.showModal(modal.modal) this.didReply = true return true } + /**Get a component from the original message of this dropdown. */ getMessageComponent(type:"button",id:string|RegExp): discord.ButtonComponent|null getMessageComponent(type:"string-dropdown",id:string|RegExp): discord.StringSelectMenuComponent|null getMessageComponent(type:"user-dropdown",id:string|RegExp): discord.UserSelectMenuComponent|null @@ -815,11 +911,17 @@ export class ODDropdownResponderInstance { return result } + /**Get the first embed of the original message if it exists. */ getMessageEmbed(): discord.Embed|null { return this.message.embeds[0] ?? null } } +/**## ODDropdownResponder `class` + * This is an open ticket dropdown responder. + * + * This class manages all workers which are executed when the related dropdown is triggered. + */ export class ODDropdownResponder extends ODResponderImplementation { /**Respond to this dropdown */ async respond(instance:ODDropdownResponderInstance, source:Source, params:Params){ @@ -828,10 +930,27 @@ export class ODDropdownResponder extends ODRespond } } +/**## ODModalResponderManager `class` + * This is an open ticket modal responder manager. + * + * It contains all Open Ticket modal responders. These can respond to modal 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 ODModalResponderManager extends ODManager> { + /**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|null = null + /**The amount of milliseconds before the timeout error callback is executed. */ #timeoutMs: number|null = null + /**A list of listeners which will listen to the raw interactionCreate event from discord.js */ #listeners: ((interaction:discord.ModalSubmitInteraction) => void)[] = [] constructor(debug:ODDebugger, debugname:string, client:ODClientManager){ @@ -863,12 +982,20 @@ export class ODModalResponderManager extends ODManager|null, timeoutMs:number|null){ @@ -917,6 +1056,7 @@ export class ODModalResponderInstance { },timeoutMs ?? 2500) } + /**Reply to this modal. */ async reply(msg:ODMessageBuildResult): Promise> { try{ const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -933,6 +1073,7 @@ export class ODModalResponderInstance { return {success:false,message:null} } } + /**Update the message of this modal. */ async update(msg:ODMessageBuildResult): Promise> { try{ const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : [] @@ -945,6 +1086,7 @@ export class ODModalResponderInstance { return {success:false,message:null} } } + /**Defer this modal. */ async defer(type:"reply"|"update", ephemeral:boolean){ if (this.interaction.deferred) return false if (type == "reply"){ @@ -958,6 +1100,11 @@ export class ODModalResponderInstance { } } +/**## ODModalResponder `class` + * This is an open ticket modal responder. + * + * This class manages all workers which are executed when the related modal is triggered. + */ export class ODModalResponder extends ODResponderImplementation { /**Respond to this modal */ async respond(instance:ODModalResponderInstance, source:Source, params:Params){ diff --git a/src/core/api/modules/startscreen.ts b/src/core/api/modules/startscreen.ts index 80c106d..92b5a60 100644 --- a/src/core/api/modules/startscreen.ts +++ b/src/core/api/modules/startscreen.ts @@ -7,8 +7,17 @@ import { ODFlag } from "./flag" import { ODPlugin, ODUnknownCrashedPlugin } from "./plugin" import ansis from "ansis" +/**## ODStartScreenComponentRenderCallback `type` + * This is the render function of a startscreen component. It also sends the location of where the component is rendered. + */ export type ODStartScreenComponentRenderCallback = (location:number) => string|Promise +/**## ODStartScreenManager `class` + * This is an open ticket startscreen manager. + * + * This class is responsible for managing & rendering the startscreen of the bot. + * The startscreen is the part you see when the bot has started up successfully. (e.g. the Open Ticket logo, logs, livestatus, flags, ...) + */ export class ODStartScreenManager extends ODManager { /**Alias to the Open Ticket debugger. */ #debug: ODDebugger @@ -47,10 +56,22 @@ export class ODStartScreenManager extends ODManager { } } +/**## ODStartScreenComponent `class` + * This is an open ticket startscreen component. + * + * This component can be rendered to the start screen of the bot. + * An optional priority can be specified to choose the location of the component. + * + * It's recommended to use pre-built components except if you really need a custom one. + */ export class ODStartScreenComponent extends ODManagerData { + /**The priority of this component. */ priority: number + /**An optional render function which will be inserted before the default renderer. */ renderBefore: ODStartScreenComponentRenderCallback|null = null + /**The render function which will render the contents of this component. */ render: ODStartScreenComponentRenderCallback + /**An optional render function which will be inserted behind the default renderer. */ renderAfter: ODStartScreenComponentRenderCallback|null = null constructor(id:ODValidId, priority:number, render:ODStartScreenComponentRenderCallback){ @@ -59,6 +80,7 @@ export class ODStartScreenComponent extends ODManagerData { this.render = render } + /**Render this component and combine it with the `renderBefore` & `renderAfter` contents. */ async renderAll(location:number){ const textBefore = (this.renderBefore) ? await this.renderBefore(location) : "" const text = await this.render(location) @@ -67,15 +89,30 @@ export class ODStartScreenComponent extends ODManagerData { } } +/**## ODStartScreenProperty `type` + * This interface contains properties used in a few default templates of the startscreen component. + */ export interface ODStartScreenProperty { + /**The key or name of this property. */ key:string, + /**The value or contents of this property. */ value:string } +/**## ODStartScreenLogoComponent `class` + * This is an open ticket startscreen logo component. + * + * This component will render an ASCII art logo (from an array) to the startscreen. Every property in the array is another row. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenLogoComponent extends ODStartScreenComponent { + /**The ASCII logo contents. */ logo: string[] + /**When enabled, the component will add a new line above the logo. */ topPadding: boolean + /**When enabled, the component will add a new line below the logo. */ bottomPadding: boolean + /**The color of the logo in hex format. */ logoHexColor: string constructor(id:ODValidId, priority:number, logo:string[], topPadding?:boolean, bottomPadding?:boolean, logoHexColor?:string){ @@ -92,14 +129,28 @@ export class ODStartScreenLogoComponent extends ODStartScreenComponent { } } +/**## ODStartScreenHeaderAlignmentSettings `type` + * This interface contains all settings used in the startscreen header component. + */ export interface ODStartScreenHeaderAlignmentSettings { + /**The alignment settings for this header. */ align:"center"|"left"|"right", + /**The width or component to use when calculating center & right alignment. */ width:number|ODStartScreenComponent } +/**## ODStartScreenHeaderComponent `class` + * This is an open ticket startscreen header component. + * + * This component will render a header to the startscreen. Properties can be aligned left, right or centered. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenHeaderComponent extends ODStartScreenComponent { + /**All properties of this header component. */ properties: ODStartScreenProperty[] + /**The spacer used between properties. */ spacer: string + /**The alignment settings of this header component. */ align: ODStartScreenHeaderAlignmentSettings|null constructor(id:ODValidId, priority:number, properties:ODStartScreenProperty[], spacer?:string, align?:ODStartScreenHeaderAlignmentSettings){ @@ -140,8 +191,16 @@ export class ODStartScreenHeaderComponent extends ODStartScreenComponent { } } +/**## ODStartScreenCategoryComponent `class` + * This is an open ticket startscreen category component. + * + * This component will render a category to the startscreen. This will only render the category name. You'll need to provide your own renderer for the contents. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenCategoryComponent extends ODStartScreenComponent { + /**The name of this category. */ name: string + /**When enabled, this category will still be rendered when the contents are empty. (enabled by default) */ renderIfEmpty: boolean constructor(id:ODValidId, priority:number, name:string, render:ODStartScreenComponentRenderCallback, renderIfEmpty?:boolean){ @@ -156,8 +215,16 @@ export class ODStartScreenCategoryComponent extends ODStartScreenComponent { } } +/**## ODStartScreenPropertiesCategoryComponent `class` + * This is an open ticket startscreen properties category component. + * + * This component will render a properties category to the startscreen. This will list the properties in the category. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenPropertiesCategoryComponent extends ODStartScreenCategoryComponent { + /**The properties of this category component. */ properties: ODStartScreenProperty[] + /**The hex color for the key/name of all the properties. */ propertyHexColor: string constructor(id:ODValidId, priority:number, name:string, properties:ODStartScreenProperty[], propertyHexColor?:string, renderIfEmpty?:boolean){ @@ -170,7 +237,14 @@ export class ODStartScreenPropertiesCategoryComponent extends ODStartScreenCateg } } +/**## ODStartScreenFlagsCategoryComponent `class` + * This is an open ticket startscreen flags category component. + * + * This component will render a flags category to the startscreen. This will list the enabled flags in the category. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenFlagsCategoryComponent extends ODStartScreenCategoryComponent { + /**A list of all flags to render. */ flags: ODFlag[] constructor(id:ODValidId, priority:number, flags:ODFlag[]){ @@ -181,8 +255,16 @@ export class ODStartScreenFlagsCategoryComponent extends ODStartScreenCategoryCo } } +/**## ODStartScreenPluginsCategoryComponent `class` + * This is an open ticket startscreen plugins category component. + * + * This component will render a plugins category to the startscreen. This will list the enabled, disabled & crashed plugins in the category. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenPluginsCategoryComponent extends ODStartScreenCategoryComponent { + /**A list of all plugins to render. */ plugins: ODPlugin[] + /**A list of all crashed plugins to render. */ unknownCrashedPlugins: ODUnknownCrashedPlugin[] constructor(id:ODValidId, priority:number, plugins:ODPlugin[], unknownCrashedPlugins:ODUnknownCrashedPlugin[]){ @@ -200,7 +282,14 @@ export class ODStartScreenPluginsCategoryComponent extends ODStartScreenCategory } } +/**## ODStartScreenLiveStatusCategoryComponent `class` + * This is an open ticket startscreen livestatus category component. + * + * This component will render a livestatus category to the startscreen. This will list the livestatus messages in the category. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenLiveStatusCategoryComponent extends ODStartScreenCategoryComponent { + /**A reference to the Open Ticket livestatus manager. */ livestatus: ODLiveStatusManager constructor(id:ODValidId, priority:number, livestatus:ODLiveStatusManager){ @@ -212,6 +301,12 @@ export class ODStartScreenLiveStatusCategoryComponent extends ODStartScreenCateg } } +/**## ODStartScreenLogsCategoryComponent `class` + * This is an open ticket startscreen logs category component. + * + * This component will render a logs category to the startscreen. This will only render the logs category name. + * An optional priority can be specified to choose the location of the component. + */ export class ODStartScreenLogCategoryComponent extends ODStartScreenCategoryComponent { constructor(id:ODValidId, priority:number){ super(id,priority,"logs",() => "",true) diff --git a/src/core/api/modules/stat.ts b/src/core/api/modules/stat.ts index fb91120..ef22771 100644 --- a/src/core/api/modules/stat.ts +++ b/src/core/api/modules/stat.ts @@ -6,10 +6,31 @@ import { ODDebugger } from "./console" import { ODDatabase, ODJsonDatabaseStructure } from "./database" import * as discord from "discord.js" +/**## ODValidStatValue `type` + * These are the only allowed types for a stat value to improve compatibility with different database systems. + */ export type ODValidStatValue = string|number|boolean + +/**## ODStatsManagerInitCallback `type` + * This callback can be used to execute something when the stats have been initiated. + * + * By default this is used to clear stats from users that left the server or tickets which don't exist anymore. + */ export type ODStatsManagerInitCallback = (database:ODJsonDatabaseStructure, deletables:ODJsonDatabaseStructure) => void|Promise + +/**## ODStatScopeSetMode `type` + * This type contains all valid methods for changing the value of a stat. + */ export type ODStatScopeSetMode = "set"|"increase"|"decrease" +/**## ODStatsManager `class` + * This is an open ticket stats manager. + * + * This class is responsible for managing all stats of the bot. + * Stats are categorized in "scopes" which can be accessed in this manager. + * + * Stats can be accessed in the individual scopes. + */ export class ODStatsManager extends ODManager { /**Alias to open ticket debugger. */ #debug: ODDebugger @@ -23,6 +44,7 @@ export class ODStatsManager extends ODManager { this.#debug = debug } + /**Select the database to use to read/write all stats from/to. */ useDatabase(database:ODDatabase){ this.database = database } @@ -31,6 +53,7 @@ export class ODStatsManager extends ODManager { if (this.database) data.useDatabase(this.database) return super.add(data,overwrite) } + /**Init all stats and run `onInit()` listeners. */ async init(){ if (!this.database) throw new ODSystemError("Unable to initialize stats scopes due to missing database!") @@ -58,6 +81,7 @@ export class ODStatsManager extends ODManager { await this.database.delete(data.category,data.key) } } + /**Reset all stats. (clears the entire database) */ async reset(){ if (!this.database) return const data = await this.database.getAll() @@ -66,11 +90,20 @@ export class ODStatsManager extends ODManager { await this.database.delete(d.category,d.key) } } + /**Run a function when the stats are initialized. This can be used to clear stats from users that left the server or tickets which don't exist anymore. */ onInit(callback:ODStatsManagerInitCallback){ this.#initListeners.push(callback) } } +/**## ODStatScope `class` + * This is an open ticket stat scope. + * + * A scope can contain multiple stats. Every scope is seperated from other scopes. + * Here, you can read & write the values of all stats. + * + * The built-in Open Ticket scopes are: `global`, `user`, `ticket` + */ export class ODStatScope extends ODManager { /**The id of this statistics scope. */ id: ODId @@ -87,9 +120,11 @@ export class ODStatScope extends ODManager { this.name = name } + /**Select the database to use to read/write all stats from/to. (Automatically assigned when used in `ODStatsManager`) */ useDatabase(database:ODDatabase){ this.database = database } + /**Get the value of a statistic. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */ async getStat(id:ODValidId, scopeId:string): Promise { if (!this.database) return null const newId = new ODId(id) @@ -105,6 +140,7 @@ export class ODStatScope extends ODManager { //return null on error return null } + /**Set, increase or decrease the value of a statistic. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */ async setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise { if (!this.database) return false const stat = this.get(id) @@ -122,6 +158,7 @@ export class ODStatScope extends ODManager { } return true } + /**Reset the value of a statistic to the initial value. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */ async resetStat(id:ODValidId, scopeId:string): Promise { if (!this.database) return null const stat = this.get(id) @@ -129,11 +166,13 @@ export class ODStatScope extends ODManager { if (stat.value != null) await this.database.set(this.id.value+"_"+stat.id.value,scopeId,stat.value) return stat.value } + /**Initialize this stat scope & return a list of all statistic ids in the following format: `_` */ init(): string[] { //get all valid stats categories this.ready = true return this.getAll().map((stat) => this.id.value+"_"+stat.id.value) } + /**Render all stats in this scope for usage in a discord message/embed. */ async render(scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User): Promise { //sort from high priority to low const derefArray = [...this.getAll()] @@ -162,6 +201,14 @@ export class ODStatScope extends ODManager { } } +/**## ODStatGlobalScope `class` + * This is an open ticket stat global scope. + * + * A scope can contain multiple stats. Every scope is seperated from other scopes. + * Here, you can read & write the values of all stats. + * + * This scope is made specifically for the global stats of Open Ticket. + */ export class ODStatGlobalScope extends ODStatScope { getStat(id:ODValidId): Promise { return super.getStat(id,"GLOBAL") @@ -177,11 +224,25 @@ export class ODStatGlobalScope extends ODStatScope { } } +/**## ODStatRenderer `type` + * This callback will render a single statistic for a discord embed/message. + */ export type ODStatRenderer = (value:ODValidStatValue, scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User) => string|Promise +/**## ODStat `class` + * This is an open ticket statistic. + * + * This single statistic doesn't do anything except defining the rules of this statistic. + * Use it in a stats scope to register a new statistic. A statistic can also include a priority to choose the render priority. + * + * It's recommended to use the `ODBasicStat` & `ODDynamicStat` classes instead of this one! + */ export class ODStat extends ODManagerData { + /**The priority of this statistic. */ priority: number + /**The render function of this statistic. */ render: ODStatRenderer + /**The value of this statistic. */ value: ODValidStatValue|null constructor(id:ODValidId, priority:number, render:ODStatRenderer, value?:ODValidStatValue){ @@ -192,7 +253,14 @@ export class ODStat extends ODManagerData { } } +/**## ODBasicStat `class` + * This is an open ticket basic statistic. + * + * This single statistic will store a number, boolean or string in the database. + * Use it to create a simple statistic for any stats scope. + */ export class ODBasicStat extends ODStat { + /**The name of this stat. Rendered in discord embeds/messages. */ name: string constructor(id:ODValidId, priority:number, name:string, value:ODValidStatValue){ @@ -203,8 +271,19 @@ export class ODBasicStat extends ODStat { } } +/**## ODDynamicStatRenderer `type` + * This callback will render a single dynamic statistic for a discord embed/message. + */ export type ODDynamicStatRenderer = (scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User) => string|Promise +/**## ODDynamicStat `class` + * This is an open ticket dynamic statistic. + * + * A dynamic statistic does not store anything in the database! Instead, it will execute a function to return a custom result. + * This can be used to show statistics which are not stored in the database. + * + * This is used in Open Ticket for the live ticket status, participants & system status. + */ export class ODDynamicStat extends ODStat { constructor(id:ODValidId, priority:number, render:ODDynamicStatRenderer){ super(id,priority,(value,scopeId,guild,channel,user) => { diff --git a/src/core/api/openticket/option.ts b/src/core/api/openticket/option.ts index 9e717fc..c75838f 100644 --- a/src/core/api/openticket/option.ts +++ b/src/core/api/openticket/option.ts @@ -276,16 +276,10 @@ export class ODRoleOption extends ODOption { } export class ODOptionSuffixManager extends ODManager { - /**Alias to open ticket global database. */ - database: ODDatabase|null = null - constructor(debug:ODDebugger){ super(debug,"ticket suffix") } - useDatabase(database:ODDatabase){ - this.database = database - } /**Instantly get the suffix from an option. */ getSuffixFromOption(option:ODTicketOption,user:discord.User): string|null { const suffix = this.getAll().find((suffix) => suffix.option.id.value == option.id.value)