@open-discord-bots/framework 0.3.14 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/api/main.js +1 -1
  2. package/dist/api/modules/responder.js +50 -26
  3. package/package.json +1 -1
  4. package/src/api/index.ts +0 -31
  5. package/src/api/main.ts +0 -203
  6. package/src/api/modules/action.ts +0 -89
  7. package/src/api/modules/base.ts +0 -845
  8. package/src/api/modules/builder.ts +0 -1755
  9. package/src/api/modules/checker.ts +0 -1826
  10. package/src/api/modules/client.ts +0 -2345
  11. package/src/api/modules/code.ts +0 -84
  12. package/src/api/modules/component.ts +0 -2000
  13. package/src/api/modules/config.ts +0 -264
  14. package/src/api/modules/console.ts +0 -697
  15. package/src/api/modules/cooldown.ts +0 -369
  16. package/src/api/modules/database.ts +0 -321
  17. package/src/api/modules/event.ts +0 -123
  18. package/src/api/modules/flag.ts +0 -99
  19. package/src/api/modules/fuse.ts +0 -365
  20. package/src/api/modules/helpmenu.ts +0 -273
  21. package/src/api/modules/language.ts +0 -230
  22. package/src/api/modules/permission.ts +0 -363
  23. package/src/api/modules/plugin.ts +0 -294
  24. package/src/api/modules/post.ts +0 -137
  25. package/src/api/modules/progressbar.ts +0 -370
  26. package/src/api/modules/responder.ts +0 -1625
  27. package/src/api/modules/session.ts +0 -181
  28. package/src/api/modules/startscreen.ts +0 -345
  29. package/src/api/modules/state.ts +0 -298
  30. package/src/api/modules/statistic.ts +0 -380
  31. package/src/api/modules/verifybar.ts +0 -68
  32. package/src/api/modules/worker.ts +0 -119
  33. package/src/cli/editConfig.ts +0 -930
  34. package/src/cli/index.ts +0 -152
  35. package/src/index.ts +0 -8
  36. package/src/startup/compilation.ts +0 -204
  37. package/src/startup/dump.ts +0 -46
  38. package/src/startup/errorHandling.ts +0 -42
  39. package/src/startup/pluginLauncher.ts +0 -265
  40. package/src/utilities/index.ts +0 -229
  41. package/tools/cleanup.js +0 -2
@@ -1,1826 +0,0 @@
1
- ///////////////////////////////////////
2
- //CONFIG CHECKER MODULE
3
- ///////////////////////////////////////
4
- import { ODDiscordIdType, ODId, ODManager, ODManagerData, ODNoGeneric, ODValidId, ODValidJsonType } from "./base.js"
5
- import { ODConfig, ODMemoryConfig } from "./config.js"
6
- import { ODLanguageManager } from "./language.js"
7
- import { ODDebugger } from "./console.js"
8
- import ansis from "ansis"
9
-
10
- /**## ODCheckerResult `interface`
11
- * This interface is the result from a config checker check() function.
12
- */
13
- export interface ODCheckerResult {
14
- valid:boolean
15
- messages:ODCheckerMessage[]
16
- }
17
-
18
- /**## ODCheckerManagerIdConstraint `type`
19
- * The constraint/layout for id mappings/interfaces of the `ODCheckerManager` class.
20
- */
21
- export type ODCheckerManagerIdConstraint = Record<string,ODChecker>
22
-
23
- /**## ODCheckerManager `class`
24
- * This is an Open Discord checker manager.
25
- *
26
- * It manages all config checkers in the bot and allows plugins to access config checkers from Open Discord & other plugins!
27
- *
28
- * You can use this class to get/add a config checker (`ODChecker`) in your plugin!
29
- */
30
- export class ODCheckerManager<
31
- IdList extends ODCheckerManagerIdConstraint = ODCheckerManagerIdConstraint,
32
- FunctionIdList extends ODCheckerFunctionManagerIdConstraint = ODCheckerFunctionManagerIdConstraint,
33
- Renderer extends ODCheckerRenderer = ODCheckerRenderer,
34
- TranslationMessageIds extends string = string,
35
- TranslationOtherIds extends string = string
36
- > extends ODManager<ODChecker> {
37
- /**The global temporary storage shared between all config checkers. */
38
- storage: ODCheckerStorage
39
- /**The class responsible for rendering the config checker report. */
40
- renderer: Renderer
41
- /**The class responsible for translating the config checker report. */
42
- translation: ODCheckerTranslationRegister<TranslationMessageIds,TranslationOtherIds>
43
- /**Final functions are global functions executed just before the report is created. */
44
- functions: ODCheckerFunctionManager<FunctionIdList>
45
- /**A variable containing the last result returned from `checkAll()` */
46
- lastResult: ODCheckerResult|null = null
47
-
48
- constructor(debug:ODDebugger, storage:ODCheckerStorage, renderer:Renderer, translation:ODCheckerTranslationRegister<TranslationMessageIds,TranslationOtherIds>, functions:ODCheckerFunctionManager<FunctionIdList>){
49
- super(debug,"config checker")
50
- this.storage = storage
51
- this.renderer = renderer
52
- this.translation = translation
53
- this.functions = functions
54
- }
55
- /**Check all config checkers registered in this manager.*/
56
- checkAll(sort:boolean): ODCheckerResult {
57
- this.storage.reset()
58
-
59
- let isValid = true
60
- const final: ODCheckerMessage[] = []
61
-
62
- const checkers = this.getAll()
63
- checkers.sort((a,b) => b.priority-a.priority)
64
-
65
- checkers.forEach((checker) => {
66
- const res = checker.check()
67
- final.push(...res.messages)
68
-
69
- if (!res.valid) isValid = false
70
- })
71
-
72
- this.functions.getAll().forEach((func) => {
73
- const res = func.func(this,this.functions)
74
- final.push(...res.messages)
75
-
76
- if (!res.valid) isValid = false
77
- })
78
-
79
- //sort messages => (info, warning, error)
80
- if (sort) final.sort((a,b) => {
81
- const typeA = (a.type == "error") ? 2 : (a.type == "warning") ? 1 : 0
82
- const typeB = (b.type == "error") ? 2 : (b.type == "warning") ? 1 : 0
83
-
84
- return typeA-typeB
85
- })
86
-
87
- this.lastResult = {
88
- valid:isValid,
89
- messages:final
90
- }
91
-
92
- return {
93
- valid:isValid,
94
- messages:final
95
- }
96
- }
97
- /**Create temporary and unlisted `ODConfig`, `ODChecker` & `ODCheckerStorage` classes. This will help you use a `ODCheckerStructure` validator without officially registering it in `opendiscord.checkers`. */
98
- createTemporaryCheckerEnvironment(){
99
- return new ODChecker("opendiscord:temporary-environment",new ODCheckerStorage(),0,new ODMemoryConfig("opendiscord:temporary-environment",{}),new ODCheckerStructure("opendiscord:temporary-environment",{}))
100
- }
101
-
102
- get<CheckerId extends keyof ODNoGeneric<IdList>>(id:CheckerId): IdList[CheckerId]
103
- get(id:ODValidId): ODChecker|null
104
-
105
- get(id:ODValidId): ODChecker|null {
106
- return super.get(id)
107
- }
108
-
109
- remove<CheckerId extends keyof ODNoGeneric<IdList>>(id:CheckerId): IdList[CheckerId]
110
- remove(id:ODValidId): ODChecker|null
111
-
112
- remove(id:ODValidId): ODChecker|null {
113
- return super.remove(id)
114
- }
115
-
116
- exists(id:keyof ODNoGeneric<IdList>): boolean
117
- exists(id:ODValidId): boolean
118
-
119
- exists(id:ODValidId): boolean {
120
- return super.exists(id)
121
- }
122
- }
123
-
124
- /**## ODCheckerStorage `class`
125
- * This is an Open Discord checker storage.
126
- *
127
- * It stores temporary data to share between config checkers!
128
- * (e.g. The `messages.json` needs to access the `"id"` from `options.json`)
129
- *
130
- *
131
- * You can use this class when you create your own config checker implementation! (not required for using the built-in config checker)
132
- */
133
- export class ODCheckerStorage {
134
- /**This is the array that stores all the data. ❌ **(don't edit unless really needed!)***/
135
- storage: {source:ODId, key:string, value:any}[] = []
136
-
137
- /**Get data from the database (`source` => id of `ODChecker`) */
138
- get(source:ODValidId, key:string): any|null {
139
- const result = this.storage.find(d => (d.source.value == new ODId(source).value) && (d.key == key))
140
- return (result) ? result.value : null
141
- }
142
- /**Add data to the database (`source` => id of `ODChecker`). This function also overwrites existing data!*/
143
- set(source:ODValidId, key:string, value:any){
144
- const index = this.storage.findIndex(d => (d.source.value == new ODId(source).value) && (d.key == key))
145
- if (index > -1){
146
- //overwrite
147
- this.storage[index] = {
148
- source:new ODId(source),
149
- key,value
150
- }
151
- return true
152
- }else{
153
- this.storage.push({
154
- source:new ODId(source),
155
- key,value
156
- })
157
- return false
158
- }
159
- }
160
- /**Delete data from the database (`source` => id of `ODChecker`) */
161
- delete(source:ODValidId, key:string){
162
- const index = this.storage.findIndex(d => (d.source.value == new ODId(source).value) && (d.key == key))
163
- if (index > -1){
164
- //delete
165
- this.storage.splice(index,1)
166
- return true
167
- }else return false
168
- }
169
-
170
- /**Reset the entire database */
171
- reset(){
172
- this.storage = []
173
- }
174
- }
175
-
176
- /**## ODCheckerRenderer `class`
177
- * This is an Open Discord checker renderer.
178
- *
179
- * It's responsible for rendering the config checker result in the console.
180
- * This class doesn't provide any components! Create new ones by extending this class.
181
- *
182
- * Use this class to change the config checker looks!
183
- */
184
- export abstract class ODCheckerRenderer {
185
- /**Get all config checker render components. These can be combined and rendered to the console. */
186
- abstract getComponents(compact:boolean, renderEmpty:boolean, translation:ODCheckerTranslationRegister<string,string>, data:ODCheckerResult): string[]
187
-
188
- /**Render all config checker render components to the console. */
189
- render(components:string[]){
190
- if (components.length < 1) return
191
- console.log("\n")
192
- components.forEach((c) => {
193
- console.log(c)
194
- })
195
- console.log("\n")
196
- }
197
- }
198
-
199
- /**## ODDefaultCheckerRendererTranslations `interface`
200
- * The required translation sentences for the default checker renderer.
201
- */
202
- export interface ODDefaultCheckerRendererTranslations {
203
- /**Example: `OPEN DISCORD`*/
204
- headerProjectName:string,
205
- /**Example: `CONFIG CHECKER`*/
206
- headerConfigchecker:string,
207
- /**Example: `check for errors in your config files!`*/
208
- headerDescription:string,
209
- /**Example: `the bot won't start until all {0}'s are fixed!`*/
210
- footerError:string,
211
- /**Example: `it's recommended to fix all {0}'s before starting!`*/
212
- footerWarning:string,
213
- /**Example: `SUPPORT: {0} - DOCS: {1}`*/
214
- footerSupport:string,
215
- /**Example: `[ERROR]`*/
216
- error:string,
217
- /**Example: `[WARNING]`*/
218
- warning:string,
219
- /**Example: `[INFO]`*/
220
- info:string,
221
- /**Example: `use {0} for more information!`*/
222
- compactInfo:string,
223
- /**Example: `path`*/
224
- dataPath:string,
225
- /**Example: `docs`*/
226
- dataDocs:string,
227
- /**Example: `message`*/
228
- dataMessage:string
229
- }
230
-
231
- /**## ODDefaultCheckerRenderer `class`
232
- * This is the default render class which renders the config checkers in the console for most Open Discord projects.
233
- *
234
- * This class can be found in the global variable `opendiscord.checkers.renderer`!
235
- */
236
- export class ODDefaultCheckerRenderer extends ODCheckerRenderer {
237
- /**The main color used when rendering the config checker. */
238
- mainColor: `#${string}`
239
- /**The url to the discord support server. */
240
- supportUrl: string
241
- /**The url to the bot documentation. */
242
- docsUrl: string
243
-
244
- /**Add additional header text. */
245
- extraHeaderText: string[] = []
246
- /**Add additional footer text. */
247
- extraFooterText: string[] = []
248
- /**Add additional top text. */
249
- extraTopText: string[] = []
250
- /**Add additional bottom text. */
251
- extraBottomText: string[] = []
252
-
253
- /**Set the character used for horizontal lines. */
254
- horizontalFiller: string = "="
255
- /**Set the character used for vertical lines. */
256
- verticalFiller: string = "|"
257
- /**Set the prefix used for the description. */
258
- descriptionSeparator: string = " => "
259
- /**Set the prefix used for the header.. */
260
- headerSeparator: string = " => "
261
- /**Set the prefix used for the footer. */
262
- footerTipPrefix: string = "=> "
263
-
264
- /**Disable rendering the header. */
265
- disableHeader: boolean = false
266
- /**Disable rendering the footer. */
267
- disableFooter: boolean = false
268
-
269
- constructor(mainColor:`#${string}`,supportUrl:string,docsUrl:string){
270
- super()
271
- this.mainColor = mainColor
272
- this.supportUrl = supportUrl
273
- this.docsUrl = docsUrl
274
- }
275
-
276
- getComponents(compact:boolean, renderEmpty:boolean, translation:ODCheckerTranslationRegister<string,string>, data:ODCheckerResult): string[] {
277
- const tm = translation
278
- const t: ODDefaultCheckerRendererTranslations = {
279
- headerProjectName:tm.get("other","opendiscord:header-projectname") ?? "OPEN DISCORD",
280
- headerConfigchecker:tm.get("other","opendiscord:header-configchecker") ?? "CONFIG CHECKER",
281
- headerDescription:tm.get("other","opendiscord:header-description") ?? "check for errors in your config files!",
282
- footerError:tm.get("other","opendiscord:footer-error") ?? "the bot won't start until all {0}'s are fixed!",
283
- footerWarning:tm.get("other","opendiscord:footer-warning") ?? "it's recommended to fix all {0}'s before starting!",
284
- footerSupport:tm.get("other","opendiscord:footer-support") ?? "SUPPORT: {0} - DOCS: {1}",
285
- error:tm.get("other","opendiscord:type-error") ?? "[ERROR]",
286
- warning:tm.get("other","opendiscord:type-warning") ?? "[WARNING]",
287
- info:tm.get("other","opendiscord:type-info") ?? "[INFO]",
288
- compactInfo:tm.get("other","opendiscord:compact-information") ?? "use {0} for more information!",
289
- dataPath:tm.get("other","opendiscord:data-path") ?? "path",
290
- dataDocs:tm.get("other","opendiscord:data-docs") ?? "docs",
291
- dataMessage:tm.get("other","opendiscord:data-message") ?? "message"
292
- }
293
- const hasErrors = data.messages.filter((m) => m.type == "error").length > 0
294
- const hasWarnings = data.messages.filter((m) => m.type == "warning").length > 0
295
- const hasInfo = data.messages.filter((m) => m.type == "info").length > 0
296
-
297
- if (!renderEmpty && !hasErrors && !hasWarnings && (!hasInfo || compact)) return []
298
-
299
- const headerText = ansis.bold.hex(this.mainColor)(t.headerProjectName)+" "+t.headerConfigchecker+this.headerSeparator+ansis.hex(this.mainColor)(t.headerDescription)
300
- const footerErrorText = (hasErrors) ? this.footerTipPrefix+ansis.gray(tm.insertTranslationParams(t.footerError,[ansis.bold.red(t.error)])) : ""
301
- const footerWarningText = (hasWarnings) ? this.footerTipPrefix+ansis.gray(tm.insertTranslationParams(t.footerWarning,[ansis.bold.yellow(t.warning)])) : ""
302
- const footerSupportText = tm.insertTranslationParams(t.footerSupport,[ansis.green(this.supportUrl),ansis.green(this.docsUrl)])
303
- const bottomCompactInfo = (compact) ? ansis.gray(tm.insertTranslationParams(t.compactInfo,[ansis.bold.green("npm start -- --checker")])) : ""
304
-
305
- const finalHeader = [headerText,...this.extraHeaderText]
306
- const finalFooter = [footerErrorText,footerWarningText,footerSupportText,...this.extraFooterText]
307
- const finalTop = [...this.extraTopText]
308
- const finalBottom = [bottomCompactInfo,...this.extraBottomText]
309
- const borderLength = this.getLongestLength([...finalHeader,...finalFooter])
310
-
311
- const finalComponents: string[] = []
312
-
313
- //header
314
- if (!this.disableHeader){
315
- finalHeader.forEach((text) => {
316
- if (text.length < 1) return
317
- finalComponents.push(this.createBlockFromText(text,borderLength))
318
- })
319
- }
320
- finalComponents.push(this.getHorizontalDivider(borderLength+4))
321
-
322
- //top
323
- finalTop.forEach((text) => {
324
- if (text.length < 1) return
325
- finalComponents.push(this.verticalFiller+" "+text)
326
- })
327
- finalComponents.push(this.verticalFiller)
328
-
329
- //messages
330
- if (compact){
331
- //use compact messages
332
- data.messages.forEach((msg,index) => {
333
- //compact mode doesn't render info
334
- if (msg.type == "info") return
335
-
336
- //check if translation available & use it if possible
337
- const rawTranslation = tm.get("message",msg.messageId.value)
338
- const translatedMessage = (rawTranslation) ? tm.insertTranslationParams(rawTranslation,msg.translationParams) : msg.message
339
-
340
- if (msg.type == "error") finalComponents.push(this.verticalFiller+" "+ansis.bold.red(`${t.error} ${translatedMessage}`))
341
- else if (msg.type == "warning") finalComponents.push(this.verticalFiller+" "+ansis.bold.yellow(`${t.warning} ${translatedMessage}`))
342
-
343
- const pathSplitter = msg.path ? ":" : ""
344
- finalComponents.push(this.verticalFiller+ansis.bold(this.descriptionSeparator)+ansis.cyan(`${ansis.magenta(msg.filepath+pathSplitter)} ${msg.path}`))
345
- if (index != data.messages.length-1) finalComponents.push(this.verticalFiller)
346
- })
347
- }else{
348
- //use full messages
349
- data.messages.forEach((msg,index) => {
350
- //check if translation available & use it if possible
351
- const rawTranslation = tm.get("message",msg.messageId.value)
352
- const translatedMessage = (rawTranslation) ? tm.insertTranslationParams(rawTranslation,msg.translationParams) : msg.message
353
-
354
- if (msg.type == "error") finalComponents.push(this.verticalFiller+" "+ansis.bold.red(`${t.error} ${translatedMessage}`))
355
- else if (msg.type == "warning") finalComponents.push(this.verticalFiller+" "+ansis.bold.yellow(`${t.warning} ${translatedMessage}`))
356
- else if (msg.type == "info") finalComponents.push(this.verticalFiller+" "+ansis.bold.blue(`${t.info} ${translatedMessage}`))
357
-
358
- const pathSplitter = msg.path ? ":" : ""
359
- finalComponents.push(this.verticalFiller+" "+ansis.bold((t.dataPath)+this.descriptionSeparator)+ansis.cyan(`${ansis.magenta(msg.filepath+pathSplitter)} ${msg.path}`))
360
- if (msg.locationDocs) finalComponents.push(this.verticalFiller+" "+ansis.bold(t.dataDocs+this.descriptionSeparator)+ansis.italic.gray(msg.locationDocs))
361
- if (msg.messageDocs) finalComponents.push(this.verticalFiller+" "+ansis.bold(t.dataMessage+this.descriptionSeparator)+ansis.italic.gray(msg.messageDocs))
362
- if (index != data.messages.length-1) finalComponents.push(this.verticalFiller)
363
- })
364
- }
365
-
366
- //bottom
367
- finalComponents.push(this.verticalFiller)
368
- finalBottom.forEach((text) => {
369
- if (text.length < 1) return
370
- finalComponents.push(this.verticalFiller+" "+text)
371
- })
372
-
373
- //footer
374
- finalComponents.push(this.getHorizontalDivider(borderLength+4))
375
- if (!this.disableFooter){
376
- finalFooter.forEach((text) => {
377
- if (text.length < 1) return
378
- finalComponents.push(this.createBlockFromText(text,borderLength))
379
- })
380
- finalComponents.push(this.getHorizontalDivider(borderLength+4))
381
- }
382
-
383
- //return all components
384
- return finalComponents
385
- }
386
- /**Get the length of the longest string in the array. */
387
- private getLongestLength(texts:string[]): number {
388
- return Math.max(...texts.map((t) => ansis.strip(t).length))
389
- }
390
- /**Get a horizontal divider used between different parts of the config checker result. */
391
- private getHorizontalDivider(width:number): string {
392
- if (width > 2) width = width-2
393
- else return this.verticalFiller+this.verticalFiller
394
- let divider = this.verticalFiller + this.horizontalFiller.repeat(width) + this.verticalFiller
395
- return divider
396
- }
397
- /**Create a block of text with a vertical divider on the left & right side. */
398
- private createBlockFromText(text:string,width:number): string {
399
- if (width < 3) return this.verticalFiller+this.verticalFiller
400
- let newWidth = width-ansis.strip(text).length+1
401
- let final = this.verticalFiller+" "+text+" ".repeat(newWidth)+this.verticalFiller
402
- return final
403
- }
404
- }
405
-
406
- /**## ODCheckerTranslationRegister `class`
407
- * This is an Open Discord checker translation register.
408
- *
409
- * It's used to store & manage the translation for each message from the config checker!
410
- * Most translations are stored by message id, but there are some exceptions like the additional text on the checker report.
411
- *
412
- * You can use this class if you want to translate your config checker messages! **This is optional & isn't required for the checker to work!**
413
- */
414
- export class ODCheckerTranslationRegister<MessageIds extends string = string, OtherIds extends string = string> {
415
- /**This is the array that stores all the data. ❌ **(don't edit unless really needed!)***/
416
- protected translations: {type:"message"|"other", id:string, translation:string}[] = []
417
-
418
- /**Get the translation from a config checker message/sentence */
419
- get(type:"other", id:OtherIds): string
420
- get(type:"message", id:MessageIds): string
421
- get(type:"message"|"other", id:string): string|null
422
- get(type:"message"|"other", id:string): string|null {
423
- const result = this.translations.find(d => (d.id == id) && (d.type == type))
424
- return (result) ? result.translation : null
425
- }
426
- /**Set the translation for a config checker message/sentence. This function also overwrites existing translations!*/
427
- set(type:"other", id:OtherIds, translation:string): boolean
428
- set(type:"message", id:MessageIds, translation:string): boolean
429
- set(type:"message"|"other", id:string, translation:string): boolean
430
- set(type:"message"|"other", id:string, translation:string){
431
- const index = this.translations.findIndex(d => (d.id == id) && (d.type == type))
432
- if (index > -1){
433
- //overwrite
434
- this.translations[index] = {type,id,translation}
435
- return true
436
- }else{
437
- this.translations.push({type,id,translation})
438
- return false
439
- }
440
- }
441
- /**Delete the translation for a config checker message/sentence. */
442
- delete(type:"other", id:OtherIds): boolean
443
- delete(type:"message", id:MessageIds): boolean
444
- delete(type:"message"|"other", id:string): boolean
445
- delete(type:"message"|"other", id:string){
446
- const index = this.translations.findIndex(d => (d.id == id) && (d.type == type))
447
- if (index > -1){
448
- //delete
449
- this.translations.splice(index,1)
450
- return true
451
- }else return false
452
- }
453
- /**Get all translations */
454
- getAll(){
455
- return this.translations
456
- }
457
- /**Insert the translation params into the text. */
458
- insertTranslationParams(text:string, translationParams:string[]){
459
- translationParams.forEach((value,index) => {
460
- text = text.replace(`{${index}}`,value)
461
- })
462
- return text
463
- }
464
- /**A shortcut to copy translations from the `ODLanguageManager` to `ODCheckerTranslationRegister` */
465
- quickTranslate(manager:ODLanguageManager, translationId:string, type:"other"|"message", id:OtherIds|MessageIds): void
466
- quickTranslate(manager:ODLanguageManager, translationId:string, type:"other"|"message", id:string): void
467
- quickTranslate(manager:ODLanguageManager, translationId:string, type:"other"|"message", id:string){
468
- const translation = manager.getTranslation(translationId)
469
- if (translation) this.set(type,id,translation)
470
- }
471
- }
472
-
473
- /**## ODCheckerFunctionCallback `type`
474
- * This is the function used in the `ODCheckerFunction` class.
475
- */
476
- export type ODCheckerFunctionCallback = (manager:ODCheckerManager<ODCheckerManagerIdConstraint,ODCheckerFunctionManagerIdConstraint,ODCheckerRenderer,string,string>, functions:ODCheckerFunctionManager<ODCheckerFunctionManagerIdConstraint>) => ODCheckerResult
477
-
478
- /**## ODCheckerFunction `class`
479
- * This is an Open Discord config checker function.
480
- *
481
- * It is a global function that will be executed after all config checkers. It can do additional checks for invalid/missing configurations.
482
- * It's mostly used for things that need to be checked globally!
483
- */
484
- export class ODCheckerFunction extends ODManagerData {
485
- /**The function which will be executed globally after all config checkers. */
486
- func: ODCheckerFunctionCallback
487
-
488
- constructor(id:ODValidId, func:ODCheckerFunctionCallback){
489
- super(id)
490
- this.func = func
491
- }
492
- }
493
-
494
- /**## ODCheckerFunctionManagerIdConstraint `type`
495
- * The constraint/layout for id mappings/interfaces of the `ODCheckerFunctionManager` class.
496
- */
497
- export type ODCheckerFunctionManagerIdConstraint = Record<string,ODCheckerFunction>
498
-
499
- /**## ODCheckerFunctionManager `class`
500
- * This is an Open Discord config checker function manager.
501
- *
502
- * It manages all `ODCheckerFunction`'s and it has some extra shortcuts for frequently used methods.
503
- */
504
- export class ODCheckerFunctionManager<IdList extends ODCheckerFunctionManagerIdConstraint = ODCheckerFunctionManagerIdConstraint> extends ODManager<ODCheckerFunction> {
505
- constructor(debug:ODDebugger){
506
- super(debug,"config checker function")
507
- }
508
-
509
- /**A shortcut to create a warning, info or error message */
510
- createMessage(checkerId:ODValidId, id:ODValidId, filepath:string, type:"info"|"warning"|"error", message:string, locationTrace:ODCheckerLocationTrace, docs:string|null, translationParams:string[], locationId:ODId, locationDocs:string|null): ODCheckerMessage {
511
- return {
512
- checkerId:new ODId(checkerId),
513
- messageId:new ODId(id),
514
- locationId,
515
-
516
- type,message,
517
- path:this.locationTraceToString(locationTrace),
518
- filepath,
519
- translationParams,
520
-
521
- messageDocs:docs,
522
- locationDocs
523
- }
524
- }
525
- /**Create a string from the location trace (path)*/
526
- locationTraceToString(trace:ODCheckerLocationTrace){
527
- const final: ODCheckerLocationTrace = []
528
- trace.forEach((t) => {
529
- if (typeof t == "number"){
530
- final.push(`:${t}`)
531
- }else{
532
- final.push(`."${t}"`)
533
- }
534
- })
535
- return final.join("").substring(1)
536
- }
537
- /**De-reference the locationTrace array. Use this before adding a value to the array*/
538
- locationTraceDeref(trace:ODCheckerLocationTrace): ODCheckerLocationTrace {
539
- return JSON.parse(JSON.stringify(trace))
540
- }
541
-
542
- get<CheckerFunctionId extends keyof ODNoGeneric<IdList>>(id:CheckerFunctionId): IdList[CheckerFunctionId]
543
- get(id:ODValidId): ODCheckerFunction|null
544
-
545
- get(id:ODValidId): ODCheckerFunction|null {
546
- return super.get(id)
547
- }
548
-
549
- remove<CheckerFunctionId extends keyof ODNoGeneric<IdList>>(id:CheckerFunctionId): IdList[CheckerFunctionId]
550
- remove(id:ODValidId): ODCheckerFunction|null
551
-
552
- remove(id:ODValidId): ODCheckerFunction|null {
553
- return super.remove(id)
554
- }
555
-
556
- exists(id:keyof ODNoGeneric<IdList>): boolean
557
- exists(id:ODValidId): boolean
558
-
559
- exists(id:ODValidId): boolean {
560
- return super.exists(id)
561
- }
562
- }
563
-
564
- /**## ODCheckerLocationTrace `type`
565
- * This type is an array of strings & numbers which represents the location trace from the config checker.
566
- * It's used to generate a path to the error (e.g. `"abc"."efg".1."something"`)
567
- */
568
- export type ODCheckerLocationTrace = (string|number)[]
569
-
570
- /**## ODCheckerOptions `interface`
571
- * This interface contains all optional properties to customise in the `ODChecker` class.
572
- */
573
- export interface ODCheckerOptions {
574
- /**The name of this config in the Interactive Setup CLI. */
575
- cliDisplayName?:string
576
- /**The description of this config in the Interactive Setup CLI. */
577
- cliDisplayDescription?:string
578
- }
579
-
580
- /**## ODChecker `class`
581
- * This is an Open Discord config checker.
582
- *
583
- * It checks a specific config file for invalid/missing configurations. This data can then be used to show to the user what's wrong!
584
- * You can check for example if a string is longer/shorter than a certain amount of characters & more!
585
- *
586
- * You can use this class when you create your own custom config file & you want to check it for syntax errors.
587
- */
588
- export class ODChecker extends ODManagerData {
589
- /**The storage of this checker (reference for `ODCheckerManager.storage`) */
590
- storage: ODCheckerStorage
591
- /**The higher the priority, the faster it gets checked! */
592
- priority: number
593
- /**The config file that needs to be checked */
594
- config: ODConfig<any>
595
- /**The structure of the config file */
596
- structure: ODCheckerStructure
597
- /**Temporary storage for all error messages from the check() method (not recommended to use) */
598
- messages: ODCheckerMessage[] = []
599
- /**Temporary storage for the quit status from the check() method (not recommended to use) */
600
- quit: boolean = false
601
- /**All additional properties of this config checker. */
602
- options: ODCheckerOptions
603
-
604
- constructor(id:ODValidId, storage: ODCheckerStorage, priority:number, config:ODConfig<any>, structure:ODCheckerStructure, options?:ODCheckerOptions){
605
- super(id)
606
- this.storage = storage
607
- this.priority = priority
608
- this.config = config
609
- this.structure = structure
610
- this.options = options ?? {}
611
- }
612
-
613
- /**Get a human-readable number string. */
614
- protected ordinalNumber(num:number){
615
- const i = Math.abs(Math.round(num))
616
- const cent = i % 100
617
- if (cent >= 10 && cent <= 20) return i+'th'
618
- const dec = i % 10
619
- if (dec === 1) return i+'st'
620
- if (dec === 2) return i+'nd'
621
- if (dec === 3) return i+'rd'
622
- return i+'th'
623
- }
624
- /**Run this checker. Returns all errors*/
625
- check(): ODCheckerResult {
626
- this.messages = []
627
- this.quit = false
628
-
629
- this.structure.check(this,this.config.data,[])
630
- return {
631
- valid:!this.quit,
632
- messages:this.messages
633
- }
634
- }
635
- /**Create a string from the location trace/path in a human readable format. */
636
- locationTraceToString(trace:ODCheckerLocationTrace){
637
- const final: ODCheckerLocationTrace = []
638
- trace.forEach((t) => {
639
- if (typeof t == "number"){
640
- final.push(`:(${this.ordinalNumber(t+1)})`)
641
- }else{
642
- final.push(`."${t}"`)
643
- }
644
- })
645
- return final.join("").substring(1)
646
- }
647
- /**De-reference the locationTrace array. Use this before adding a value to the array*/
648
- locationTraceDeref(trace:ODCheckerLocationTrace): ODCheckerLocationTrace {
649
- return JSON.parse(JSON.stringify(trace))
650
- }
651
-
652
- /**A shortcut to create a warning, info or error message */
653
- createMessage(id:ODValidId, type:"info"|"warning"|"error", message:string, locationTrace:ODCheckerLocationTrace, docs:string|null, translationParams:string[], locationId:ODId, locationDocs:string|null){
654
- if (type == "error") this.quit = true
655
- this.messages.push({
656
- checkerId:this.id,
657
- messageId:new ODId(id),
658
- locationId,
659
-
660
- type,message,
661
- path:this.locationTraceToString(locationTrace),
662
- filepath:this.config.path,
663
- translationParams,
664
-
665
- messageDocs:docs,
666
- locationDocs
667
- })
668
- }
669
- }
670
-
671
- /**## ODCheckerMessage `interface`
672
- * This interface is an object which has all variables required for a config checker message!
673
- */
674
- export interface ODCheckerMessage {
675
- checkerId:ODId,
676
- messageId:ODId,
677
- locationId:ODId,
678
-
679
- type:"info"|"warning"|"error",
680
- message:string,
681
- path:string,
682
- filepath:string,
683
- translationParams:string[],
684
-
685
- messageDocs:string|null,
686
- locationDocs:string|null
687
- }
688
-
689
- /**## ODCheckerStructureOptions `interface`
690
- * This interface has the basic options for the `ODCheckerStructure`!
691
- */
692
- export interface ODCheckerStructureOptions {
693
- /**Add a custom checker function. Returns `true` when valid. */
694
- custom?:(checker:ODChecker, value:ODValidJsonType, locationTrace:ODCheckerLocationTrace, locationId:ODId, locationDocs:string|null) => boolean,
695
- /**Set the url to the documentation of this variable. */
696
- docs?:string,
697
- /**The name of this config in the Interactive Setup CLI. */
698
- cliDisplayName?:string
699
- /**The description of this config in the Interactive Setup CLI. */
700
- cliDisplayDescription?:string
701
- /**Hide the description of this config in the Interactive Setup CLI parent view/list. */
702
- cliHideDescriptionInParent?:boolean
703
- /**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. */
704
- cliInitDefaultValue?:ODValidJsonType
705
- }
706
-
707
- /**## ODCheckerStructure `class`
708
- * This is an Open Discord config checker structure.
709
- *
710
- * This class will check for a single variable in a config file, customise it in the settings!
711
- * If you want prebuilt checkers (for strings, booleans, numbers, ...), check the other `ODCheckerStructure`'s!
712
- *
713
- * **Not recommended to use!** It's recommended to extend from another `ODConfigCheckerStructure` class!
714
- */
715
- export class ODCheckerStructure {
716
- /**The id of this checker structure */
717
- id: ODId
718
- /**The options for this checker structure */
719
- options: ODCheckerStructureOptions
720
-
721
- constructor(id:ODValidId, options:ODCheckerStructureOptions){
722
- this.id = new ODId(id)
723
- this.options = options
724
- }
725
-
726
- /**Check a variable if it matches all settings in this checker. This function is automatically executed by Open Discord! */
727
- check(checker:ODChecker, value:ODValidJsonType, locationTrace:ODCheckerLocationTrace): boolean {
728
- if (typeof this.options.custom != "undefined"){
729
- return this.options.custom(checker,value,locationTrace,this.id,(this.options.docs ?? null))
730
- }else return true
731
- }
732
- }
733
-
734
- /**## ODCheckerObjectStructureOptions `interface`
735
- * This interface has the options for `ODCheckerObjectStructure`!
736
- */
737
- export interface ODCheckerObjectStructureOptions extends ODCheckerStructureOptions {
738
- /**Add a checker for a property in an object (can also be optional) */
739
- children:{key:string, priority?:number, optional?:boolean, cliHideInEditMode?:boolean, checker:ODCheckerStructure}[],
740
- /**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. */
741
- cliInitSkipKeys?:string[],
742
- /**The key of a (primitive) property in this object to show the value of in the Interactive Setup CLI when listed in an array. */
743
- cliDisplayKeyInParentArray?:string,
744
- /**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. */
745
- cliDisplayAdditionalKeysInParentArray?:string[]
746
- }
747
-
748
- /**## ODCheckerObjectStructure `class`
749
- * This is an Open Discord config checker structure.
750
- *
751
- * This class will check for an object variable in a config file, customise it in the settings!
752
- * A checker for the children can be set in the settings.
753
- */
754
- export class ODCheckerObjectStructure extends ODCheckerStructure {
755
- declare options: ODCheckerObjectStructureOptions
756
-
757
- constructor(id:ODValidId, options:ODCheckerObjectStructureOptions){
758
- super(id,options)
759
- }
760
-
761
- check(checker:ODChecker, value:Record<string,any>, locationTrace:ODCheckerLocationTrace): boolean {
762
- const lt = checker.locationTraceDeref(locationTrace)
763
-
764
- //check type & options
765
- if (typeof value != "object"){
766
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: object!",lt,null,["object"],this.id,(this.options.docs ?? null))
767
- return false
768
- }
769
-
770
- //sort children
771
- if (typeof this.options.children == "undefined") return super.check(checker,value,locationTrace)
772
- const sortedChildren = this.options.children.sort((a,b) => {
773
- if ((a.priority ?? 0) < (b.priority ?? 0)) return -1
774
- else if ((a.priority ?? 0) > (b.priority ?? 0)) return 1
775
- else return 0
776
- })
777
-
778
- //check children
779
- let localQuit = false
780
- sortedChildren.forEach((child) => {
781
- const localLt = checker.locationTraceDeref(lt)
782
- localLt.push(child.key)
783
-
784
- if (typeof value[child.key] == "undefined"){
785
- if (!child.optional){
786
- localQuit = true
787
- checker.createMessage("opendiscord:property-missing","error",`The property "${child.key}" is mising from this object!`,lt,null,[`"${child.key}"`],this.id,(this.options.docs ?? null))
788
- }else{
789
- checker.createMessage("opendiscord:property-optional","info",`The property "${child.key}" is optional in this object!`,lt,null,[`"${child.key}"`],this.id,(this.options.docs ?? null))
790
- }
791
- }else if (!child.checker.check(checker,value[child.key],localLt)) localQuit = true
792
- })
793
-
794
- //do local quit or check custom function
795
- if (localQuit) return false
796
- else return super.check(checker,value,locationTrace)
797
- }
798
- }
799
-
800
- /**## ODCheckerStringStructureOptions `interface`
801
- * This interface has the options for `ODCheckerStringStructure`!
802
- */
803
- export interface ODCheckerStringStructureOptions extends ODCheckerStructureOptions {
804
- /**The minimum length of this string */
805
- minLength?:number,
806
- /**The maximum length of this string */
807
- maxLength?:number,
808
- /**Set the required length of this string */
809
- length?:number,
810
- /**This string needs to start with ... */
811
- startsWith?:string,
812
- /**This string needs to end with ... */
813
- endsWith?:string,
814
- /**This string needs to contain ... */
815
- contains?:string,
816
- /**This string is not allowed to contain ... */
817
- invertedContains?:string,
818
- /**You need to choose between ... */
819
- choices?:string[],
820
- /**This string needs to be in lowercase. */
821
- lowercaseOnly?:boolean,
822
- /**This string needs to be in uppercase. */
823
- uppercaseOnly?:boolean,
824
- /**This string shouldn't contain any special characters (allowed: A-Z, a-z, 0-9, space, a few punctuation marks, ...). */
825
- noSpecialCharacters?:boolean,
826
- /**Do not allow any spaces in this string. */
827
- withoutSpaces?:boolean,
828
- /**Give a warning when a sentence doesn't start with a capital letter. Or require every word to start with a capital letter. (Ignores numbers, unicode characters, ...) */
829
- capitalLetterWarning?:false|"sentence"|"word"
830
- /**Give a warning when a sentence doesn't end with a punctuation letter (.,?!) */
831
- punctuationWarning?:boolean
832
- /**The string needs to match this regex */
833
- regex?:RegExp,
834
- /**Provide an optional list for autocomplete when using the Interactive Setup CLI. Defaults to the `choices` option. */
835
- cliAutocompleteList?:string[],
836
- /**Dynamically provide a list for autocomplete items when using the Interactive Setup CLI. */
837
- cliAutocompleteFunc?:() => Promise<string[]|null>
838
- }
839
-
840
- /**## ODCheckerStringStructure `class`
841
- * This is an Open Discord config checker structure.
842
- *
843
- * This class will check for a string variable in a config file, customise it in the settings!
844
- */
845
- export class ODCheckerStringStructure extends ODCheckerStructure {
846
- declare options: ODCheckerStringStructureOptions
847
-
848
- constructor(id:ODValidId, options:ODCheckerStringStructureOptions){
849
- super(id,options)
850
- }
851
-
852
- check(checker:ODChecker, value:string, locationTrace:ODCheckerLocationTrace): boolean {
853
- const lt = checker.locationTraceDeref(locationTrace)
854
-
855
- //check type & options
856
- if (typeof value != "string"){
857
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: string!",lt,null,["string"],this.id,(this.options.docs ?? null))
858
- return false
859
- }else if (typeof this.options.minLength != "undefined" && value.length < this.options.minLength){
860
- checker.createMessage("opendiscord:string-too-short","error",`This string can't be shorter than ${this.options.minLength} characters!`,lt,null,[this.options.minLength.toString()],this.id,(this.options.docs ?? null))
861
- return false
862
- }else if (typeof this.options.maxLength != "undefined" && value.length > this.options.maxLength){
863
- checker.createMessage("opendiscord:string-too-long","error",`This string can't be longer than ${this.options.maxLength} characters!`,lt,null,[this.options.maxLength.toString()],this.id,(this.options.docs ?? null))
864
- return false
865
- }else if (typeof this.options.length != "undefined" && value.length !== this.options.length){
866
- checker.createMessage("opendiscord:string-length-invalid","error",`This string needs to be ${this.options.length} characters long!`,lt,null,[this.options.length.toString()],this.id,(this.options.docs ?? null))
867
- return false
868
- }else if (typeof this.options.startsWith != "undefined" && !value.startsWith(this.options.startsWith)){
869
- checker.createMessage("opendiscord:string-starts-with","error",`This string needs to start with "${this.options.startsWith}"!`,lt,null,[`"${this.options.startsWith}"`],this.id,(this.options.docs ?? null))
870
- return false
871
- }else if (typeof this.options.endsWith != "undefined" && !value.endsWith(this.options.endsWith)){
872
- checker.createMessage("opendiscord:string-ends-with","error",`This string needs to end with "${this.options.endsWith}"!`,lt,null,[`"${this.options.endsWith}"`],this.id,(this.options.docs ?? null))
873
- return false
874
- }else if (typeof this.options.contains != "undefined" && !value.includes(this.options.contains)){
875
- checker.createMessage("opendiscord:string-contains","error",`This string needs to contain "${this.options.contains}"!`,lt,null,[`"${this.options.contains}"`],this.id,(this.options.docs ?? null))
876
- return false
877
- }else if (typeof this.options.invertedContains != "undefined" && value.includes(this.options.invertedContains)){
878
- checker.createMessage("opendiscord:string-inverted-contains","error",`This string is not allowed to contain "${this.options.invertedContains}"!`,lt,null,[`"${this.options.invertedContains}"`],this.id,(this.options.docs ?? null))
879
- return false
880
- }else if (typeof this.options.choices != "undefined" && !this.options.choices.includes(value)){
881
- checker.createMessage("opendiscord:string-choices","error",`This string can only be one of the following values: "${this.options.choices.join(`", "`)}"!`,lt,null,[`"${this.options.choices.join(`", "`)}"`],this.id,(this.options.docs ?? null))
882
- return false
883
- }else if (this.options.lowercaseOnly && value !== value.toLowerCase()){
884
- checker.createMessage("opendiscord:string-lowercase","error",`This string must be written in lowercase only!`,lt,null,[],this.id,(this.options.docs ?? null))
885
- return false
886
- }else if (this.options.uppercaseOnly && value !== value.toUpperCase()){
887
- checker.createMessage("opendiscord:string-uppercase","error",`This string must be written in uppercase only!`,lt,null,[],this.id,(this.options.docs ?? null))
888
- return false
889
- }else if (this.options.noSpecialCharacters && !/^[A-Za-z0-9 ]*$/.test(value)){
890
- checker.createMessage("opendiscord:string-special-characters","error",`This string is not allowed to contain any special characters! (a-z, 0-9 & space only)`,lt,null,[],this.id,(this.options.docs ?? null))
891
- return false
892
- }else if (this.options.withoutSpaces && value.includes(" ")){
893
- checker.createMessage("opendiscord:string-no-spaces","error",`This string is not allowed to contain spaces!`,lt,null,[],this.id,(this.options.docs ?? null))
894
- return false
895
- }else if (typeof this.options.regex != "undefined" && !this.options.regex.test(value)){
896
- checker.createMessage("opendiscord:string-regex","error","This string is invalid!",lt,null,[],this.id,(this.options.docs ?? null))
897
- return false
898
- }else{
899
- //warnings
900
- if ((this.options.capitalLetterWarning == "word" && !value.split(" ").every((word) => word.length == 0 || /^[^a-z].*/.test(word)))) checker.createMessage("opendiscord:string-capital-word","warning",`It's recommended that each word in this string starts with a capital letter!`,lt,null,[],this.id,(this.options.docs ?? null))
901
- if ((this.options.capitalLetterWarning == "sentence" && !value.split(/ *[.?!] */).every((sentence) => sentence.length == 0 || /^[^a-z].*/.test(sentence)))) checker.createMessage("opendiscord:string-capital-sentence","warning",`It looks like some sentences in this string don't start with a capital letter!`,lt,null,[],this.id,(this.options.docs ?? null))
902
- if (this.options.punctuationWarning && value.length > 0 && (!value.endsWith(".") && !value.endsWith("?") && !value.endsWith("!") && !value.endsWith("'") && !value.endsWith('"') && !value.endsWith(",") && !value.endsWith(";") && !value.endsWith(":") && !value.endsWith("="))) checker.createMessage("opendiscord:string-punctuation","warning",`It looks like the sentence in this string doesn't end with a punctuation mark!`,lt,null,[],this.id,(this.options.docs ?? null))
903
-
904
- return super.check(checker,value,locationTrace)
905
- }
906
- }
907
- }
908
-
909
- /**## ODCheckerNumberStructureOptions `interface`
910
- * This interface has the options for `ODCheckerNumberStructure`!
911
- */
912
- export interface ODCheckerNumberStructureOptions extends ODCheckerStructureOptions {
913
- /**Is `NaN` (not a number) allowed? (`false` by default) */
914
- nanAllowed?:boolean
915
- /**The minimum length of this number */
916
- minLength?:number,
917
- /**The maximum length of this number */
918
- maxLength?:number,
919
- /**Set the required length of this number */
920
- length?:number,
921
- /**The minimum value of this number */
922
- min?:number,
923
- /**The maximum value of this number */
924
- max?:number,
925
- /**This number is required to match the value */
926
- is?:number,
927
- /**Only allow a multiple of ... starting at `this.offset` or 0 */
928
- step?:number,
929
- /**The offset for the step function. */
930
- offset?:number,
931
- /**This number needs to start with ... */
932
- startsWith?:string,
933
- /**This number needs to end with ... */
934
- endsWith?:string,
935
- /**This number needs to contain ... */
936
- contains?:string,
937
- /**This number is not allowed to contain ... */
938
- invertedContains?:string,
939
- /**You need to choose between ... */
940
- choices?:number[],
941
- /**Are numbers with a decimal value allowed? */
942
- floatAllowed?:boolean,
943
- /**Are negative numbers allowed (without zero) */
944
- negativeAllowed?:boolean,
945
- /**Are positive numers allowed (without zero) */
946
- positiveAllowed?:boolean,
947
- /**Is zero allowed? */
948
- zeroAllowed?:boolean
949
- }
950
-
951
- /**## ODCheckerNumberStructure `class`
952
- * This is an Open Discord config checker structure.
953
- *
954
- * This class will check for a number variable in a config file, customise it in the settings!
955
- */
956
- export class ODCheckerNumberStructure extends ODCheckerStructure {
957
- declare options: ODCheckerNumberStructureOptions
958
-
959
- constructor(id:ODValidId, options:ODCheckerNumberStructureOptions){
960
- super(id,options)
961
- }
962
-
963
- check(checker:ODChecker, value:number, locationTrace:ODCheckerLocationTrace): boolean {
964
- const lt = checker.locationTraceDeref(locationTrace)
965
-
966
- //offset for step
967
- const stepOffset = (typeof this.options.offset != "undefined") ? this.options.offset : 0
968
-
969
- //check type & options
970
- if (typeof value != "number"){
971
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: number!",lt,null,["number"],this.id,(this.options.docs ?? null))
972
- return false
973
- }else if (!this.options.nanAllowed && isNaN(value)){
974
- checker.createMessage("opendiscord:number-nan","error",`This number can't be NaN (Not A Number)!`,lt,null,[],this.id,(this.options.docs ?? null))
975
- return false
976
- }else if (typeof this.options.minLength != "undefined" && value.toString().length < this.options.minLength){
977
- checker.createMessage("opendiscord:number-too-short","error",`This number can't be shorter than ${this.options.minLength} characters!`,lt,null,[this.options.minLength.toString()],this.id,(this.options.docs ?? null))
978
- return false
979
- }else if (typeof this.options.maxLength != "undefined" && value.toString().length > this.options.maxLength){
980
- checker.createMessage("opendiscord:number-too-long","error",`This number can't be longer than ${this.options.maxLength} characters!`,lt,null,[this.options.maxLength.toString()],this.id,(this.options.docs ?? null))
981
- return false
982
- }else if (typeof this.options.length != "undefined" && value.toString().length !== this.options.length){
983
- checker.createMessage("opendiscord:number-length-invalid","error",`This number needs to be ${this.options.length} characters long!`,lt,null,[this.options.length.toString()],this.id,(this.options.docs ?? null))
984
- return false
985
- }else if (typeof this.options.min != "undefined" && value < this.options.min){
986
- checker.createMessage("opendiscord:number-too-small","error",`This number needs to be at least ${this.options.min}!`,lt,null,[this.options.min.toString()],this.id,(this.options.docs ?? null))
987
- return false
988
- }else if (typeof this.options.max != "undefined" && value > this.options.max){
989
- checker.createMessage("opendiscord:number-too-large","error",`This number needs to be at most ${this.options.max}!`,lt,null,[this.options.max.toString()],this.id,(this.options.docs ?? null))
990
- return false
991
- }else if (typeof this.options.is != "undefined" && value == this.options.is){
992
- checker.createMessage("opendiscord:number-not-equal","error",`This number needs to be ${this.options.is}!`,lt,null,[this.options.is.toString()],this.id,(this.options.docs ?? null))
993
- return false
994
- }else if (typeof this.options.step != "undefined" && ((value - stepOffset) % this.options.step) !== 0){
995
- if (stepOffset > 0) checker.createMessage("opendiscord:number-step-offset","error",`This number needs to be a multiple of ${this.options.step} starting with ${stepOffset}!`,lt,null,[this.options.step.toString(),stepOffset.toString()],this.id,(this.options.docs ?? null))
996
- else checker.createMessage("opendiscord:number-step","error",`This number needs to be a multiple of ${this.options.step}!`,lt,null,[this.options.step.toString()],this.id,(this.options.docs ?? null))
997
- return false
998
- }else if (typeof this.options.startsWith != "undefined" && !value.toString().startsWith(this.options.startsWith)){
999
- checker.createMessage("opendiscord:number-starts-with","error",`This number needs to start with "${this.options.startsWith}"!`,lt,null,[`"${this.options.startsWith}"`],this.id,(this.options.docs ?? null))
1000
- return false
1001
- }else if (typeof this.options.endsWith != "undefined" && !value.toString().endsWith(this.options.endsWith)){
1002
- checker.createMessage("opendiscord:number-ends-with","error",`This number needs to end with "${this.options.endsWith}"!`,lt,null,[`"${this.options.endsWith}"`],this.id,(this.options.docs ?? null))
1003
- return false
1004
- }else if (typeof this.options.contains != "undefined" && !value.toString().includes(this.options.contains)){
1005
- checker.createMessage("opendiscord:number-contains","error",`This number needs to contain "${this.options.contains}"!`,lt,null,[`"${this.options.contains}"`],this.id,(this.options.docs ?? null))
1006
- return false
1007
- }else if (typeof this.options.invertedContains != "undefined" && value.toString().includes(this.options.invertedContains)){
1008
- checker.createMessage("opendiscord:number-inverted-contains","error",`This number is not allowed to contain "${this.options.invertedContains}"!`,lt,null,[`"${this.options.invertedContains}"`],this.id,(this.options.docs ?? null))
1009
- return false
1010
- }else if (typeof this.options.choices != "undefined" && !this.options.choices.includes(value)){
1011
- checker.createMessage("opendiscord:number-choices","error",`This number can only be one of the following values: "${this.options.choices.join(`", "`)}"!`,lt,null,[`"${this.options.choices.join(`", "`)}"`],this.id,(this.options.docs ?? null))
1012
- return false
1013
- }else if (typeof this.options.floatAllowed != "undefined" && !this.options.floatAllowed && (value % 1) !== 0){
1014
- checker.createMessage("opendiscord:number-float","error","This number can't be a decimal!",lt,null,[],this.id,(this.options.docs ?? null))
1015
- return false
1016
- }else if (typeof this.options.negativeAllowed != "undefined" && !this.options.negativeAllowed && value < 0){
1017
- checker.createMessage("opendiscord:number-negative","error","This number can't be negative!",lt,null,[],this.id,(this.options.docs ?? null))
1018
- return false
1019
- }else if (typeof this.options.positiveAllowed != "undefined" && !this.options.positiveAllowed && value > 0){
1020
- checker.createMessage("opendiscord:number-positive","error","This number can't be positive!",lt,null,[],this.id,(this.options.docs ?? null))
1021
- return false
1022
- }else if (typeof this.options.zeroAllowed != "undefined" && !this.options.zeroAllowed && value === 0){
1023
- checker.createMessage("opendiscord:number-zero","error","This number can't be zero!",lt,null,[],this.id,(this.options.docs ?? null))
1024
- return false
1025
- }else return super.check(checker,value,locationTrace)
1026
- }
1027
- }
1028
-
1029
- /**## ODCheckerBooleanStructureOptions `interface`
1030
- * This interface has the options for `ODCheckerBooleanStructure`!
1031
- */
1032
- export interface ODCheckerBooleanStructureOptions extends ODCheckerStructureOptions {
1033
- /**Is `true` allowed? */
1034
- trueAllowed?:boolean,
1035
- /**Is `false` allowed? */
1036
- falseAllowed?:boolean
1037
- }
1038
-
1039
- /**## ODCheckerBooleanStructure `class`
1040
- * This is an Open Discord config checker structure.
1041
- *
1042
- * This class will check for a boolean variable in a config file, customise it in the settings!
1043
- */
1044
- export class ODCheckerBooleanStructure extends ODCheckerStructure {
1045
- declare options: ODCheckerBooleanStructureOptions
1046
-
1047
- constructor(id:ODValidId, options:ODCheckerBooleanStructureOptions){
1048
- super(id,options)
1049
- }
1050
-
1051
- check(checker:ODChecker, value:boolean, locationTrace:ODCheckerLocationTrace): boolean {
1052
- const lt = checker.locationTraceDeref(locationTrace)
1053
-
1054
- //check type & options
1055
- if (typeof value != "boolean"){
1056
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: boolean!",lt,null,["boolean"],this.id,(this.options.docs ?? null))
1057
- return false
1058
- }else if (typeof this.options.trueAllowed != "undefined" && !this.options.trueAllowed && value == true){
1059
- checker.createMessage("opendiscord:boolean-true","error","This boolean can't be true!",lt,null,[],this.id,(this.options.docs ?? null))
1060
- return false
1061
- }else if (typeof this.options.falseAllowed != "undefined" && !this.options.falseAllowed && value == false){
1062
- checker.createMessage("opendiscord:boolean-false","error","This boolean can't be false!",lt,null,[],this.id,(this.options.docs ?? null))
1063
- return false
1064
- }else return super.check(checker,value,locationTrace)
1065
- }
1066
- }
1067
-
1068
- /**## ODCheckerArrayStructureOptions `interface`
1069
- * This interface has the options for `ODCheckerArrayStructure`!
1070
- */
1071
- export interface ODCheckerArrayStructureOptions extends ODCheckerStructureOptions {
1072
- /**The checker for all the properties in this array */
1073
- propertyChecker?:ODCheckerStructure,
1074
- /**Don't allow this array to be empty */
1075
- disableEmpty?:boolean,
1076
- /**This array is required to be empty */
1077
- emptyRequired?:boolean,
1078
- /**The minimum length of this array */
1079
- minLength?:number,
1080
- /**The maximum length of this array */
1081
- maxLength?:number,
1082
- /**The length of the array needs to be the same as this value */
1083
- length?:number,
1084
- /**Allow double values (only for `string`, `number` & `boolean`) */
1085
- allowDoubles?:boolean
1086
- /**Only allow these types in the array (for multi-type propertyCheckers) */
1087
- allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[],
1088
- /**The name of the properties inside this array. Used in the GUI of the Interactive Setup CLI. */
1089
- cliDisplayPropertyName?:string
1090
- }
1091
-
1092
- /**## ODCheckerArrayStructure `class`
1093
- * This is an Open Discord config checker structure.
1094
- *
1095
- * This class will check for an array variable in a config file, customise it in the settings!
1096
- */
1097
- export class ODCheckerArrayStructure extends ODCheckerStructure {
1098
- declare options: ODCheckerArrayStructureOptions
1099
-
1100
- constructor(id:ODValidId, options:ODCheckerArrayStructureOptions){
1101
- super(id,options)
1102
- }
1103
-
1104
- check(checker:ODChecker, value:Array<any>, locationTrace:ODCheckerLocationTrace): boolean {
1105
- const lt = checker.locationTraceDeref(locationTrace)
1106
-
1107
- if (!Array.isArray(value)){
1108
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: array!",lt,null,["array"],this.id,(this.options.docs ?? null))
1109
- return false
1110
- }else if (typeof this.options.disableEmpty != "undefined" && this.options.disableEmpty && value.length == 0){
1111
- checker.createMessage("opendiscord:array-empty-disabled","error","This array isn't allowed to be empty!",lt,null,[],this.id,(this.options.docs ?? null))
1112
- return false
1113
- }else if (typeof this.options.emptyRequired != "undefined" && this.options.emptyRequired && value.length != 0){
1114
- checker.createMessage("opendiscord:array-empty-required","error","This array is required to be empty!",lt,null,[],this.id,(this.options.docs ?? null))
1115
- return false
1116
- }else if (typeof this.options.minLength != "undefined" && value.length < this.options.minLength){
1117
- checker.createMessage("opendiscord:array-too-short","error",`This array needs to have a length of at least ${this.options.minLength}!`,lt,null,[this.options.minLength.toString()],this.id,(this.options.docs ?? null))
1118
- return false
1119
- }else if (typeof this.options.maxLength != "undefined" && value.length > this.options.maxLength){
1120
- checker.createMessage("opendiscord:array-too-long","error",`This array needs to have a length of at most ${this.options.maxLength}!`,lt,null,[this.options.maxLength.toString()],this.id,(this.options.docs ?? null))
1121
- return false
1122
- }else if (typeof this.options.length != "undefined" && value.length == this.options.length){
1123
- checker.createMessage("opendiscord:array-length-invalid","error",`This array needs to have a length of ${this.options.length}!`,lt,null,[this.options.length.toString()],this.id,(this.options.docs ?? null))
1124
- return false
1125
- }else if (typeof this.options.allowedTypes != "undefined" && !this.arrayAllowedTypesCheck(value,this.options.allowedTypes)){
1126
- checker.createMessage("opendiscord:array-invalid-types","error",`This array can only contain the following types: ${this.options.allowedTypes.join(", ")}!`,lt,null,[this.options.allowedTypes.join(", ").toString()],this.id,(this.options.docs ?? null))
1127
- return false
1128
- }else if (typeof this.options.allowDoubles != "undefined" && !this.options.allowDoubles && this.arrayHasDoubles(value)){
1129
- checker.createMessage("opendiscord:array-double","error","This array doesn't allow the same value twice!",lt,null,[],this.id,(this.options.docs ?? null))
1130
- return false
1131
- }else{
1132
- //check all properties
1133
- let localQuit = false
1134
- if (this.options.propertyChecker) value.forEach((property,index) => {
1135
- if (!this.options.propertyChecker) return
1136
-
1137
- const localLt = checker.locationTraceDeref(lt)
1138
- localLt.push(index)
1139
-
1140
- if (!this.options.propertyChecker.check(checker,property,localLt)) localQuit = true
1141
- })
1142
-
1143
- //return false if invalid properties
1144
- if (localQuit){
1145
- checker.quit = true
1146
- return false
1147
- }else return super.check(checker,value,locationTrace)
1148
- }
1149
- }
1150
-
1151
- /**Check this array for the allowed types */
1152
- protected arrayAllowedTypesCheck(array:any[],allowedTypes:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]): boolean {
1153
- //return TRUE if ALL values are valid
1154
- return !array.some((value) => {
1155
- if (allowedTypes.includes("string") && typeof value == "string"){
1156
- return false //this value is valid
1157
- }else if (allowedTypes.includes("number") && typeof value == "number"){
1158
- return false //this value is valid
1159
- }else if (allowedTypes.includes("boolean") && typeof value == "boolean"){
1160
- return false //this value is valid
1161
- }else if (allowedTypes.includes("object") && typeof value == "object"){
1162
- return false //this value is valid
1163
- }else if (allowedTypes.includes("array") && Array.isArray(value)){
1164
- return false //this value is valid
1165
- }else if (allowedTypes.includes("null") && value === null){
1166
- return false //this value is valid
1167
- }else if (allowedTypes.includes("other")){
1168
- return false //this value is valid
1169
- }else{
1170
- return true //this value is invalid
1171
- }
1172
- })
1173
- }
1174
- /**Check this array for doubles */
1175
- protected arrayHasDoubles(array:any[]): boolean {
1176
- const alreadyFound: string[] = []
1177
- let hasDoubles = false
1178
- array.forEach((value) => {
1179
- if (alreadyFound.includes(value)) hasDoubles = true
1180
- else alreadyFound.push(value)
1181
- })
1182
-
1183
- return hasDoubles
1184
- }
1185
- }
1186
-
1187
- /**## ODCheckerNullStructureOptions `interface`
1188
- * This interface has the options for `ODCheckerNullStructure`!
1189
- */
1190
- export interface ODCheckerNullStructureOptions extends ODCheckerStructureOptions {
1191
- /**Is the value allowed to be null */
1192
- nullAllowed?:boolean
1193
- }
1194
-
1195
- /**## ODCheckerNullStructure `class`
1196
- * This is an Open Discord config checker structure.
1197
- *
1198
- * This class will check for a null variable in a config file, customise it in the settings!
1199
- */
1200
- export class ODCheckerNullStructure extends ODCheckerStructure {
1201
- declare options: ODCheckerNullStructureOptions
1202
-
1203
- constructor(id:ODValidId, options:ODCheckerNullStructureOptions){
1204
- super(id,options)
1205
- }
1206
-
1207
- check(checker:ODChecker, value:null, locationTrace:ODCheckerLocationTrace): boolean {
1208
- const lt = checker.locationTraceDeref(locationTrace)
1209
-
1210
- //check type & options
1211
- if (typeof this.options.nullAllowed != "undefined" && !this.options.nullAllowed && value == null){
1212
- checker.createMessage("opendiscord:null-invalid","error","This property can't be null!",lt,null,[],this.id,(this.options.docs ?? null))
1213
- return false
1214
- }else if (value !== null){
1215
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: null!",lt,null,["null"],this.id,(this.options.docs ?? null))
1216
- return false
1217
- }else return super.check(checker,value,locationTrace)
1218
- }
1219
- }
1220
-
1221
- /**## ODCheckerTypeSwitchStructureOptions `interface`
1222
- * This interface has the options for `ODCheckerTypeSwitchStructure`!
1223
- */
1224
- export interface ODCheckerTypeSwitchStructureOptions extends ODCheckerStructureOptions {
1225
- /**A checker that will always run (replaces all other checkers) */
1226
- all?:ODCheckerStructure,
1227
- /**A checker when the property is a string */
1228
- string?:ODCheckerStringStructure,
1229
- /**A checker when the property is a number */
1230
- number?:ODCheckerNumberStructure,
1231
- /**A checker when the property is a boolean */
1232
- boolean?:ODCheckerBooleanStructure,
1233
- /**A checker when the property is null */
1234
- null?:ODCheckerNullStructure,
1235
- /**A checker when the property is an array */
1236
- array?:ODCheckerArrayStructure,
1237
- /**A checker when the property is an object */
1238
- object?:ODCheckerObjectStructure,
1239
- /**A checker when the property is something else */
1240
- other?:ODCheckerStructure,
1241
- /**A list of allowed types */
1242
- allowedTypes:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
1243
- }
1244
-
1245
- /**## ODCheckerTypeSwitchStructure `class`
1246
- * This is an Open Discord config checker structure.
1247
- *
1248
- * This class will switch checkers based on the type of the variable in a config file, customise it in the settings!
1249
- */
1250
- export class ODCheckerTypeSwitchStructure extends ODCheckerStructure {
1251
- declare options: ODCheckerTypeSwitchStructureOptions
1252
-
1253
- constructor(id:ODValidId, options:ODCheckerTypeSwitchStructureOptions){
1254
- super(id,options)
1255
- }
1256
-
1257
- check(checker:ODChecker, value:any, locationTrace:ODCheckerLocationTrace): boolean {
1258
- const lt = checker.locationTraceDeref(locationTrace)
1259
-
1260
- if (this.options.all){
1261
- return this.options.all.check(checker,value,lt)
1262
-
1263
- }else if (this.options.string && typeof value == "string"){
1264
- return this.options.string.check(checker,value,lt)
1265
-
1266
- }else if (this.options.number && typeof value == "number"){
1267
- return this.options.number.check(checker,value,lt)
1268
-
1269
- }else if (this.options.boolean && typeof value == "boolean"){
1270
- return this.options.boolean.check(checker,value,lt)
1271
-
1272
- }else if (this.options.array && Array.isArray(value)){
1273
- return this.options.array.check(checker,value,lt)
1274
-
1275
- }else if (this.options.null && value === null){
1276
- return this.options.null.check(checker,value,lt)
1277
-
1278
- }else if (this.options.object && typeof value == "object"){
1279
- return this.options.object.check(checker,value,lt)
1280
-
1281
- }else if (this.options.other){
1282
- return this.options.other.check(checker,value,lt)
1283
-
1284
- }else if (this.options.allowedTypes && this.options.allowedTypes.length > 0){
1285
- checker.createMessage("opendiscord:switch-invalid-type","error",`This needs to be one of the following types: ${this.options.allowedTypes.join(", ")}!`,lt,null,[this.options.allowedTypes.join(", ")],this.id,(this.options.docs ?? null))
1286
- return false
1287
- }else return super.check(checker,value,locationTrace)
1288
- }
1289
- }
1290
-
1291
- /**## ODCheckerObjectSwitchStructureOptions `interface`
1292
- * This interface has the options for `ODCheckerObjectSwitchStructure`!
1293
- */
1294
- export interface ODCheckerObjectSwitchStructureOptions extends ODCheckerStructureOptions {
1295
- /**An array of object checkers with their name, properties & priority. */
1296
- objects:{
1297
- /**The properties to match for this checker to be used. */
1298
- properties:{key:string, value:boolean|string|number}[],
1299
- /**The name for this object type (used in rendering) */
1300
- name:string,
1301
- /**The higher the priority, the earlier this checker will be tested. */
1302
- priority:number,
1303
- /**The object checker used once the properties have been matched. */
1304
- checker:ODCheckerObjectStructure
1305
- }[]
1306
- }
1307
-
1308
- /**## ODCheckerObjectSwitchStructure `class`
1309
- * This is an Open Discord config checker structure.
1310
- *
1311
- * This class will switch object checkers based on a variable match in one of the objects, customise it in the settings!
1312
- */
1313
- export class ODCheckerObjectSwitchStructure extends ODCheckerStructure {
1314
- declare options: ODCheckerObjectSwitchStructureOptions
1315
-
1316
- constructor(id:ODValidId, options:ODCheckerObjectSwitchStructureOptions){
1317
- super(id,options)
1318
- }
1319
-
1320
- check(checker:ODChecker, value:Record<string,any>, locationTrace:ODCheckerLocationTrace): boolean {
1321
- const lt = checker.locationTraceDeref(locationTrace)
1322
-
1323
- if (this.options.objects){
1324
- //check type & options
1325
- if (typeof value != "object"){
1326
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: object!",lt,null,["object"],this.id,(this.options.docs ?? null))
1327
- return false
1328
- }
1329
-
1330
- //sort objects
1331
- const sortedObjects = this.options.objects.sort((a,b) => {
1332
- if (a.priority < b.priority) return -1
1333
- else if (a.priority > b.priority) return 1
1334
- else return 0
1335
- })
1336
-
1337
-
1338
- //check objects
1339
- let localQuit = false
1340
- let didSelectObject = false
1341
- sortedObjects.forEach((obj) => {
1342
- if (!obj.properties.some((p) => value[p.key] !== p.value)){
1343
- didSelectObject = true
1344
- if (!obj.checker.check(checker,value,lt)) localQuit = true
1345
- }
1346
- })
1347
-
1348
- //do local quit or check custom function
1349
- if (!didSelectObject){
1350
- checker.createMessage("opendiscord:object-switch-invalid-type","error",`This object needs to be one of the following types: ${this.options.objects.map((obj) => obj.name).join(", ")}!`,lt,null,[this.options.objects.map((obj) => obj.name).join(", ")],this.id,(this.options.docs ?? null))
1351
- return false
1352
- }else if (localQuit){
1353
- return false
1354
- }else return super.check(checker,value,locationTrace)
1355
- }else return super.check(checker,value,locationTrace)
1356
- }
1357
- }
1358
-
1359
- /**## ODCheckerEnabledObjectStructureOptions `interface`
1360
- * This interface has the options for `ODCheckerEnabledObjectStructure`!
1361
- */
1362
- export interface ODCheckerEnabledObjectStructureOptions extends ODCheckerStructureOptions {
1363
- /**The name of the property to match the `enabledValue`. */
1364
- property:string,
1365
- /**The value of the property to be enabled. (e.g. `true`) */
1366
- enabledValue:boolean|string|number,
1367
- /**The object checker to use once the property has been matched. */
1368
- checker:ODCheckerObjectStructure,
1369
- /**Ignore the `checker` if the object is disabled (`property` doesn't match `enabledValue`) */
1370
- ignoreCheckIfDisabled?:boolean
1371
- }
1372
-
1373
- /**## ODCheckerEnabledObjectStructure `class`
1374
- * This is an Open Discord config checker structure.
1375
- *
1376
- * This class will enable an object checker based on a variable match in the object, customise it in the settings!
1377
- */
1378
- export class ODCheckerEnabledObjectStructure extends ODCheckerStructure {
1379
- declare options: ODCheckerEnabledObjectStructureOptions
1380
-
1381
- constructor(id:ODValidId, options:ODCheckerEnabledObjectStructureOptions){
1382
- super(id,options)
1383
- }
1384
-
1385
- check(checker:ODChecker, value:Record<string,any>, locationTrace:ODCheckerLocationTrace): boolean {
1386
- const lt = checker.locationTraceDeref(locationTrace)
1387
-
1388
- if (typeof value != "object"){
1389
- //value isn't an object
1390
- checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: object!",lt,null,["object"],this.id,(this.options.docs ?? null))
1391
- return false
1392
-
1393
- }else if (this.options.property && typeof value[this.options.property] == "undefined"){
1394
- //property doesn't exist
1395
- checker.createMessage("opendiscord:property-missing","error",`The property "${this.options.property}" is mising from this object!`,lt,null,[`"${this.options.property}"`],this.id,(this.options.docs ?? null))
1396
- return false
1397
-
1398
- }else if (this.options.property && value[this.options.property] === (typeof this.options.enabledValue == "undefined" ? true : this.options.enabledValue)){
1399
- //this object is enabled
1400
- if (this.options.checker) return this.options.checker.check(checker,value,lt)
1401
- else return super.check(checker,value,locationTrace)
1402
-
1403
- }else{
1404
- //this object is disabled
1405
- if (this.options.property) checker.createMessage("opendiscord:object-disabled","info",`This object is disabled, enable it using "${this.options.property}"!`,lt,null,[`"${this.options.property}"`],this.id,(this.options.docs ?? null))
1406
- if (this.options.checker && !this.options.ignoreCheckIfDisabled) return this.options.checker.check(checker,value,lt)
1407
- else return super.check(checker,value,locationTrace)
1408
- }
1409
- }
1410
- }
1411
-
1412
- /**## ODCheckerCustomStructure_DiscordId `class`
1413
- * This is an Open Discord custom checker structure.
1414
- *
1415
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1416
- * You can compare it to a blueprint for a specific checker.
1417
- *
1418
- * **This custom checker is made for discord ids (channel, user, role, ...)**
1419
- */
1420
- export class ODCheckerCustomStructure_DiscordId extends ODCheckerStringStructure {
1421
- /**The type of id (used in rendering) */
1422
- readonly type: ODDiscordIdType
1423
- /**Is this id allowed to be empty */
1424
- readonly emptyAllowed: boolean
1425
- /**Extra matches (value will also be valid when one of these options match) */
1426
- readonly extraOptions: string[]
1427
-
1428
- constructor(id:ODValidId, type:ODDiscordIdType, emptyAllowed:boolean, extraOptions:string[], options?:ODCheckerStringStructureOptions){
1429
- //add premade custom structure checker
1430
- const newOptions = options ?? {}
1431
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1432
- const lt = checker.locationTraceDeref(locationTrace)
1433
-
1434
- if (typeof value != "string") return false
1435
- else if ((!emptyAllowed && value.length < 15) || value.length > 50 || !/^[0-9]*$/.test(value)){
1436
- if (!(extraOptions.length > 0 && extraOptions.some((opt) => opt == value))){
1437
- //value is not an id & not one of the extra options
1438
- if (extraOptions.length > 0) checker.createMessage("opendiscord:discord-invalid-id-options","error",`This is an invalid discord ${type} id! You can also use one of these: ${extraOptions.join(", ")}!`,lt,null,[type,extraOptions.join(", ")],this.id,(this.options.docs ?? null))
1439
- else checker.createMessage("opendiscord:discord-invalid-id","error",`This is an invalid discord ${type} id!`,lt,null,[type],this.id,(this.options.docs ?? null))
1440
- return false
1441
- }else return true
1442
- }
1443
- return true
1444
- }
1445
- super(id,newOptions)
1446
- this.type = type
1447
- this.emptyAllowed = emptyAllowed
1448
- this.extraOptions = extraOptions
1449
- }
1450
- }
1451
-
1452
- /**## ODCheckerCustomStructure_DiscordIdArray `class`
1453
- * This is an Open Discord custom checker structure.
1454
- *
1455
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1456
- * You can compare it to a blueprint for a specific checker.
1457
- *
1458
- * **This custom checker is made for discord id arrays (channel, user, role, ...)**
1459
- */
1460
- export class ODCheckerCustomStructure_DiscordIdArray extends ODCheckerArrayStructure {
1461
- /**The type of id (used in rendering) */
1462
- readonly type: ODDiscordIdType
1463
- /**Extra matches (value will also be valid when one of these options match) */
1464
- readonly extraOptions: string[]
1465
-
1466
- constructor(id:ODValidId, type:ODDiscordIdType, extraOptions:string[], options?:ODCheckerArrayStructureOptions, idOptions?:ODCheckerStringStructureOptions){
1467
- //add premade custom structure checker
1468
- const newOptions = options ?? {}
1469
- newOptions.propertyChecker = new ODCheckerCustomStructure_DiscordId(id,type,false,extraOptions,idOptions)
1470
- super(id,newOptions)
1471
- this.type = type
1472
- this.extraOptions = extraOptions
1473
- }
1474
- }
1475
-
1476
- /**## ODCheckerCustomStructure_DiscordToken `class`
1477
- * This is an Open Discord custom checker structure.
1478
- *
1479
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1480
- * You can compare it to a blueprint for a specific checker.
1481
- *
1482
- * **This custom checker is made for a discord (auth) token**
1483
- */
1484
- export class ODCheckerCustomStructure_DiscordToken extends ODCheckerStringStructure {
1485
- constructor(id:ODValidId, options?:ODCheckerStringStructureOptions){
1486
- //add premade custom structure checker
1487
- const newOptions = options ?? {}
1488
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1489
- const lt = checker.locationTraceDeref(locationTrace)
1490
-
1491
- if (typeof value != "string" || !/^[A-Za-z0-9-_\.]+$/.test(value)){
1492
- checker.createMessage("opendiscord:discord-invalid-token","error","This is an invalid discord token (syntactically)!",lt,null,[],this.id,(this.options.docs ?? null))
1493
- return false
1494
- }
1495
- return true
1496
- }
1497
- super(id,newOptions)
1498
- }
1499
- }
1500
-
1501
- /**## ODCheckerCustomStructure_DiscordToken `class`
1502
- * This is an Open Discord custom checker structure.
1503
- *
1504
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1505
- * You can compare it to a blueprint for a specific checker.
1506
- *
1507
- * **This custom checker is made for a hex color**
1508
- */
1509
- export class ODCheckerCustomStructure_HexColor extends ODCheckerStringStructure {
1510
- /**When enabled, you are also allowed to use `#fff` instead of `#ffffff` */
1511
- readonly allowShortForm: boolean
1512
- /**Allow this hex color to be empty. */
1513
- readonly emptyAllowed: boolean
1514
-
1515
- constructor(id:ODValidId, allowShortForm:boolean, emptyAllowed:boolean, options?:ODCheckerStringStructureOptions){
1516
- //add premade custom structure checker
1517
- const newOptions = options ?? {}
1518
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1519
- const lt = checker.locationTraceDeref(locationTrace)
1520
-
1521
- if (typeof value != "string") return false
1522
- else if (emptyAllowed && value.length == 0){
1523
- return true
1524
- }else if ((!allowShortForm && !/^#[a-fA-F0-9]{6}$/.test(value)) || (allowShortForm && !/^#[a-fA-F0-9]{6}$/.test(value) && !/^#[a-fA-F0-9]{3}$/.test(value))){
1525
- checker.createMessage("opendiscord:color-invalid","error","This is an invalid hex color!",lt,null,[],this.id,(this.options.docs ?? null))
1526
- return false
1527
- }else return true
1528
- }
1529
- super(id,newOptions)
1530
- this.allowShortForm = allowShortForm
1531
- this.emptyAllowed = emptyAllowed
1532
- }
1533
- }
1534
-
1535
- /**## ODCheckerCustomStructure_EmojiString `class`
1536
- * This is an Open Discord custom checker structure.
1537
- *
1538
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1539
- * You can compare it to a blueprint for a specific checker.
1540
- *
1541
- * **This custom checker is made for an emoji (string)**
1542
- */
1543
- export class ODCheckerCustomStructure_EmojiString extends ODCheckerStringStructure {
1544
- /**The minimum amount of emojis required (0 to allow empty) */
1545
- readonly minLength: number
1546
- /**The maximum amount of emojis allowed */
1547
- readonly maxLength: number
1548
- /**Allow custom discord emoji ids (`<:12345678910:emoji_name>`) */
1549
- readonly allowCustomDiscordEmoji: boolean
1550
-
1551
- constructor(id:ODValidId, minLength:number, maxLength:number, allowCustomDiscordEmoji:boolean, options?:ODCheckerStringStructureOptions){
1552
- //add premade custom structure checker
1553
- const newOptions = options ?? {}
1554
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1555
- const lt = checker.locationTraceDeref(locationTrace)
1556
- if (typeof value != "string") return false
1557
-
1558
- const discordEmojiSplitter = /(?:<a?:[^:]*:[0-9]+>)/g
1559
- const splitted = value.split(discordEmojiSplitter)
1560
- const discordEmojiAmount = splitted.length-1
1561
- const unicodeEmojiAmount = [...new Intl.Segmenter().segment(splitted.join(""))].length
1562
- const emojiAmount = discordEmojiAmount+unicodeEmojiAmount
1563
-
1564
- if (emojiAmount < minLength){
1565
- checker.createMessage("opendiscord:emoji-too-short","error",`This string needs to have at least ${minLength} emoji's!`,lt,null,[maxLength.toString()],this.id,(this.options.docs ?? null))
1566
- return false
1567
- }else if (emojiAmount > maxLength){
1568
- checker.createMessage("opendiscord:emoji-too-long","error",`This string needs to have at most ${maxLength} emoji's!`,lt,null,[maxLength.toString()],this.id,(this.options.docs ?? null))
1569
- return false
1570
- }else if (!allowCustomDiscordEmoji && /<a?:[^:]*:[0-9]+>/.test(value)){
1571
- checker.createMessage("opendiscord:emoji-custom","error",`This emoji can't be a custom discord emoji!`,lt,null,[],this.id,(this.options.docs ?? null))
1572
- return false
1573
- }else if (!/^(?:\p{Emoji}|\p{Emoji_Component}|(?:<a?:[^:]*:[0-9]+>))*$/u.test(value)){
1574
- checker.createMessage("opendiscord:emoji-invalid","error","This is an invalid emoji!",lt,null,[],this.id,(this.options.docs ?? null))
1575
- return false
1576
- }
1577
- return true
1578
- }
1579
- super(id,newOptions)
1580
- this.minLength = minLength
1581
- this.maxLength = maxLength
1582
- this.allowCustomDiscordEmoji = allowCustomDiscordEmoji
1583
- }
1584
- }
1585
-
1586
- /**## ODCheckerCustomStructureOptions_UrlString `interface`
1587
- * This interface has the options for `ODCheckerCustomStructure_UrlString`!
1588
- */
1589
- export interface ODCheckerCustomStructureOptions_UrlString {
1590
- /**Allow urls with `http://` instead of `https://` */
1591
- allowHttp?:boolean
1592
- /**Allowed hostnames (string or regex) => will match domain + subdomain */
1593
- allowedHostnames?: (string|RegExp)[]
1594
- /**Allowed extentions (string) => will match the end of the url (`.png`,`.svg`,...) */
1595
- allowedExtensions?: string[]
1596
- /**Allowed paths (string or regex) => will match path + extension (not domain + subdomain) */
1597
- allowedPaths?: (string|RegExp)[],
1598
- /**A regex that will be executed on the entire url (including search params, protcol, domain, ...) */
1599
- regex?:RegExp
1600
- }
1601
-
1602
- /**## ODCheckerCustomStructure_UrlString `class`
1603
- * This is an Open Discord custom checker structure.
1604
- *
1605
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1606
- * You can compare it to a blueprint for a specific checker.
1607
- *
1608
- * **This custom checker is made for a URL (string)**
1609
- */
1610
- export class ODCheckerCustomStructure_UrlString extends ODCheckerStringStructure {
1611
- /**The settings for this url */
1612
- readonly urlSettings: ODCheckerCustomStructureOptions_UrlString
1613
- /**Is this url allowed to be empty? */
1614
- readonly emptyAllowed: boolean
1615
-
1616
- constructor(id:ODValidId, emptyAllowed:boolean, urlSettings:ODCheckerCustomStructureOptions_UrlString, options?:ODCheckerStringStructureOptions){
1617
- //add premade custom structure checker
1618
- const newOptions = options ?? {}
1619
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1620
- const lt = checker.locationTraceDeref(locationTrace)
1621
-
1622
- if (typeof value != "string") return false
1623
- else if (emptyAllowed && value.length == 0){
1624
- return true
1625
- }else if (!this.urlIsValid(value)){
1626
- checker.createMessage("opendiscord:url-invalid","error","This url is invalid!",lt,null,[],this.id,(this.options.docs ?? null))
1627
- return false
1628
- }else if (typeof this.urlSettings.allowHttp != "undefined" && !this.urlSettings.allowHttp && !/^(https:\/\/)/.test(value)){
1629
- checker.createMessage("opendiscord:url-invalid-http","error","This url can only use the https:// protocol!",lt,null,[],this.id,(this.options.docs ?? null))
1630
- return false
1631
- }else if (!/^(http(s)?:\/\/)/.test(value)){
1632
- checker.createMessage("opendiscord:url-invalid-protocol","error","This url can only use the http:// & https:// protocols!",lt,null,[],this.id,(this.options.docs ?? null))
1633
- return false
1634
- }else if (typeof this.urlSettings.allowedHostnames != "undefined" && !this.urlHasValidHostname(value,this.urlSettings.allowedHostnames)){
1635
- checker.createMessage("opendiscord:url-invalid-hostname","error","This url has a disallowed hostname!",lt,null,[],this.id,(this.options.docs ?? null))
1636
- return false
1637
- }else if (typeof this.urlSettings.allowedExtensions != "undefined" && !this.urlHasValidExtension(value,this.urlSettings.allowedExtensions)){
1638
- checker.createMessage("opendiscord:url-invalid-extension","error",`This url has an invalid extension! Choose between: ${this.urlSettings.allowedExtensions.join(", ")}!"`,lt,null,[this.urlSettings.allowedExtensions.join(", ")],this.id,(this.options.docs ?? null))
1639
- return false
1640
- }else if (typeof this.urlSettings.allowedPaths != "undefined" && !this.urlHasValidPath(value,this.urlSettings.allowedPaths)){
1641
- checker.createMessage("opendiscord:url-invalid-path","error","This url has an invalid path!",lt,null,[],this.id,(this.options.docs ?? null))
1642
- return false
1643
- }else if (typeof this.urlSettings.regex != "undefined" && !this.urlSettings.regex.test(value)){
1644
- checker.createMessage("opendiscord:url-invalid","error","This url is invalid!",lt,null,[],this.id,(this.options.docs ?? null))
1645
- return false
1646
- }else return true
1647
- }
1648
- super(id,newOptions)
1649
- this.urlSettings = urlSettings
1650
- this.emptyAllowed = emptyAllowed
1651
- }
1652
-
1653
- /**Check for the hostname */
1654
- protected urlHasValidHostname(url:string,hostnames:(string|RegExp)[]): boolean {
1655
- try {
1656
- const hostname = new URL(url).hostname
1657
- return hostnames.some((rule) => {
1658
- if (typeof rule == "string"){
1659
- return rule == hostname
1660
- }else{
1661
- return rule.test(hostname)
1662
- }
1663
- })
1664
-
1665
- }catch{
1666
- return false
1667
- }
1668
- }
1669
- /**Check for the extension */
1670
- protected urlHasValidExtension(url:string,extensions:string[]): boolean {
1671
- try {
1672
- const path = new URL(url).pathname
1673
- return extensions.some((rule) => {
1674
- return path.endsWith(rule)
1675
- })
1676
- }catch{
1677
- return false
1678
- }
1679
- }
1680
- /**Check for the path */
1681
- protected urlHasValidPath(url:string,paths:(string|RegExp)[]): boolean {
1682
- try {
1683
- const path = new URL(url).pathname
1684
- return paths.some((rule) => {
1685
- if (typeof rule == "string"){
1686
- return rule == path
1687
- }else{
1688
- return rule.test(path)
1689
- }
1690
- })
1691
- }catch{
1692
- return false
1693
- }
1694
- }
1695
- /**Do general syntax check on url */
1696
- protected urlIsValid(url:string){
1697
- try {
1698
- new URL(url)
1699
- return true
1700
- }catch{
1701
- return false
1702
- }
1703
- }
1704
- }
1705
-
1706
- /**## ODCheckerCustomStructure_UniqueId `class`
1707
- * This is an Open Discord custom checker structure.
1708
- *
1709
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1710
- * You can compare it to a blueprint for a specific checker.
1711
- *
1712
- * **This custom checker is made for a unique id (per source & scope)**
1713
- */
1714
- export class ODCheckerCustomStructure_UniqueId extends ODCheckerStringStructure {
1715
- /**The source of this unique id (generally the plugin name or `opendiscord`) */
1716
- readonly source: string
1717
- /**The scope of this unique id (id needs to be unique in this scope) */
1718
- readonly scope: string
1719
-
1720
- constructor(id:ODValidId, source:string, scope:string, options?:ODCheckerStringStructureOptions){
1721
- //add premade custom structure checker
1722
- const newOptions = options ?? {}
1723
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1724
- const lt = checker.locationTraceDeref(locationTrace)
1725
-
1726
- if (typeof value != "string") return false
1727
- const uniqueArray: string[] = (checker.storage.get(source,scope) === null) ? [] : checker.storage.get(source,scope)
1728
- if (uniqueArray.includes(value)){
1729
- //unique id already exists => throw error
1730
- checker.createMessage("opendiscord:id-not-unique","error","This id isn't unique, use another id instead!",lt,null,[],this.id,(this.options.docs ?? null))
1731
- return false
1732
- }else{
1733
- //unique id doesn't exists => add to list
1734
- uniqueArray.push(value)
1735
- checker.storage.set(source,scope,uniqueArray)
1736
- return true
1737
- }
1738
- }
1739
- super(id,newOptions)
1740
- this.source = source
1741
- this.scope = scope
1742
- }
1743
- }
1744
-
1745
- /**## ODCheckerCustomStructure_UniqueIdArray `class`
1746
- * This is an Open Discord custom checker structure.
1747
- *
1748
- * This class extends a primitive config checker & adds another layer of checking in the `custom` function.
1749
- * You can compare it to a blueprint for a specific checker.
1750
- *
1751
- * **This custom checker is made for a unique id array (per source & scope)**
1752
- */
1753
- export class ODCheckerCustomStructure_UniqueIdArray extends ODCheckerArrayStructure {
1754
- /**The source to read unique ids (generally the plugin name or `opendiscord`) */
1755
- readonly source: string
1756
- /**The scope to read unique ids (id needs to be unique in this scope) */
1757
- readonly scope: string
1758
- /**The scope to push unique ids when used in this array! */
1759
- readonly usedScope: string|null
1760
-
1761
- constructor(id:ODValidId, source:string, scope:string, usedScope?:string, options?:ODCheckerArrayStructureOptions, idOptions?:Omit<ODCheckerStringStructureOptions,"minLength"|"custom">){
1762
- //add premade custom structure checker
1763
- const newOptions = options ?? {}
1764
- newOptions.propertyChecker = new ODCheckerStringStructure("opendiscord:unique-id",{...(idOptions ?? {}),minLength:1,custom:(checker,value,locationTrace,locationId,locationDocs) => {
1765
- if (typeof value != "string") return false
1766
- const localLt = checker.locationTraceDeref(locationTrace)
1767
- localLt.pop()
1768
-
1769
- const uniqueArray: string[] = checker.storage.get(source,scope) ?? []
1770
- if (uniqueArray.includes(value)){
1771
- //exists
1772
- if (usedScope){
1773
- const current: string[] = checker.storage.get(source,usedScope) ?? []
1774
- current.push(value)
1775
- checker.storage.set(source,usedScope,current)
1776
- }
1777
- return true
1778
- }else{
1779
- //doesn't exist
1780
- checker.createMessage("opendiscord:id-non-existent","error",`The id "${value}" doesn't exist!`,localLt,null,[`"${value}"`],locationId,locationDocs)
1781
- return false
1782
- }
1783
- }})
1784
- super(id,newOptions)
1785
- this.source = source
1786
- this.scope = scope
1787
- this.usedScope = usedScope ?? null
1788
- }
1789
- }
1790
-
1791
- /*TEMPLATE!!!!
1792
- export interface ODCheckerTemplateStructureOptions extends ODCheckerStructureOptions {
1793
-
1794
- }
1795
- export class ODCheckerTemplateStructure extends ODCheckerStructure {
1796
- declare options: ODCheckerTemplateStructureOptions
1797
-
1798
- constructor(id:ODValidId, options:ODCheckerTemplateStructureOptions){
1799
- super(id,options)
1800
- }
1801
-
1802
- check(checker:ODChecker, value:any, locationTrace:ODCheckerLocationTrace): boolean {
1803
- const lt = checker.locationTraceDeref(locationTrace)
1804
-
1805
- return super.check(checker,value,locationTrace)
1806
- }
1807
- }
1808
- */
1809
- /*CUSTOM TEMPLATE!!!!
1810
- export class ODCheckerCustomStructure_Template extends ODCheckerTemplateStructure {
1811
- idk: string
1812
-
1813
- constructor(id:ODValidId, idk:string, options?:ODCheckerStringStructureOptions){
1814
- //add premade custom structure checker
1815
- const newOptions = options ?? {}
1816
- newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
1817
- const lt = checker.locationTraceDeref(locationTrace)
1818
-
1819
- //do custom check & push error message. Return true if correct
1820
- return boolean
1821
- }
1822
- super(id,newOptions)
1823
- this.idk = idk
1824
- }
1825
- }
1826
- */