Merge branch 'dev-v4.1.0' into dev
This commit is contained in:
@@ -289,6 +289,16 @@ export class ODCheckerFunctionManager extends ODManager<ODCheckerFunction> {
|
||||
*/
|
||||
export type ODCheckerLocationTrace = (string|number)[]
|
||||
|
||||
/**## ODCheckerOptions `interface`
|
||||
* This interface contains all optional properties to customise in the `ODChecker` class.
|
||||
*/
|
||||
export interface ODCheckerOptions {
|
||||
/**The name of this config in the Interactive Setup CLI. */
|
||||
cliDisplayName?:string
|
||||
/**The description of this config in the Interactive Setup CLI. */
|
||||
cliDisplayDescription?:string
|
||||
}
|
||||
|
||||
/**## ODChecker `class`
|
||||
* This is an Open Ticket config checker.
|
||||
*
|
||||
@@ -310,13 +320,16 @@ export class ODChecker extends ODManagerData {
|
||||
messages: ODCheckerMessage[] = []
|
||||
/**Temporary storage for the quit status from the check() method (not recommended to use) */
|
||||
quit: boolean = false
|
||||
/**All additional properties of this config checker. */
|
||||
options: ODCheckerOptions
|
||||
|
||||
constructor(id:ODValidId, storage: ODCheckerStorage, priority:number, config:ODConfig, structure: ODCheckerStructure){
|
||||
constructor(id:ODValidId, storage: ODCheckerStorage, priority:number, config:ODConfig, structure:ODCheckerStructure, options?:ODCheckerOptions){
|
||||
super(id)
|
||||
this.storage = storage
|
||||
this.priority = priority
|
||||
this.config = config
|
||||
this.structure = structure
|
||||
this.options = options ?? {}
|
||||
}
|
||||
|
||||
/**Run this checker. Returns all errors*/
|
||||
@@ -391,7 +404,15 @@ export interface ODCheckerStructureOptions {
|
||||
/**Add a custom checker function. Returns `true` when valid. */
|
||||
custom?:(checker:ODChecker, value:ODValidJsonType, locationTrace:ODCheckerLocationTrace, locationId:ODId, locationDocs:string|null) => boolean,
|
||||
/**Set the url to the documentation of this variable. */
|
||||
docs?:string
|
||||
docs?:string,
|
||||
/**The name of this config in the Interactive Setup CLI. */
|
||||
cliDisplayName?:string
|
||||
/**The description of this config in the Interactive Setup CLI. */
|
||||
cliDisplayDescription?:string
|
||||
/**Hide the description of this config in the Interactive Setup CLI parent view/list. */
|
||||
cliHideDescriptionInParent?:boolean
|
||||
/**The default value of this variable when creating it in the Interactive Setup CLI. When not specified, the user will be asked to insert a value. */
|
||||
cliInitDefaultValue?:ODValidJsonType
|
||||
}
|
||||
|
||||
/**## ODCheckerStructure `class`
|
||||
@@ -426,7 +447,13 @@ export class ODCheckerStructure {
|
||||
*/
|
||||
export interface ODCheckerObjectStructureOptions extends ODCheckerStructureOptions {
|
||||
/**Add a checker for a property in an object (can also be optional) */
|
||||
children?:{key:string, checker:ODCheckerStructure, priority:number, optional:boolean}[]
|
||||
children:{key:string, priority:number, optional:boolean, cliHideInEditMode?:boolean, checker:ODCheckerStructure}[],
|
||||
/**A list of keys to skip when creating this object with the Interactive Setup CLI. The default value of these properties will be used instead. */
|
||||
cliInitSkipKeys?:string[],
|
||||
/**The key of a (primitive) property in this object to show the value of in the Interactive Setup CLI when listed in an array. */
|
||||
cliDisplayKeyInParentArray?:string,
|
||||
/**A list of additional (primitive) property keys in this object to show the value of in the Interactive Setup CLI when listed in an array. */
|
||||
cliDisplayAdditionalKeysInParentArray?:string[]
|
||||
}
|
||||
|
||||
/**## ODCheckerObjectStructure `class`
|
||||
@@ -500,7 +527,11 @@ export interface ODCheckerStringStructureOptions extends ODCheckerStructureOptio
|
||||
/**You need to choose between ... */
|
||||
choices?:string[],
|
||||
/**The string needs to match this regex */
|
||||
regex?:RegExp
|
||||
regex?:RegExp,
|
||||
/**Provide an optional list for autocomplete when using the Interactive Setup CLI. Defaults to the `choices` option. */
|
||||
cliAutocompleteList?:string[],
|
||||
/**Dynamically provide a list for autocomplete items when using the Interactive Setup CLI. */
|
||||
cliAutocompleteFunc?:() => Promise<string[]|null>
|
||||
}
|
||||
|
||||
/**## ODCheckerStringStructure `class`
|
||||
@@ -718,7 +749,9 @@ export interface ODCheckerArrayStructureOptions extends ODCheckerStructureOption
|
||||
/**Allow double values (only for `string`, `number` & `boolean`) */
|
||||
allowDoubles?:boolean
|
||||
/**Only allow these types in the array (for multi-type propertyCheckers) */
|
||||
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
|
||||
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[],
|
||||
/**The name of the properties inside this array. Used in the GUI of the Interactive Setup CLI. */
|
||||
cliDisplayPropertyName?:string
|
||||
}
|
||||
|
||||
/**## ODCheckerArrayStructure `class`
|
||||
@@ -863,15 +896,15 @@ export interface ODCheckerTypeSwitchStructureOptions extends ODCheckerStructureO
|
||||
/**A checker when the property is a boolean */
|
||||
boolean?:ODCheckerBooleanStructure,
|
||||
/**A checker when the property is null */
|
||||
null?:ODCheckerStructure,
|
||||
null?:ODCheckerNullStructure,
|
||||
/**A checker when the property is an array */
|
||||
array?:ODCheckerStructure,
|
||||
array?:ODCheckerArrayStructure,
|
||||
/**A checker when the property is an object */
|
||||
object?:ODCheckerObjectStructure,
|
||||
/**A checker when the property is something else */
|
||||
other?:ODCheckerStructure,
|
||||
/**A list of allowed types */
|
||||
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
|
||||
allowedTypes:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
|
||||
}
|
||||
|
||||
/**## ODCheckerTypeSwitchStructure `class`
|
||||
@@ -925,9 +958,9 @@ export class ODCheckerTypeSwitchStructure extends ODCheckerStructure {
|
||||
*/
|
||||
export interface ODCheckerObjectSwitchStructureOptions extends ODCheckerStructureOptions {
|
||||
/**An array of object checkers with their name, properties & priority. */
|
||||
objects?:{
|
||||
objects:{
|
||||
/**The properties to match for this checker to be used. */
|
||||
properties:{key:string, value:any}[],
|
||||
properties:{key:string, value:boolean|string|number}[],
|
||||
/**The name for this object type (used in rendering) */
|
||||
name:string,
|
||||
/**The higher the priority, the earlier this checker will be tested. */
|
||||
@@ -993,11 +1026,11 @@ export class ODCheckerObjectSwitchStructure extends ODCheckerStructure {
|
||||
*/
|
||||
export interface ODCheckerEnabledObjectStructureOptions extends ODCheckerStructureOptions {
|
||||
/**The name of the property to match the `enabledValue`. */
|
||||
property?:string,
|
||||
/**The value of the property to be enabled. Defaults to `true` */
|
||||
enabledValue?:any,
|
||||
property:string,
|
||||
/**The value of the property to be enabled. (e.g. `true`) */
|
||||
enabledValue:boolean|string|number,
|
||||
/**The object checker to use once the property has been matched. */
|
||||
checker?:ODCheckerObjectStructure
|
||||
checker:ODCheckerObjectStructure
|
||||
}
|
||||
|
||||
/**## ODCheckerEnabledObjectStructure `class`
|
||||
@@ -1387,35 +1420,29 @@ export class ODCheckerCustomStructure_UniqueIdArray extends ODCheckerArrayStruct
|
||||
/**The scope to push unique ids when used in this array! */
|
||||
readonly usedScope: string|null
|
||||
|
||||
constructor(id:ODValidId, source:string, scope:string, usedScope?:string, options?:ODCheckerArrayStructureOptions){
|
||||
constructor(id:ODValidId, source:string, scope:string, usedScope?:string, options?:ODCheckerArrayStructureOptions, idOptions?:Omit<ODCheckerStringStructureOptions,"minLength"|"custom">){
|
||||
//add premade custom structure checker
|
||||
const newOptions = options ?? {}
|
||||
newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
|
||||
const lt = checker.locationTraceDeref(locationTrace)
|
||||
newOptions.propertyChecker = new ODCheckerStringStructure("opendiscord:unique-id",{...(idOptions ?? {}),minLength:1,custom:(checker,value,locationTrace,locationId,locationDocs) => {
|
||||
if (typeof value != "string") return false
|
||||
const localLt = checker.locationTraceDeref(locationTrace)
|
||||
localLt.pop()
|
||||
|
||||
if (!Array.isArray(value)) return false
|
||||
const uniqueArray: string[] = (checker.storage.get(source,scope) === null) ? [] : checker.storage.get(source,scope)
|
||||
|
||||
let localQuit = false
|
||||
value.forEach((id,index) => {
|
||||
if (typeof id != "string") return
|
||||
const localLt = checker.locationTraceDeref(lt)
|
||||
localLt.push(index)
|
||||
if (uniqueArray.includes(id)){
|
||||
//exists
|
||||
if (usedScope){
|
||||
const current: string[] = checker.storage.get(source,usedScope) ?? []
|
||||
current.push(id)
|
||||
checker.storage.set(source,usedScope,current)
|
||||
}
|
||||
}else{
|
||||
//doesn't exist
|
||||
checker.createMessage("opendiscord:id-non-existent","error",`The id "${id}" doesn't exist!`,localLt,null,[`"${id}"`],this.id,(this.options.docs ?? null))
|
||||
localQuit = true
|
||||
const uniqueArray: string[] = checker.storage.get(source,scope) ?? []
|
||||
if (uniqueArray.includes(value)){
|
||||
//exists
|
||||
if (usedScope){
|
||||
const current: string[] = checker.storage.get(source,usedScope) ?? []
|
||||
current.push(value)
|
||||
checker.storage.set(source,usedScope,current)
|
||||
}
|
||||
})
|
||||
return !localQuit
|
||||
}
|
||||
return true
|
||||
}else{
|
||||
//doesn't exist
|
||||
checker.createMessage("opendiscord:id-non-existent","error",`The id "${value}" doesn't exist!`,localLt,null,[`"${value}"`],locationId,locationDocs)
|
||||
return false
|
||||
}
|
||||
}})
|
||||
super(id,newOptions)
|
||||
this.source = source
|
||||
this.scope = scope
|
||||
|
||||
@@ -114,7 +114,7 @@ export class ODClientManager {
|
||||
this.#debug.debug("Created client with permissions: "+this.permissions.join(", "))
|
||||
}
|
||||
/**Get all servers the bot is part of. */
|
||||
getGuilds(){
|
||||
async getGuilds(): Promise<discord.Guild[]> {
|
||||
if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
|
||||
if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
|
||||
|
||||
@@ -135,8 +135,8 @@ export class ODClientManager {
|
||||
})
|
||||
}
|
||||
}
|
||||
/**Log-in with a discord auth token. */
|
||||
login(): Promise<boolean> {
|
||||
/**Log-in with a discord auth token. Rejects returns `false` using 'softErrors' on failure. */
|
||||
login(softErrors?:boolean): Promise<boolean> {
|
||||
return new Promise(async (resolve,reject) => {
|
||||
if (!this.initiated) reject("Client isn't initiated yet!")
|
||||
if (!this.token) reject("Client doesn't have a token!")
|
||||
@@ -158,7 +158,8 @@ export class ODClientManager {
|
||||
this.#debug.debug("Finished discord.js client.login()")
|
||||
this.loggedIn = true
|
||||
}catch(err){
|
||||
if (err.message.toLowerCase().includes("used disallowed intents")){
|
||||
if (softErrors) return resolve(false)
|
||||
else if (err.message.toLowerCase().includes("used disallowed intents")){
|
||||
process.emit("uncaughtException",new ODSystemError("Used disallowed intents"))
|
||||
}else if (err.message.toLowerCase().includes("tokeninvalid") || err.message.toLowerCase().includes("an invalid token was provided")){
|
||||
process.emit("uncaughtException",new ODSystemError("Invalid discord bot token provided"))
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ODId, ODManager, ODManagerData, ODPromiseVoid, ODSystemError, ODValidId
|
||||
import nodepath from "path"
|
||||
import { ODDebugger } from "./console"
|
||||
import fs from "fs"
|
||||
import * as fjs from "formatted-json-stringify"
|
||||
|
||||
/**## ODConfigManager `class`
|
||||
* This is an Open Ticket config manager.
|
||||
@@ -14,8 +15,17 @@ import fs from "fs"
|
||||
* You can use this class to get/change/add a config file (`ODConfig`) in your plugin!
|
||||
*/
|
||||
export class ODConfigManager extends ODManager<ODConfig> {
|
||||
/**Alias to Open Ticket debugger. */
|
||||
#debug: ODDebugger
|
||||
|
||||
constructor(debug:ODDebugger){
|
||||
super(debug,"config")
|
||||
this.#debug = debug
|
||||
}
|
||||
add(data:ODConfig|ODConfig[],overwrite?:boolean): boolean {
|
||||
if (Array.isArray(data)) data.forEach((d) => d.useDebug(this.#debug))
|
||||
else data.useDebug(this.#debug)
|
||||
return super.add(data,overwrite)
|
||||
}
|
||||
/**Init all config files. */
|
||||
async init(){
|
||||
@@ -42,15 +52,45 @@ export class ODConfig extends ODManagerData {
|
||||
path: string = ""
|
||||
/**An object/array of the entire config file! Variables inside it can be edited while the bot is running! */
|
||||
data: any
|
||||
/**Is this config already initiated? */
|
||||
initiated: boolean = false
|
||||
/**An array of listeners to run when the config gets reloaded. These are not executed on the initial loading. */
|
||||
protected reloadListeners: Function[] = []
|
||||
/**Alias to Open Ticket debugger. */
|
||||
protected debug: ODDebugger|null = null
|
||||
|
||||
constructor(id:ODValidId, data:any){
|
||||
super(id)
|
||||
this.data = data
|
||||
}
|
||||
|
||||
/**Use the Open Ticket debugger for logs. */
|
||||
useDebug(debug:ODDebugger|null){
|
||||
this.debug = debug
|
||||
}
|
||||
/**Init the config. */
|
||||
init(): ODPromiseVoid {
|
||||
//nothing
|
||||
this.initiated = true
|
||||
if (this.debug) this.debug.debug("Initiated config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
|
||||
//please implement this feature in your own config extension & extend this function.
|
||||
}
|
||||
/**Reload the config. Be aware that this doesn't update the config data everywhere in the bot! */
|
||||
reload(): ODPromiseVoid {
|
||||
if (this.debug) this.debug.debug("Reloaded config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
|
||||
//please implement this feature in your own config extension & extend this function.
|
||||
}
|
||||
/**Save the edited config to the filesystem. This is used by the Interactive Setup CLI. It's not recommended to use this while the bot is running. */
|
||||
save(): ODPromiseVoid {
|
||||
if (this.debug) this.debug.debug("Saved config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
|
||||
//please implement this feature in your own config extension & extend this function.
|
||||
}
|
||||
/**Listen for a reload of this JSON file! */
|
||||
onReload(cb:Function){
|
||||
this.reloadListeners.push(cb)
|
||||
}
|
||||
/**Remove all reload listeners. Not recommended! */
|
||||
removeAllReloadListeners(){
|
||||
this.reloadListeners = []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +105,13 @@ export class ODConfig extends ODManagerData {
|
||||
* const config = new api.ODJsonConfig("plugin-config","test.json","./plugins/testplugin/")
|
||||
*/
|
||||
export class ODJsonConfig extends ODConfig {
|
||||
/**An array of listeners to run when the config gets reloaded. These are not executed on the initial loading. */
|
||||
#reloadListeners: Function[] = []
|
||||
formatter: fjs.custom.BaseFormatter
|
||||
|
||||
constructor(id:ODValidId, file:string, customPath?:string){
|
||||
constructor(id:ODValidId, file:string, customPath?:string, formatter?:fjs.custom.BaseFormatter){
|
||||
super(id,{})
|
||||
this.file = (file.endsWith(".json")) ? file : file+".json"
|
||||
this.path = customPath ? nodepath.join("./",customPath,this.file) : nodepath.join("./config/",this.file)
|
||||
this.formatter = formatter ?? new fjs.DefaultFormatter(null,true," ")
|
||||
}
|
||||
|
||||
/**Init the config. */
|
||||
@@ -79,17 +119,20 @@ export class ODJsonConfig extends ODConfig {
|
||||
if (!fs.existsSync(this.path)) throw new ODSystemError("Unable to parse config \""+nodepath.join("./",this.path)+"\", the file doesn't exist!")
|
||||
try{
|
||||
this.data = JSON.parse(fs.readFileSync(this.path).toString())
|
||||
super.init()
|
||||
}catch(err){
|
||||
process.emit("uncaughtException",err)
|
||||
throw new ODSystemError("Unable to parse config \""+nodepath.join("./",this.path)+"\"!")
|
||||
}
|
||||
}
|
||||
/**Reload the JSON file. Be aware that this doesn't update classes that used individual parts of the config data! */
|
||||
/**Reload the config. Be aware that this doesn't update the config data everywhere in the bot! */
|
||||
reload(){
|
||||
if (!this.initiated) throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\", the file hasn't been initiated yet!")
|
||||
if (!fs.existsSync(this.path)) throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\", the file doesn't exist!")
|
||||
try{
|
||||
this.data = JSON.parse(fs.readFileSync(this.path).toString())
|
||||
this.#reloadListeners.forEach((cb) => {
|
||||
super.reload()
|
||||
this.reloadListeners.forEach((cb) => {
|
||||
try{
|
||||
cb()
|
||||
}catch(err){
|
||||
@@ -101,12 +144,16 @@ export class ODJsonConfig extends ODConfig {
|
||||
throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\"!")
|
||||
}
|
||||
}
|
||||
/**Listen for a reload of this JSON file! */
|
||||
onReload(cb:Function){
|
||||
this.#reloadListeners.push(cb)
|
||||
}
|
||||
/**Remove all reload listeners. Not recommended! */
|
||||
removeAllReloadListeners(){
|
||||
this.#reloadListeners = []
|
||||
/**Save the edited config to the filesystem. This is used by the Interactive Setup CLI. It's not recommended to use this while the bot is running. */
|
||||
save(): ODPromiseVoid {
|
||||
if (!this.initiated) throw new ODSystemError("Unable to save config \""+nodepath.join("./",this.path)+"\", the file hasn't been initiated yet!")
|
||||
try{
|
||||
const contents = this.formatter.stringify(this.data)
|
||||
fs.writeFileSync(this.path,contents)
|
||||
super.save()
|
||||
}catch(err){
|
||||
process.emit("uncaughtException",err)
|
||||
throw new ODSystemError("Unable to save config \""+nodepath.join("./",this.path)+"\"!")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,6 +241,8 @@ export class ODConsoleManager {
|
||||
historylength = 100
|
||||
/**An alias to the debugfile manager. (`otdebug.txt`) */
|
||||
debugfile: ODDebugFileManager
|
||||
/**Is silent mode enabled? */
|
||||
silent: boolean = false
|
||||
|
||||
constructor(historylength:number, debugfile:ODDebugFileManager){
|
||||
this.historylength = historylength
|
||||
@@ -253,12 +255,12 @@ export class ODConsoleManager {
|
||||
log(message:string, type?:ODConsoleMessageTypes, params?:ODConsoleMessageParam[]): void
|
||||
log(message:ODConsoleMessage|ODError|string, type?:ODConsoleMessageTypes, params?:ODConsoleMessageParam[]){
|
||||
if (message instanceof ODConsoleMessage){
|
||||
message.render()
|
||||
if (!this.silent) message.render()
|
||||
if (this.debugfile) this.debugfile.writeConsoleMessage(message)
|
||||
this.history.push(message)
|
||||
|
||||
}else if (message instanceof ODError){
|
||||
message.render()
|
||||
if (!this.silent) message.render()
|
||||
if (this.debugfile) this.debugfile.writeErrorMessage(message)
|
||||
this.history.push(message)
|
||||
|
||||
@@ -272,7 +274,7 @@ export class ODConsoleManager {
|
||||
else if (type == "error") newMessage = new ODConsoleErrorMessage(message,params)
|
||||
else newMessage = new ODConsoleSystemMessage(message,params)
|
||||
|
||||
newMessage.render()
|
||||
if (!this.silent) newMessage.render()
|
||||
if (this.debugfile) this.debugfile.writeConsoleMessage(newMessage)
|
||||
this.history.push(newMessage)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ export interface ODDefaults {
|
||||
crashOnError:boolean,
|
||||
/**Enable the system responsible for the `--debug` flag. */
|
||||
debugLoading:boolean,
|
||||
/**Enable the system responsible for the `--silent` flag. */
|
||||
silentLoading:boolean,
|
||||
/**When enabled, you're able to use the "!OPENTICKET:dump" command to send the OT debug file. This is only possible when you're the owner of the bot. */
|
||||
allowDumpCommand:boolean,
|
||||
/**Enable loading all Open Ticket plugins, sadly enough is only useful for the system :) */
|
||||
@@ -233,6 +235,7 @@ export class ODDefaultsManager {
|
||||
errorHandling:true,
|
||||
crashOnError:false,
|
||||
debugLoading:true,
|
||||
silentLoading:true,
|
||||
allowDumpCommand:true,
|
||||
pluginLoading:true,
|
||||
softPluginLoading:false,
|
||||
|
||||
Reference in New Issue
Block a user