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