@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.
Files changed (103) hide show
  1. package/LICENSE.md +713 -0
  2. package/README.md +104 -0
  3. package/dist/api/api.d.ts +26 -0
  4. package/dist/api/api.js +44 -0
  5. package/dist/api/main.d.ts +133 -0
  6. package/dist/api/main.js +87 -0
  7. package/dist/api/modules/action.d.ts +34 -0
  8. package/dist/api/modules/action.js +58 -0
  9. package/dist/api/modules/base.d.ts +329 -0
  10. package/dist/api/modules/base.js +804 -0
  11. package/dist/api/modules/builder.d.ts +647 -0
  12. package/dist/api/modules/builder.js +1441 -0
  13. package/dist/api/modules/checker.d.ts +648 -0
  14. package/dist/api/modules/checker.js +1324 -0
  15. package/dist/api/modules/client.d.ts +768 -0
  16. package/dist/api/modules/client.js +1859 -0
  17. package/dist/api/modules/code.d.ts +33 -0
  18. package/dist/api/modules/code.js +57 -0
  19. package/dist/api/modules/config.d.ts +70 -0
  20. package/dist/api/modules/config.js +206 -0
  21. package/dist/api/modules/console.d.ts +305 -0
  22. package/dist/api/modules/console.js +598 -0
  23. package/dist/api/modules/cooldown.d.ts +138 -0
  24. package/dist/api/modules/cooldown.js +359 -0
  25. package/dist/api/modules/database.d.ts +135 -0
  26. package/dist/api/modules/database.js +271 -0
  27. package/dist/api/modules/event.d.ts +43 -0
  28. package/dist/api/modules/event.js +100 -0
  29. package/dist/api/modules/flag.d.ts +40 -0
  30. package/dist/api/modules/flag.js +72 -0
  31. package/dist/api/modules/fuse.d.ts +218 -0
  32. package/dist/api/modules/fuse.js +123 -0
  33. package/dist/api/modules/helpmenu.d.ts +106 -0
  34. package/dist/api/modules/helpmenu.js +167 -0
  35. package/dist/api/modules/language.d.ts +85 -0
  36. package/dist/api/modules/language.js +195 -0
  37. package/dist/api/modules/permission.d.ts +121 -0
  38. package/dist/api/modules/permission.js +314 -0
  39. package/dist/api/modules/plugin.d.ts +128 -0
  40. package/dist/api/modules/plugin.js +168 -0
  41. package/dist/api/modules/post.d.ts +44 -0
  42. package/dist/api/modules/post.js +92 -0
  43. package/dist/api/modules/progressbar.d.ts +108 -0
  44. package/dist/api/modules/progressbar.js +233 -0
  45. package/dist/api/modules/responder.d.ts +506 -0
  46. package/dist/api/modules/responder.js +1468 -0
  47. package/dist/api/modules/session.d.ts +58 -0
  48. package/dist/api/modules/session.js +171 -0
  49. package/dist/api/modules/startscreen.d.ts +165 -0
  50. package/dist/api/modules/startscreen.js +293 -0
  51. package/dist/api/modules/stat.d.ts +142 -0
  52. package/dist/api/modules/stat.js +293 -0
  53. package/dist/api/modules/verifybar.d.ts +54 -0
  54. package/dist/api/modules/verifybar.js +60 -0
  55. package/dist/api/modules/worker.d.ts +41 -0
  56. package/dist/api/modules/worker.js +93 -0
  57. package/dist/api/utils.d.ts +61 -0
  58. package/dist/api/utils.js +254 -0
  59. package/dist/index.d.ts +4 -1
  60. package/dist/index.js +40 -0
  61. package/dist/startup/dump.d.ts +14 -0
  62. package/dist/startup/dump.js +79 -0
  63. package/dist/startup/errorHandling.d.ts +2 -0
  64. package/dist/startup/errorHandling.js +43 -0
  65. package/dist/startup/pluginLauncher.d.ts +2 -0
  66. package/dist/startup/pluginLauncher.js +202 -0
  67. package/package.json +9 -3
  68. package/src/api/api.ts +29 -0
  69. package/src/api/main.ts +189 -0
  70. package/src/api/modules/action.ts +58 -0
  71. package/src/api/modules/base.ts +811 -0
  72. package/src/api/modules/builder.ts +1554 -0
  73. package/src/api/modules/checker.ts +1549 -0
  74. package/src/api/modules/client.ts +2247 -0
  75. package/src/api/modules/code.ts +58 -0
  76. package/src/api/modules/config.ts +159 -0
  77. package/src/api/modules/console.ts +665 -0
  78. package/src/api/modules/cooldown.ts +348 -0
  79. package/src/api/modules/database.ts +278 -0
  80. package/src/api/modules/event.ts +99 -0
  81. package/src/api/modules/flag.ts +73 -0
  82. package/src/api/modules/fuse.ts +348 -0
  83. package/src/api/modules/helpmenu.ts +216 -0
  84. package/src/api/modules/language.ts +201 -0
  85. package/src/api/modules/permission.ts +340 -0
  86. package/src/api/modules/plugin.ts +242 -0
  87. package/src/api/modules/post.ts +90 -0
  88. package/src/api/modules/progressbar.ts +232 -0
  89. package/src/api/modules/responder.ts +1420 -0
  90. package/src/api/modules/session.ts +155 -0
  91. package/src/api/modules/startscreen.ts +320 -0
  92. package/src/api/modules/stat.ts +313 -0
  93. package/src/api/modules/verifybar.ts +61 -0
  94. package/src/api/modules/worker.ts +93 -0
  95. package/src/api/utils.ts +206 -0
  96. package/src/cli/cli.ts +151 -0
  97. package/src/cli/editConfig.ts +943 -0
  98. package/src/index.ts +6 -1
  99. package/src/startup/compilation.ts +186 -0
  100. package/src/startup/dump.ts +45 -0
  101. package/src/startup/errorHandling.ts +38 -0
  102. package/src/startup/pluginLauncher.ts +261 -0
  103. package/LICENSE +0 -21
@@ -0,0 +1,313 @@
1
+ ///////////////////////////////////////
2
+ //STAT MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODManager, ODManagerData, ODSystemError, ODValidId } from "./base"
5
+ import { ODDebugger } from "./console"
6
+ import { ODDatabase, ODJsonDatabaseStructure } from "./database"
7
+ import * as discord from "discord.js"
8
+
9
+ /**## ODValidStatValue `type`
10
+ * These are the only allowed types for a stat value to improve compatibility with different database systems.
11
+ */
12
+ export type ODValidStatValue = string|number|boolean
13
+
14
+ /**## ODStatsManagerInitCallback `type`
15
+ * This callback can be used to execute something when the stats have been initiated.
16
+ *
17
+ * By default this is used to clear stats from users that left the server or tickets which don't exist anymore.
18
+ */
19
+ export type ODStatsManagerInitCallback = (database:ODJsonDatabaseStructure, deletables:ODJsonDatabaseStructure) => void|Promise<void>
20
+
21
+ /**## ODStatScopeSetMode `type`
22
+ * This type contains all valid methods for changing the value of a stat.
23
+ */
24
+ export type ODStatScopeSetMode = "set"|"increase"|"decrease"
25
+
26
+ /**## ODStatsManager `class`
27
+ * This is an Open Discord stats manager.
28
+ *
29
+ * This class is responsible for managing all stats of the bot.
30
+ * Stats are categorized in "scopes" which can be accessed in this manager.
31
+ *
32
+ * Stats can be accessed in the individual scopes.
33
+ */
34
+ export class ODStatsManager extends ODManager<ODStatScope> {
35
+ /**Alias to Open Discord debugger. */
36
+ #debug: ODDebugger
37
+ /**Alias to Open Discord stats database. */
38
+ database: ODDatabase|null = null
39
+ /**All the listeners for the init event. */
40
+ #initListeners: ODStatsManagerInitCallback[] = []
41
+
42
+ constructor(debug:ODDebugger){
43
+ super(debug,"stat scope")
44
+ this.#debug = debug
45
+ }
46
+
47
+ /**Select the database to use to read/write all stats from/to. */
48
+ useDatabase(database:ODDatabase){
49
+ this.database = database
50
+ }
51
+ add(data:ODStatScope, overwrite?:boolean): boolean {
52
+ data.useDebug(this.#debug,"stat")
53
+ if (this.database) data.useDatabase(this.database)
54
+ return super.add(data,overwrite)
55
+ }
56
+ /**Init all stats and run `onInit()` listeners. */
57
+ async init(){
58
+ if (!this.database) throw new ODSystemError("Unable to initialize stats scopes due to missing database!")
59
+
60
+ //get all valid categories
61
+ const validCategories: string[] = []
62
+ for (const scope of this.getAll()){
63
+ validCategories.push(...scope.init())
64
+ }
65
+
66
+ //filter out the deletable stats
67
+ const deletableStats: ODJsonDatabaseStructure = []
68
+ const data = await this.database.getAll()
69
+ data.forEach((data) => {
70
+ if (!validCategories.includes(data.category)) deletableStats.push(data)
71
+ })
72
+
73
+ //do additional deletion
74
+ for (const cb of this.#initListeners){
75
+ await cb(data,deletableStats)
76
+ }
77
+
78
+ //delete all deletable stats
79
+ for (const data of deletableStats){
80
+ if (!this.database) return
81
+ await this.database.delete(data.category,data.key)
82
+ }
83
+ }
84
+ /**Reset all stats. (clears the entire database) */
85
+ async reset(){
86
+ if (!this.database) return
87
+ const data = await this.database.getAll()
88
+ for (const d of data){
89
+ if (!this.database) return
90
+ await this.database.delete(d.category,d.key)
91
+ }
92
+ }
93
+ /**Run a function when the stats are initialized. This can be used to clear stats from users that left the server or tickets which don't exist anymore. */
94
+ onInit(callback:ODStatsManagerInitCallback){
95
+ this.#initListeners.push(callback)
96
+ }
97
+ }
98
+
99
+ /**## ODStatScope `class`
100
+ * This is an Open Discord stat scope.
101
+ *
102
+ * A scope can contain multiple stats. Every scope is seperated from other scopes.
103
+ * Here, you can read & write the values of all stats.
104
+ *
105
+ * The built-in Open Discord scopes are: `global`, `user`, `ticket`
106
+ */
107
+ export class ODStatScope extends ODManager<ODStat> {
108
+ /**The id of this statistics scope. */
109
+ id: ODId
110
+ /**Is this stat scope already initialized? */
111
+ ready: boolean = false
112
+ /**Alias to Open Discord stats database. */
113
+ database: ODDatabase|null = null
114
+ /**The name of this scope (used in embed title) */
115
+ name:string
116
+
117
+ constructor(id:ODValidId, name:string){
118
+ super()
119
+ this.id = new ODId(id)
120
+ this.name = name
121
+ }
122
+
123
+ /**Select the database to use to read/write all stats from/to. (Automatically assigned when used in `ODStatsManager`) */
124
+ useDatabase(database:ODDatabase){
125
+ this.database = database
126
+ }
127
+ /**Get the value of a statistic. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */
128
+ async getStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null> {
129
+ if (!this.database) return null
130
+ const newId = new ODId(id)
131
+ const data = await this.database.get(this.id.value+"_"+newId.value,scopeId)
132
+
133
+ if (typeof data == "undefined"){
134
+ //set stats to default value & return
135
+ return this.resetStat(id,scopeId)
136
+ }else if (typeof data == "string" || typeof data == "boolean" || typeof data == "number"){
137
+ //return value received from database
138
+ return data
139
+ }
140
+ //return null on error
141
+ return null
142
+ }
143
+ /**Get the value of a statistic for all `scopeId`'s. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */
144
+ async getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
145
+ if (!this.database) return []
146
+ const newId = new ODId(id)
147
+ const data = await this.database.getCategory(this.id.value+"_"+newId.value) ?? []
148
+ const output: {id:string,value:ODValidStatValue}[] = []
149
+
150
+ for (const stat of data){
151
+ if (typeof stat.value == "string" || typeof stat.value == "boolean" || typeof stat.value == "number"){
152
+ //return value received from database
153
+ output.push({id:stat.key,value:stat.value})
154
+ }
155
+ }
156
+
157
+ //return null on error
158
+ return output
159
+ }
160
+ /**Set, increase or decrease the value of a statistic. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */
161
+ async setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean> {
162
+ if (!this.database) return false
163
+ const stat = this.get(id)
164
+ if (!stat) return false
165
+ if (mode == "set" || typeof value != "number"){
166
+ await this.database.set(this.id.value+"_"+stat.id.value,scopeId,value)
167
+ }else if (mode == "increase"){
168
+ const currentValue = await this.getStat(id,scopeId)
169
+ if (typeof currentValue != "number") await this.database.set(this.id.value+"_"+stat.id.value,scopeId,0+value)
170
+ else await this.database.set(this.id.value+"_"+stat.id.value,scopeId,currentValue+value)
171
+ }else if (mode == "decrease"){
172
+ const currentValue = await this.getStat(id,scopeId)
173
+ if (typeof currentValue != "number") await this.database.set(this.id.value+"_"+stat.id.value,scopeId,0-value)
174
+ else await this.database.set(this.id.value+"_"+stat.id.value,scopeId,currentValue-value)
175
+ }
176
+ return true
177
+ }
178
+ /**Reset the value of a statistic to the initial value. The `scopeId` is the unique id of the user, channel, role, etc that the stats are related to. */
179
+ async resetStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null> {
180
+ if (!this.database) return null
181
+ const stat = this.get(id)
182
+ if (!stat) return null
183
+ if (stat.value != null) await this.database.set(this.id.value+"_"+stat.id.value,scopeId,stat.value)
184
+ return stat.value
185
+ }
186
+ /**Initialize this stat scope & return a list of all statistic ids in the following format: `<scopeid>_<statid>` */
187
+ init(): string[] {
188
+ //get all valid stats categories
189
+ this.ready = true
190
+ return this.getAll().map((stat) => this.id.value+"_"+stat.id.value)
191
+ }
192
+ /**Render all stats in this scope for usage in a discord message/embed. */
193
+ async render(scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User): Promise<string> {
194
+ //sort from high priority to low
195
+ const derefArray = [...this.getAll()]
196
+ derefArray.sort((a,b) => {
197
+ return b.priority-a.priority
198
+ })
199
+ const result: string[] = []
200
+
201
+ for (const stat of derefArray){
202
+ try {
203
+ if (stat instanceof ODDynamicStat){
204
+ //dynamic render (without value)
205
+ result.push(await stat.render("",scopeId,guild,channel,user))
206
+ }else{
207
+ //normal render (with value)
208
+ const value = await this.getStat(stat.id,scopeId)
209
+ if (value != null) result.push(await stat.render(value,scopeId,guild,channel,user))
210
+ }
211
+
212
+ }catch(err){
213
+ process.emit("uncaughtException",err)
214
+ }
215
+ }
216
+
217
+ return result.filter((stat) => stat !== "").join("\n")
218
+ }
219
+ }
220
+
221
+ /**## ODStatGlobalScope `class`
222
+ * This is an Open Discord stat global scope.
223
+ *
224
+ * A scope can contain multiple stats. Every scope is seperated from other scopes.
225
+ * Here, you can read & write the values of all stats.
226
+ *
227
+ * This scope is made specifically for the global stats of Open Discord.
228
+ */
229
+ export class ODStatGlobalScope extends ODStatScope {
230
+ getStat(id:ODValidId): Promise<ODValidStatValue|null> {
231
+ return super.getStat(id,"GLOBAL")
232
+ }
233
+ getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
234
+ return super.getAllStats(id)
235
+ }
236
+ setStat(id:ODValidId, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean> {
237
+ return super.setStat(id,"GLOBAL",value,mode)
238
+ }
239
+ resetStat(id:ODValidId): Promise<ODValidStatValue|null> {
240
+ return super.resetStat(id,"GLOBAL")
241
+ }
242
+ render(scopeId:"GLOBAL", guild:discord.Guild, channel:discord.TextBasedChannel, user: discord.User): Promise<string> {
243
+ return super.render("GLOBAL",guild,channel,user)
244
+ }
245
+ }
246
+
247
+ /**## ODStatRenderer `type`
248
+ * This callback will render a single statistic for a discord embed/message.
249
+ */
250
+ export type ODStatRenderer = (value:ODValidStatValue, scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User) => string|Promise<string>
251
+
252
+ /**## ODStat `class`
253
+ * This is an Open Discord statistic.
254
+ *
255
+ * This single statistic doesn't do anything except defining the rules of this statistic.
256
+ * Use it in a stats scope to register a new statistic. A statistic can also include a priority to choose the render priority.
257
+ *
258
+ * It's recommended to use the `ODBasicStat` & `ODDynamicStat` classes instead of this one!
259
+ */
260
+ export class ODStat extends ODManagerData {
261
+ /**The priority of this statistic. */
262
+ priority: number
263
+ /**The render function of this statistic. */
264
+ render: ODStatRenderer
265
+ /**The value of this statistic. */
266
+ value: ODValidStatValue|null
267
+
268
+ constructor(id:ODValidId, priority:number, render:ODStatRenderer, value?:ODValidStatValue){
269
+ super(id)
270
+ this.priority = priority
271
+ this.render = render
272
+ this.value = value ?? null
273
+ }
274
+ }
275
+
276
+ /**## ODBasicStat `class`
277
+ * This is an Open Discord basic statistic.
278
+ *
279
+ * This single statistic will store a number, boolean or string in the database.
280
+ * Use it to create a simple statistic for any stats scope.
281
+ */
282
+ export class ODBasicStat extends ODStat {
283
+ /**The name of this stat. Rendered in discord embeds/messages. */
284
+ name: string
285
+
286
+ constructor(id:ODValidId, priority:number, name:string, value:ODValidStatValue){
287
+ super(id,priority,(value) => {
288
+ return ""+name+": `"+value.toString()+"`"
289
+ },value)
290
+ this.name = name
291
+ }
292
+ }
293
+
294
+ /**## ODDynamicStatRenderer `type`
295
+ * This callback will render a single dynamic statistic for a discord embed/message.
296
+ */
297
+ export type ODDynamicStatRenderer = (scopeId:string, guild:discord.Guild, channel:discord.TextBasedChannel, user:discord.User) => string|Promise<string>
298
+
299
+ /**## ODDynamicStat `class`
300
+ * This is an Open Discord dynamic statistic.
301
+ *
302
+ * A dynamic statistic does not store anything in the database! Instead, it will execute a function to return a custom result.
303
+ * This can be used to show statistics which are not stored in the database.
304
+ *
305
+ * This is used in Open Discord for the live ticket status, participants & system status.
306
+ */
307
+ export class ODDynamicStat extends ODStat {
308
+ constructor(id:ODValidId, priority:number, render:ODDynamicStatRenderer){
309
+ super(id,priority,(value,scopeId,guild,channel,user) => {
310
+ return render(scopeId,guild,channel,user)
311
+ })
312
+ }
313
+ }
@@ -0,0 +1,61 @@
1
+ ///////////////////////////////////////
2
+ //VERIFYBAR MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODManager, ODManagerData, ODValidId } from "./base"
5
+ import { ODMessage } from "./builder"
6
+ import { ODDebugger } from "./console"
7
+ import { ODButtonResponderInstance } from "./responder"
8
+ import * as discord from "discord.js"
9
+ import { ODWorkerManager } from "./worker"
10
+
11
+ /**## ODVerifyBar `class`
12
+ * This is an Open Discord verifybar.
13
+ *
14
+ * It is contains 2 sets of workers and a lot of utilities for the (✅ ❌) verifybars in the bot.
15
+ *
16
+ * It doesn't contain the code which activates or spawns the verifybars!
17
+ */
18
+ export class ODVerifyBar extends ODManagerData {
19
+ /**All workers that will run when the verifybar is accepted. */
20
+ success: ODWorkerManager<ODButtonResponderInstance,"verifybar",{data:string|null,verifybarMessage:discord.Message<boolean>|null}>
21
+ /**All workers that will run when the verifybar is stopped. */
22
+ failure: ODWorkerManager<ODButtonResponderInstance,"verifybar",{data:string|null,verifybarMessage:discord.Message<boolean>|null}>
23
+ /**The message that will be built wen activating this verifybar. */
24
+ message: ODMessage<"verifybar",{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,verifybar:ODVerifyBar,originalMessage:discord.Message<boolean>}>
25
+ /**When disabled, it will skip the verifybar and instantly fire the `success` workers. */
26
+ enabled: boolean
27
+
28
+ constructor(id:ODValidId, message:ODMessage<"verifybar",{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,originalMessage:discord.Message<boolean>}>, enabled?:boolean){
29
+ super(id)
30
+ this.success = new ODWorkerManager("descending")
31
+ this.failure = new ODWorkerManager("descending")
32
+ this.message = message
33
+ this.enabled = enabled ?? true
34
+ }
35
+
36
+ /**Build the message and reply to a button with this verifybar. */
37
+ async activate(responder:ODButtonResponderInstance){
38
+ if (this.enabled){
39
+ //show verifybar
40
+ const {guild,channel,user,message} = responder
41
+ await responder.update(await this.message.build("verifybar",{guild,channel,user,verifybar:this,originalMessage:message}))
42
+ }else{
43
+ //instant success
44
+ if (this.success) await this.success.executeWorkers(responder,"verifybar",{data:null,verifybarMessage:null})
45
+ }
46
+ }
47
+ }
48
+
49
+ /**## ODVerifyBarManager `class`
50
+ * This is an Open Discord verifybar manager.
51
+ *
52
+ * It contains all (✅ ❌) verifybars in the bot.
53
+ * The `ODVerifyBar` classes contain `ODWorkerManager`'s that will be fired when the continue/stop buttons are pressed.
54
+ *
55
+ * It doesn't contain the code which activates the verifybars! This should be implemented by your own.
56
+ */
57
+ export class ODVerifyBarManager extends ODManager<ODVerifyBar> {
58
+ constructor(debug:ODDebugger){
59
+ super(debug,"verifybar")
60
+ }
61
+ }
@@ -0,0 +1,93 @@
1
+ ///////////////////////////////////////
2
+ //WORKER MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODManager, ODManagerData, ODValidId } from "./base"
5
+
6
+ /**## ODWorkerCallback `type`
7
+ * This is the callback used in `ODWorker`!
8
+ */
9
+ export type ODWorkerCallback<Instance, Source extends string, Params> = (instance:Instance, params:Params, source:Source, cancel:() => void) => void|Promise<void>
10
+
11
+ /**## ODWorker `class`
12
+ * This is an Open Discord worker.
13
+ *
14
+ * You can compare it with a normal javascript callback, but slightly more advanced!
15
+ *
16
+ * - It has an `id` for identification of the function
17
+ * - A `priority` to know when to execute this callback (related to others)
18
+ * - It knows who called this callback (`source`)
19
+ * - And much more!
20
+ */
21
+ export class ODWorker<Instance, Source extends string, Params> extends ODManagerData {
22
+ /**The priority of this worker */
23
+ priority: number
24
+ /**The main callback of this worker */
25
+ callback: ODWorkerCallback<Instance,Source,Params>
26
+
27
+ constructor(id:ODValidId, priority:number, callback:ODWorkerCallback<Instance,Source,Params>){
28
+ super(id)
29
+ this.priority = priority
30
+ this.callback = callback
31
+ }
32
+ }
33
+
34
+ /**## ODWorker `class`
35
+ * This is an Open Discord worker manager.
36
+ *
37
+ * It manages & executes `ODWorker`'s in the correct order.
38
+ *
39
+ * You can register a custom worker in this class to create a message or button.
40
+ */
41
+ export class ODWorkerManager<Instance, Source extends string, Params> extends ODManager<ODWorker<Instance,Source,Params>> {
42
+ /**The order of execution for workers inside this manager. */
43
+ #priorityOrder: "ascending"|"descending"
44
+ /**The backup worker will be executed when one of the workers fails or cancels execution. */
45
+ backupWorker: ODWorker<{reason:"error"|"cancel"},Source,Params>|null = null
46
+
47
+ constructor(priorityOrder:"ascending"|"descending"){
48
+ super()
49
+ this.#priorityOrder = priorityOrder
50
+ }
51
+
52
+ /**Get all workers in sorted order. */
53
+ getSortedWorkers(priority:"ascending"|"descending"){
54
+ const derefArray = [...this.getAll()]
55
+
56
+ return derefArray.sort((a,b) => {
57
+ if (priority == "ascending") return a.priority-b.priority
58
+ else return b.priority-a.priority
59
+ })
60
+ }
61
+ /**Execute all workers on an instance using the given source & parameters. */
62
+ async executeWorkers(instance:Instance, source:Source, params:Params){
63
+ const derefParams = {...params}
64
+ const workers = this.getSortedWorkers(this.#priorityOrder)
65
+ let didCancel = false
66
+ let didCrash = false
67
+
68
+ for (const worker of workers){
69
+ if (didCancel) break
70
+ try {
71
+ await worker.callback(instance,derefParams,source,() => {
72
+ didCancel = true
73
+ })
74
+ }catch(err){
75
+ process.emit("uncaughtException",err)
76
+ didCrash = true
77
+ }
78
+ }
79
+ if (didCancel && this.backupWorker){
80
+ try{
81
+ await this.backupWorker.callback({reason:"cancel"},derefParams,source,() => {})
82
+ }catch(err){
83
+ process.emit("uncaughtException",err)
84
+ }
85
+ }else if (didCrash && this.backupWorker){
86
+ try{
87
+ await this.backupWorker.callback({reason:"error"},derefParams,source,() => {})
88
+ }catch(err){
89
+ process.emit("uncaughtException",err)
90
+ }
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,206 @@
1
+ import * as fs from "fs"
2
+ import ansis from "ansis"
3
+ import * as api from "./api"
4
+
5
+ /**## sharedFuses `utility variable`
6
+ * All shared fuses from Open Discord. Please use `opendiscord.sharedFuses` instead!
7
+ */
8
+ export const sharedFuses: api.ODSharedFuseManager = new api.ODSharedFuseManager()
9
+
10
+ /**## checkNodeVersion `utility function`
11
+ * Check if the node.js version is v20 or higher.
12
+ */
13
+ export function checkNodeVersion(project:api.ODProjectType){
14
+ const nodev = process.versions.node.split(".")
15
+ if (Number(nodev[0]) < 20){
16
+ const title = (project == "openticket") ? "OPEN TICKET" : "OPEN MODERATION"
17
+ console.log("\n\n==============================\n["+title+" ERROR]: Invalid node.js version. Open Ticket requires node.js v20 or above!\n==============================\n\n")
18
+ process.exit(1)
19
+ }
20
+ }
21
+
22
+ /**## moduleInstalled `utility function`
23
+ * Use this function to check if an npm package is installed or not!
24
+ * @example utilities.moduleInstalled("discord.js") //check if discord.js is installed
25
+ */
26
+ export function moduleInstalled(id:string,throwError?:boolean): boolean {
27
+ try{
28
+ require.resolve(id)
29
+ return true
30
+ }catch{
31
+ if (throwError) throw new Error("npm module \""+id+"\" is not installed! Install it via 'npm install "+id+"'")
32
+ return false
33
+ }
34
+ }
35
+
36
+ /**## initialStartupLogs `utility function`
37
+ * Use this function to check if an npm package is installed or not!
38
+ * @example utilities.moduleInstalled("discord.js") //check if discord.js is installed
39
+ */
40
+ export function initialStartupLogs(opendiscord:api.ODMain,project:api.ODProjectType){
41
+ const title = (project == "openticket") ? "OPEN TICKET" : "OPEN MODERATION"
42
+ console.log("\n--------------------------- "+title+" STARTUP ---------------------------")
43
+ opendiscord.log("Logging system activated!","system")
44
+ opendiscord.debug.debug("Using Node.js "+process.version+"!")
45
+
46
+ try{
47
+ const packageJson = JSON.parse(fs.readFileSync("./package.json").toString())
48
+ opendiscord.debug.debug("Using discord.js "+packageJson.dependencies["discord.js"]+"!")
49
+ opendiscord.debug.debug("Using @discordjs/rest "+packageJson.dependencies["@discordjs/rest"]+"!")
50
+ opendiscord.debug.debug("Using ansis "+packageJson.dependencies["ansis"]+"!")
51
+ opendiscord.debug.debug("Using formatted-json-stringify "+packageJson.dependencies["formatted-json-stringify"]+"!")
52
+ opendiscord.debug.debug("Using terminal-kit "+packageJson.dependencies["terminal-kit"]+"!")
53
+ opendiscord.debug.debug("Using typescript "+packageJson.dependencies["typescript"]+"!")
54
+ }catch{
55
+ opendiscord.debug.debug("Failed to fetch module versions!")
56
+ }
57
+ }
58
+
59
+ /**## timer `utility function`
60
+ * Use this to wait for a certain amount of milliseconds. This only works when using `await`
61
+ * @example await utilities.timer(1000) //wait 1sec
62
+ */
63
+ export async function timer(ms:number): Promise<void> {
64
+ return new Promise((resolve) => {
65
+ setTimeout(() => {
66
+ resolve()
67
+ },ms)
68
+ })
69
+ }
70
+
71
+ /**## emojiTitle `utility function`
72
+ * Use this function to create a title with an emoji before/after the text. The style & divider are set in `opendiscord.sharedFuses`
73
+ * @example utilities.emojiTitle("📎","Links") //create a title with an emoji based on the bot emoji style
74
+ */
75
+ export function emojiTitle(emoji:string, text:string){
76
+ const style = sharedFuses.getFuse("emojiTitleStyle")
77
+ const divider = sharedFuses.getFuse("emojiTitleDivider")
78
+
79
+ if (style == "disabled") return text
80
+ else if (style == "before") return emoji+divider+text
81
+ else if (style == "after") return text+divider+emoji
82
+ else if (style == "double") return emoji+divider+text+divider+emoji
83
+ else return text
84
+ }
85
+
86
+ /**## runAsync `utility function`
87
+ * Use this function to run a snippet of code asyncronous without creating a separate function for it!
88
+ */
89
+ export async function runAsync(func:() => Promise<void>): Promise<void> {
90
+ func()
91
+ }
92
+
93
+ /**## timedAwait `utility function`
94
+ * Use this function to await a promise but reject after the certain timeout has been reached.
95
+ */
96
+ export function timedAwait<ReturnValue>(promise:ReturnValue,timeout:number,onError:(err:Error) => void): ReturnValue {
97
+ let allowResolve = true
98
+ return new Promise(async (resolve,reject) => {
99
+ //set timeout & stop if it is before the promise resolved
100
+ setTimeout(() => {
101
+ allowResolve = false
102
+ reject("utilities.timedAwait() => Promise Timeout")
103
+ },timeout)
104
+
105
+ //get promise result & return if not already rejected
106
+ try{
107
+ const res = await promise
108
+ if (allowResolve) resolve(res)
109
+ }catch(err){
110
+ onError(err)
111
+ }
112
+ return promise
113
+ }) as ReturnValue
114
+ }
115
+
116
+ /**## dateString `utility function`
117
+ * Use this function to create a short date string in the following format: `DD/MM/YYYY HH:MM:SS`
118
+ */
119
+ export function dateString(date): string {
120
+ return `${date.getDate()}/${date.getMonth()+1}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
121
+ }
122
+
123
+ /**## asyncReplace `utility function`
124
+ * Same as `string.replace(search, value)` but with async compatibility
125
+ */
126
+ export async function asyncReplace(text:string, regex:RegExp, func:(value:string,...args:any[]) => Promise<string>): Promise<string> {
127
+ const promises: Promise<string>[] = []
128
+ text.replace(regex,(match,...args) => {
129
+ promises.push(func(match,...args))
130
+ return match
131
+ })
132
+ const data = await Promise.all(promises)
133
+ const result = text.replace(regex,(match) => {
134
+ const replaceResult = data.shift()
135
+ return replaceResult ?? match
136
+ })
137
+ return result
138
+ }
139
+
140
+ /**## getLongestLength `utility function`
141
+ * Get the length of the longest string in the array.
142
+ */
143
+ export function getLongestLength(texts:string[]): number {
144
+ return Math.max(...texts.map((t) => ansis.strip(t).length))
145
+ }
146
+
147
+ /**## ordinalNumber `utility function`
148
+ * Get a human readable ordinal number (e.g. 1st, 2nd, 3rd, 4th, ...) from a Javascript number.
149
+ */
150
+ export function ordinalNumber(num:number){
151
+ const i = Math.abs(Math.round(num))
152
+ const cent = i % 100
153
+ if (cent >= 10 && cent <= 20) return i+'th'
154
+ const dec = i % 10
155
+ if (dec === 1) return i+'st'
156
+ if (dec === 2) return i+'nd'
157
+ if (dec === 3) return i+'rd'
158
+ return i+'th'
159
+ }
160
+
161
+ /**## trimEmojis `utility function`
162
+ * Trim/remove all emoji's from a Javascript string.
163
+ */
164
+ export function trimEmojis(text){
165
+ return text.replace(/(\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\uFE0E)?)*)/gu,"")
166
+ }
167
+
168
+ /**## easterEggs `utility object`
169
+ * Object containing data for Open Ticket easter eggs.
170
+ */
171
+ export const easterEggs: api.ODEasterEggs = {
172
+ /* THANK YOU TO ALL OUR CONTRIBUTORS!!! */
173
+ creator:"779742674932072469", //DJj123dj
174
+ translators:[
175
+ "779742674932072469", //DJj123dj
176
+ "574172558006681601", //Sanke
177
+ "540639725300613136", //Guillee.3
178
+ "547231585368539136", //Mods HD
179
+ "664934139954331649", //SpyEye
180
+ "498055992962187264", //Redactado
181
+ "912052735950618705", //T0miiis
182
+ "366673202610569227", //johusens
183
+ "360780292853858306", //David.3
184
+ "950611418389024809", //Sarcastic
185
+ "461603955517161473", //Maurizo
186
+ "465111430274875402", //The_Gamer
187
+ "586376952470831104", //Erxg
188
+ "226695254433202176", //Mkevas
189
+ "437695615095275520", //NoOneNook
190
+ "530047191222583307", //Anderskiy
191
+ "719072181631320145", //ToStam
192
+ "1172870906377408512", //Stragar
193
+ "1084794575945744445", //Sasanwm
194
+ "449613814049275905", //Benzorich
195
+ "905373133085741146", //Ronalds
196
+ "918504977369018408", //Palestinian
197
+ "807970841035145216", //Kornel0706
198
+ "1198883915826475080", //Nova
199
+ "669988226819162133", //Danoglez
200
+ "1313597620996018271", //Fraden1
201
+ "547809968145956884", //TsgIndrius
202
+ "264120132660363267", //Quiradon
203
+ "1272034143777329215", //NotMega
204
+ "LOREMIPSUM", //TODO, ADD MORE IDS IN FUTURE!
205
+ ]
206
+ }