@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,340 @@
1
+ ///////////////////////////////////////
2
+ //PERMISSION MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODValidId, ODManager, ODSystemError, ODManagerData } from "./base"
5
+ import * as discord from "discord.js"
6
+ import { ODDebugger } from "./console"
7
+ import { ODClientManager } from "./client"
8
+
9
+ /**## ODPermissionType `type`
10
+ * All available permission types/levels. Can be used in the `ODPermission` class.
11
+ */
12
+ export type ODPermissionType = "member"|"support"|"moderator"|"admin"|"owner"|"developer"
13
+
14
+ /**## ODPermissionScope `type`
15
+ * The scope in which a certain permission is active.
16
+ */
17
+ export type ODPermissionScope = "global-user"|"channel-user"|"global-role"|"channel-role"
18
+
19
+ /**## ODPermissionResult `interface`
20
+ * The result returned by `ODPermissionManager.getPermissions()`.
21
+ */
22
+ export interface ODPermissionResult {
23
+ /**The permission type. */
24
+ type:ODPermissionType
25
+ /**The permission scope. */
26
+ scope:ODPermissionScope|"default"
27
+ /**The highest level available for this scope. */
28
+ level:ODPermissionLevel,
29
+ /**The permission which returned this level. */
30
+ source:ODPermission|null
31
+ }
32
+
33
+ /**## ODPermissionLevel `enum`
34
+ * All available permission types/levels. But as `enum` instead of `type`. Used to calculate the level.
35
+ */
36
+ export enum ODPermissionLevel {
37
+ /**A normal member. (Default for everyone) */
38
+ member,
39
+ /**Support team. Higher than a normal member. (Used for ticket-admins) */
40
+ support,
41
+ /**Moderator. Higher than the support team. (Unused) */
42
+ moderator,
43
+ /**Admin. Higher than a moderator. (Used for global-admins) */
44
+ admin,
45
+ /**Server owner. (Able to use all commands including `/stats reset`) */
46
+ owner,
47
+ /**Bot owner or all users from dev team. (Able to use all commands including `/stats reset`) */
48
+ developer
49
+ }
50
+
51
+ /**## ODPermission `class`
52
+ * This is an Open Discord permission.
53
+ *
54
+ * It defines a single permission level for a specific scope (global/channel & user/role)
55
+ * These permissions only apply to commands & interactions.
56
+ * They are not related to channel permissions in the ticket system.
57
+ *
58
+ * Register this class to an `ODPermissionManager` to use it!
59
+ */
60
+ export class ODPermission extends ODManagerData {
61
+ /**The scope of this permission. */
62
+ readonly scope: ODPermissionScope
63
+ /**The type/level of this permission. */
64
+ readonly permission: ODPermissionType
65
+ /**The user/role of this permission. */
66
+ readonly value: discord.Role|discord.User
67
+ /**The channel that this permission applies to. (`null` when global) */
68
+ readonly channel: discord.Channel|null
69
+
70
+ constructor(id:ODValidId, scope:"global-user", permission:ODPermissionType, value:discord.User)
71
+ constructor(id:ODValidId, scope:"global-role", permission:ODPermissionType, value:discord.Role)
72
+ constructor(id:ODValidId, scope:"channel-user", permission:ODPermissionType, value:discord.User, channel:discord.Channel)
73
+ constructor(id:ODValidId, scope:"channel-role", permission:ODPermissionType, value:discord.Role, channel:discord.Channel)
74
+ constructor(id:ODValidId, scope:ODPermissionScope, permission:ODPermissionType, value:discord.Role|discord.User, channel?:discord.Channel){
75
+ super(id)
76
+ this.scope = scope
77
+ this.permission = permission
78
+ this.value = value
79
+ this.channel = channel ?? null
80
+ }
81
+ }
82
+
83
+ /**## ODPermissionSettings `interface`
84
+ * Optional settings for the `getPermissions()` method in the `ODPermissionManager`.
85
+ */
86
+ export interface ODPermissionSettings {
87
+ /**Include permissions from the global user scope. */
88
+ allowGlobalUserScope?:boolean,
89
+ /**Include permissions from the global role scope. */
90
+ allowGlobalRoleScope?:boolean,
91
+ /**Include permissions from the channel user scope. */
92
+ allowChannelUserScope?:boolean,
93
+ /**Include permissions from the channel role scope. */
94
+ allowChannelRoleScope?:boolean,
95
+ /**Only include permissions of which the id matches this regex. */
96
+ idRegex?:RegExp
97
+ }
98
+
99
+ /**## ODPermissionCalculationCallback `type`
100
+ * The callback of the permission calculation. (Used in `ODPermissionManager`)
101
+ */
102
+ export type ODPermissionCalculationCallback = (user:discord.User, channel?:discord.Channel|null, guild?:discord.Guild|null, settings?:ODPermissionSettings|null) => Promise<ODPermissionResult>
103
+
104
+ /**## ODPermissionCommandResult `type`
105
+ * The result of calculating permissions for a command.
106
+ */
107
+ export type ODPermissionCommandResult = {
108
+ /**Returns `true` when the user has valid permissions. */
109
+ hasPerms:false,
110
+ reason:"no-perms"|"disabled"|"not-in-server"
111
+ }|{
112
+ /**Returns `true` when the user has valid permissions. */
113
+ hasPerms:true,
114
+ /**Is the user a server admin or a normal member? This does not decide if the user has permissions or not. */
115
+ isAdmin:boolean
116
+ }
117
+
118
+ /**## ODPermissionManager `class`
119
+ * This is an Open Discord permission manager.
120
+ *
121
+ * It manages all permissions in the bot!
122
+ * Use the `getPermissions()` and `hasPermissions()` methods to get user perms.
123
+ *
124
+ * Add new permissions using the `ODPermission` class in your plugin!
125
+ */
126
+ export class ODPermissionManager extends ODManager<ODPermission> {
127
+ /**Alias for Open Discord debugger. */
128
+ #debug: ODDebugger
129
+ /**The function for calculating permissions in this manager. */
130
+ #calculation: ODPermissionCalculationCallback|null
131
+ /**An alias to the Open Discord client manager. */
132
+ #client: ODClientManager
133
+ /**The result which is returned when no other permissions match. (`member` by default) */
134
+ defaultResult: ODPermissionResult = {
135
+ level:ODPermissionLevel["member"],
136
+ scope:"default",
137
+ type:"member",
138
+ source:null
139
+ }
140
+
141
+ constructor(debug:ODDebugger, client:ODClientManager, useDefaultCalculation?:boolean){
142
+ super(debug,"permission")
143
+ this.#debug = debug
144
+ this.#calculation = useDefaultCalculation ? this.#defaultCalculation : null
145
+ this.#client = client
146
+ }
147
+
148
+ /**Edit the permission calculation function in this manager. */
149
+ setCalculation(calculation:ODPermissionCalculationCallback){
150
+ this.#calculation = calculation
151
+ }
152
+ /**Edit the result which is returned when no other permissions match. (`member` by default) */
153
+ setDefaultResult(result:ODPermissionResult){
154
+ this.defaultResult = result
155
+ }
156
+ /**Get an `ODPermissionResult` based on a few context factors. Use `hasPermissions()` to simplify the result. */
157
+ getPermissions(user:discord.User, channel?:discord.Channel|null, guild?:discord.Guild|null, settings?:ODPermissionSettings|null): Promise<ODPermissionResult> {
158
+ try{
159
+ if (!this.#calculation) throw new ODSystemError("ODPermissionManager:getPermissions() => missing perms calculation")
160
+ return this.#calculation(user,channel,guild,settings)
161
+ }catch(err){
162
+ process.emit("uncaughtException",err)
163
+ throw new ODSystemError("ODPermissionManager:getPermissions() => failed perms calculation")
164
+ }
165
+ }
166
+ /**Simplifies the `ODPermissionResult` returned from `getPermissions()` and returns a boolean to check if the user matches the required permissions. */
167
+ hasPermissions(minimum:ODPermissionType, data:ODPermissionResult){
168
+ if (minimum == "member") return true
169
+ else if (minimum == "support") return (data.level >= ODPermissionLevel["support"])
170
+ else if (minimum == "moderator") return (data.level >= ODPermissionLevel["moderator"])
171
+ else if (minimum == "admin") return (data.level >= ODPermissionLevel["admin"])
172
+ else if (minimum == "owner") return (data.level >= ODPermissionLevel["owner"])
173
+ else if (minimum == "developer") return (data.level >= ODPermissionLevel["developer"])
174
+ else throw new ODSystemError("Invalid minimum permission type at ODPermissionManager.hasPermissions()")
175
+ }
176
+ /**Check for permissions. (default calculation) */
177
+ async #defaultCalculation(user:discord.User,channel?:discord.Channel|null,guild?:discord.Guild|null, settings?:ODPermissionSettings|null): Promise<ODPermissionResult> {
178
+ const globalCalc = await this.#defaultGlobalCalculation(user,channel,guild,settings)
179
+ const channelCalc = await this.#defaultChannelCalculation(user,channel,guild,settings)
180
+
181
+ if (globalCalc.level > channelCalc.level) return globalCalc
182
+ else return channelCalc
183
+ }
184
+ /**Check for global permissions. Result will be compared with the channel perms in `#defaultCalculation()`. */
185
+ async #defaultGlobalCalculation(user:discord.User,channel?:discord.Channel|null,guild?:discord.Guild|null, settings?:ODPermissionSettings|null): Promise<ODPermissionResult> {
186
+ const idRegex = (settings && typeof settings.idRegex != "undefined") ? settings.idRegex : null
187
+ const allowGlobalUserScope = (settings && typeof settings.allowGlobalUserScope != "undefined") ? settings.allowGlobalUserScope : true
188
+ const allowGlobalRoleScope = (settings && typeof settings.allowGlobalRoleScope != "undefined") ? settings.allowGlobalRoleScope : true
189
+
190
+ //check for global user permissions
191
+ if (allowGlobalUserScope){
192
+ const users = this.getFiltered((permission) => (!idRegex || (idRegex && idRegex.test(permission.id.value))) && permission.scope == "global-user" && (permission.value instanceof discord.User) && permission.value.id == user.id)
193
+
194
+ if (users.length > 0){
195
+ //sort all permisions from highest to lowest
196
+ users.sort((a,b) => {
197
+ const levelA = ODPermissionLevel[a.permission]
198
+ const levelB = ODPermissionLevel[b.permission]
199
+
200
+ if (levelB > levelA) return 1
201
+ else if (levelA > levelB) return -1
202
+ else return 0
203
+ })
204
+
205
+ return {
206
+ type:users[0].permission,
207
+ scope:"global-user",
208
+ level:ODPermissionLevel[users[0].permission],
209
+ source:users[0] ?? null
210
+ }
211
+ }
212
+ }
213
+
214
+ //check for global role permissions
215
+ if (allowGlobalRoleScope){
216
+ if (guild){
217
+ const member = await this.#client.fetchGuildMember(guild,user.id)
218
+ if (member){
219
+ const memberRoles = member.roles.cache.map((role) => role.id)
220
+ const roles = this.getFiltered((permission) => (!idRegex || (idRegex && idRegex.test(permission.id.value))) && permission.scope == "global-role" && (permission.value instanceof discord.Role) && memberRoles.includes(permission.value.id) && permission.value.guild.id == guild.id)
221
+
222
+ if (roles.length > 0){
223
+ //sort all permisions from highest to lowest
224
+ roles.sort((a,b) => {
225
+ const levelA = ODPermissionLevel[a.permission]
226
+ const levelB = ODPermissionLevel[b.permission]
227
+
228
+ if (levelB > levelA) return 1
229
+ else if (levelA > levelB) return -1
230
+ else return 0
231
+ })
232
+
233
+ return {
234
+ type:roles[0].permission,
235
+ scope:"global-role",
236
+ level:ODPermissionLevel[roles[0].permission],
237
+ source:roles[0] ?? null
238
+ }
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+ //spread result to prevent accidental referencing
245
+ return {...this.defaultResult}
246
+ }
247
+ /**Check for channel permissions. Result will be compared with the global perms in `#defaultCalculation()`. */
248
+ async #defaultChannelCalculation(user:discord.User,channel?:discord.Channel|null,guild?:discord.Guild|null, settings?:ODPermissionSettings|null): Promise<ODPermissionResult> {
249
+ const idRegex = (settings && typeof settings.idRegex != "undefined") ? settings.idRegex : null
250
+ const allowChannelUserScope = (settings && typeof settings.allowChannelUserScope != "undefined") ? settings.allowChannelUserScope : true
251
+ const allowChannelRoleScope = (settings && typeof settings.allowChannelRoleScope != "undefined") ? settings.allowChannelRoleScope : true
252
+
253
+ if (guild && channel && !channel.isDMBased()){
254
+ //check for channel user permissions
255
+ if (allowChannelUserScope){
256
+ const users = this.getFiltered((permission) => (!idRegex || (idRegex && idRegex.test(permission.id.value))) && permission.scope == "channel-user" && permission.channel && (permission.channel.id == channel.id) && (permission.value instanceof discord.User) && permission.value.id == user.id)
257
+
258
+ if (users.length > 0){
259
+ //sort all permisions from highest to lowest
260
+ users.sort((a,b) => {
261
+ const levelA = ODPermissionLevel[a.permission]
262
+ const levelB = ODPermissionLevel[b.permission]
263
+
264
+ if (levelB > levelA) return 1
265
+ else if (levelA > levelB) return -1
266
+ else return 0
267
+ })
268
+
269
+ return {
270
+ type:users[0].permission,
271
+ scope:"channel-user",
272
+ level:ODPermissionLevel[users[0].permission],
273
+ source:users[0] ?? null
274
+ }
275
+ }
276
+ }
277
+
278
+ //check for channel role permissions
279
+ if (allowChannelRoleScope){
280
+ const member = await this.#client.fetchGuildMember(guild,user.id)
281
+ if (member){
282
+ const memberRoles = member.roles.cache.map((role) => role.id)
283
+ const roles = this.getFiltered((permission) => (!idRegex || (idRegex && idRegex.test(permission.id.value))) && permission.scope == "channel-role" && permission.channel && (permission.channel.id == channel.id) && (permission.value instanceof discord.Role) && memberRoles.includes(permission.value.id) && permission.value.guild.id == guild.id)
284
+
285
+ if (roles.length > 0){
286
+ //sort all permisions from highest to lowest
287
+ roles.sort((a,b) => {
288
+ const levelA = ODPermissionLevel[a.permission]
289
+ const levelB = ODPermissionLevel[b.permission]
290
+
291
+ if (levelB > levelA) return 1
292
+ else if (levelA > levelB) return -1
293
+ else return 0
294
+ })
295
+
296
+ return {
297
+ type:roles[0].permission,
298
+ scope:"channel-role",
299
+ level:ODPermissionLevel[roles[0].permission],
300
+ source:roles[0] ?? null
301
+ }
302
+ }
303
+ }
304
+ }
305
+ }
306
+
307
+ //spread result to prevent accidental modification because of referencing
308
+ return {...this.defaultResult}
309
+ }
310
+
311
+ /**Check the permissions for a certain command of the bot. */
312
+ async checkCommandPerms(permissionMode:string,requiredLevel:ODPermissionType,user:discord.User,member?:discord.GuildMember|null,channel?:discord.Channel|null,guild?:discord.Guild|null,settings?:ODPermissionSettings): Promise<ODPermissionCommandResult> {
313
+ if (permissionMode === "none"){
314
+ return {hasPerms:false,reason:"disabled"}
315
+
316
+ }else if (permissionMode === "everyone"){
317
+ const isAdmin = this.hasPermissions(requiredLevel,await this.getPermissions(user,channel,guild,settings))
318
+ return {hasPerms:true,isAdmin}
319
+
320
+ }else if (permissionMode === "admin"){
321
+ const isAdmin = this.hasPermissions(requiredLevel,await this.getPermissions(user,channel,guild,settings))
322
+ if (!isAdmin) return {hasPerms:false,reason:"no-perms"}
323
+ else return {hasPerms:true,isAdmin}
324
+ }else{
325
+ if (!guild || !member){
326
+ this.#debug.debug("ODPermissionManager.checkCommandPerms(): Permission Error, Not in server! (#1)")
327
+ return {hasPerms:false,reason:"not-in-server"}
328
+ }
329
+ const role = await this.#client.fetchGuildRole(guild,permissionMode)
330
+ if (!role){
331
+ this.#debug.debug("ODPermissionManager.checkCommandPerms(): Permission Error, Not in server! (#2)")
332
+ return {hasPerms:false,reason:"not-in-server"}
333
+ }
334
+ if (!role.members.has(member.id)) return {hasPerms:false,reason:"no-perms"}
335
+
336
+ const isAdmin = this.hasPermissions(requiredLevel,await this.getPermissions(user,channel,guild,settings))
337
+ return {hasPerms:true,isAdmin}
338
+ }
339
+ }
340
+ }
@@ -0,0 +1,242 @@
1
+ ///////////////////////////////////////
2
+ //PLUGIN MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODManager, ODManagerData, ODValidId, ODVersion } from "./base"
5
+ import { ODDebugger } from "./console"
6
+ import nodepath from "path"
7
+
8
+ /**## ODUnknownCrashedPlugin `interface`
9
+ * Basic details for a plugin that crashed while loading the `plugin.json` file.
10
+ */
11
+ export interface ODUnknownCrashedPlugin {
12
+ /**The name of the plugin. (path when plugin crashed before `name` was loaded) */
13
+ name:string,
14
+ /**The description of the plugin. (when found before crashing) */
15
+ description:string
16
+ }
17
+
18
+ /**## ODPluginManager `class`
19
+ * This is an Open Discord plugin manager.
20
+ *
21
+ * It manages all active plugins in the bot!
22
+ * It also contains all "plugin classes" which are managers registered by plugins.
23
+ * These are accessible via the `opendiscord.plugins.classes` global.
24
+ *
25
+ * Use `isPluginLoaded()` to check if a plugin has been loaded.
26
+ */
27
+ export class ODPluginManager extends ODManager<ODPlugin> {
28
+ /**A manager for all custom managers registered by plugins. */
29
+ classes: ODPluginClassManager
30
+ /**A list of basic details from all plugins that crashed while loading the `plugin.json` file. */
31
+ unknownCrashedPlugins: ODUnknownCrashedPlugin[] = []
32
+
33
+ constructor(debug:ODDebugger){
34
+ super(debug,"plugin")
35
+ this.classes = new ODPluginClassManager(debug)
36
+ }
37
+
38
+ /**Check if a plugin has been loaded successfully and is available for usage.*/
39
+ isPluginLoaded(id:ODValidId): boolean {
40
+ const newId = new ODId(id)
41
+ const plugin = this.get(newId)
42
+ return (plugin !== null && plugin.executed)
43
+ }
44
+ }
45
+
46
+ /**## ODPluginData `interface`
47
+ * Parsed data from the `plugin.json` file in a plugin.
48
+ */
49
+ export interface ODPluginData {
50
+ /**The name of this plugin (shown on startup) */
51
+ name:string,
52
+ /**The id of this plugin. (Must be identical to directory name) */
53
+ id:string,
54
+ /**The version of this plugin. */
55
+ version:string,
56
+ /**The location of the start file of the plugin relative to the rootDir of the plugin */
57
+ startFile:string,
58
+ /**A list of compatible versions. (e.g. `["OTv4.0.x", "OMv1.x.x"]`) (optional, will be required in future version)
59
+ * - `OT` --> Open Ticket support
60
+ * - `OM` --> Open Moderation support
61
+ */
62
+ supportedVersions?:string[],
63
+
64
+ /**Is this plugin enabled? */
65
+ enabled:boolean,
66
+ /**The priority of this plugin. Higher priority will load before lower priority. */
67
+ priority:number,
68
+ /**A list of events to register to the `opendiscord.events` global before loading any plugins. This way, plugins with a higher priority are able to use events from this plugin as well! */
69
+ events:string[]
70
+
71
+ /**Npm dependencies which are required for this plugin to work. */
72
+ npmDependencies:string[],
73
+ /**Plugins which are required for this plugin to work. */
74
+ requiredPlugins:string[],
75
+ /**Plugins which are incompatible with this plugin. */
76
+ incompatiblePlugins:string[],
77
+
78
+ /**Additional details about this plugin. */
79
+ details:ODPluginDetails
80
+ }
81
+
82
+ /**## ODPluginDetails `interface`
83
+ * Additional details in the `plugin.json` file from a plugin.
84
+ */
85
+ export interface ODPluginDetails {
86
+ /**The main author of the plugin. Additional contributors can be specified in `contributors`. */
87
+ author:string,
88
+ /**A list of plugin contributors. (optional, will be required in future version) */
89
+ contributors?:string[],
90
+ /**A short description of this plugin. */
91
+ shortDescription:string,
92
+ /**A large description of this plugin. */
93
+ longDescription:string,
94
+ /**A URL to a cover image of this plugin. (currently unused) */
95
+ imageUrl:string,
96
+ /**A URL to the website/project page of this plugin. (currently unused) */
97
+ projectUrl:string,
98
+ /**A list of tags/categories that this plugin affects. */
99
+ tags:string[]
100
+ }
101
+
102
+ /**## ODPlugin `class`
103
+ * This is an Open Discord plugin.
104
+ *
105
+ * It represents a single plugin in the `./plugins/` directory.
106
+ * All plugins are accessible via the `opendiscord.plugins` global.
107
+ *
108
+ * Don't re-execute plugins which are already enabled! It might break the bot or plugin.
109
+ */
110
+ export class ODPlugin extends ODManagerData {
111
+ /**The name of the directory of this plugin. (same as id) */
112
+ dir: string
113
+ /**All plugin data found in the `plugin.json` file. */
114
+ data: ODPluginData
115
+ /**The name of this plugin. */
116
+ name: string
117
+ /**The priority of this plugin. */
118
+ priority: number
119
+ /**The version of this plugin. */
120
+ version: ODVersion
121
+ /**The additional details of this plugin. */
122
+ details: ODPluginDetails
123
+
124
+ /**Is this plugin enabled? */
125
+ enabled: boolean
126
+ /**Did this plugin execute successfully?. */
127
+ executed: boolean
128
+ /**Did this plugin crash? (A reason is available in the `crashReason`) */
129
+ crashed: boolean
130
+ /**The reason which caused this plugin to crash. */
131
+ crashReason: null|"incompatible.plugin"|"missing.plugin"|"missing.dependency"|"incompatible.version"|"executed" = null
132
+
133
+ constructor(dir:string, jsondata:ODPluginData){
134
+ super(jsondata.id)
135
+ this.dir = dir
136
+ this.data = jsondata
137
+ this.name = jsondata.name
138
+ this.priority = jsondata.priority
139
+ this.version = ODVersion.fromString("plugin",jsondata.version)
140
+ this.details = jsondata.details
141
+
142
+ this.enabled = jsondata.enabled
143
+ this.executed = false
144
+ this.crashed = false
145
+ }
146
+
147
+ /**Get the startfile location relative to the `./plugins/` directory. (`./dist/plugins/`) when compiled) */
148
+ getStartFile(){
149
+ const newFile = this.data.startFile.replace(/\.ts$/,".js")
150
+ return nodepath.join(this.dir,newFile)
151
+ }
152
+ /**Execute this plugin. Returns `false` on crash. */
153
+ async execute(debug:ODDebugger,force?:boolean): Promise<boolean> {
154
+ if ((this.enabled && !this.crashed) || force){
155
+ try{
156
+ //import relative plugin directory path (works on windows & unix based systems)
157
+ const pluginPath = nodepath.join("../../../../plugins/",this.getStartFile()).replaceAll("\\","/")
158
+ await import(pluginPath)
159
+ debug.console.log("Plugin \""+this.id.value+"\" loaded successfully!","plugin")
160
+ this.executed = true
161
+ return true
162
+ }catch(error){
163
+ this.crashed = true
164
+ this.crashReason = "executed"
165
+
166
+ debug.console.log(error.message+", canceling plugin execution...","plugin",[
167
+ {key:"path",value:"./plugins/"+this.dir}
168
+ ])
169
+ debug.console.log("You can see more about this error in the ./debug.txt file!","info")
170
+ debug.console.debugfile.writeText(error.stack)
171
+
172
+ return false
173
+ }
174
+ }else return true
175
+ }
176
+
177
+ /**Check if a npm dependency exists. */
178
+ #checkDependency(id:string){
179
+ try{
180
+ require.resolve(id)
181
+ return true
182
+ }catch{
183
+ return false
184
+ }
185
+ }
186
+
187
+ /**Get a list of all missing npm dependencies that are required for this plugin. */
188
+ dependenciesInstalled(){
189
+ const missing: string[] = []
190
+ this.data.npmDependencies.forEach((d) => {
191
+ if (!this.#checkDependency(d)){
192
+ missing.push(d)
193
+ }
194
+ })
195
+
196
+ return missing
197
+ }
198
+ /**Get a list of all missing plugins that are required for this plugin. */
199
+ pluginsInstalled(manager:ODPluginManager){
200
+ const missing: string[] = []
201
+ this.data.requiredPlugins.forEach((p) => {
202
+ const plugin = manager.get(p)
203
+ if (!plugin || !plugin.enabled){
204
+ missing.push(p)
205
+ }
206
+ })
207
+
208
+ return missing
209
+ }
210
+ /**Get a list of all enabled incompatible plugins that interfere with this plugin. */
211
+ pluginsIncompatible(manager:ODPluginManager){
212
+ const incompatible: string[] = []
213
+ this.data.incompatiblePlugins.forEach((p) => {
214
+ const plugin = manager.get(p)
215
+ if (plugin && plugin.enabled){
216
+ incompatible.push(p)
217
+ }
218
+ })
219
+
220
+ return incompatible
221
+ }
222
+ /**Get a list of all authors & contributors of this plugin. */
223
+ getAuthors(): string[] {
224
+ return [this.details.author,...(this.details.contributors ?? [])]
225
+ }
226
+ }
227
+
228
+ /**## ODPluginClassManager `class`
229
+ * This is an Open Discord plugin class manager.
230
+ *
231
+ * It manages all managers registered by plugins!
232
+ * Plugins are able to register their own managers, handlers, functions, classes, ... here.
233
+ * By doing this, other plugins are also able to make use of it.
234
+ * This can be useful for plugins that want to extend other plugins.
235
+ *
236
+ * Use `isPluginLoaded()` to check if a plugin has been loaded before trying to access the manager.
237
+ */
238
+ export class ODPluginClassManager extends ODManager<ODManagerData> {
239
+ constructor(debug:ODDebugger){
240
+ super(debug,"plugin class")
241
+ }
242
+ }
@@ -0,0 +1,90 @@
1
+ ///////////////////////////////////////
2
+ //POST MODULE
3
+ ///////////////////////////////////////
4
+ import { ODId, ODManager, ODManagerData, ODValidId } from "./base"
5
+ import { ODMessageBuildResult, ODMessageBuildSentResult } from "./builder"
6
+ import { ODDebugger } from "./console"
7
+ import * as discord from "discord.js"
8
+
9
+ /**## ODPostManager `class`
10
+ * This is an Open Discord post manager.
11
+ *
12
+ * It manages `ODPosts`'s for you.
13
+ *
14
+ * You can use this to get the logs channel of the bot (or some other static channel/category).
15
+ */
16
+ export class ODPostManager extends ODManager<ODPost<discord.GuildBasedChannel>> {
17
+ /**A reference to the main server of the bot */
18
+ #guild: discord.Guild|null = null
19
+
20
+ constructor(debug:ODDebugger){
21
+ super(debug,"post")
22
+ }
23
+
24
+ add(data:ODPost<discord.GuildBasedChannel>, overwrite?:boolean): boolean {
25
+ if (this.#guild) data.useGuild(this.#guild)
26
+ return super.add(data,overwrite)
27
+ }
28
+ /**Initialize the post manager & all posts. */
29
+ async init(guild:discord.Guild){
30
+ this.#guild = guild
31
+ for (const post of this.getAll()){
32
+ post.useGuild(guild)
33
+ await post.init()
34
+ }
35
+ }
36
+ }
37
+
38
+ /**## ODPost `class`
39
+ * This is an Open Discord post class.
40
+ *
41
+ * A post is just a shortcut to a static discord channel or category.
42
+ * This can be used to get a specific channel over and over again!
43
+ *
44
+ * This class also contains utilities for sending messages via the Open Discord builders.
45
+ */
46
+ export class ODPost<ChannelType extends discord.GuildBasedChannel> extends ODManagerData {
47
+ /**A reference to the main server of the bot */
48
+ #guild: discord.Guild|null = null
49
+ /**Is this post already initialized? */
50
+ ready: boolean = false
51
+ /**The discord.js channel */
52
+ channel: ChannelType|null = null
53
+ /**The discord channel id */
54
+ channelId: string
55
+
56
+ constructor(id:ODValidId, channelId:string){
57
+ super(id)
58
+ this.channelId = channelId
59
+ }
60
+
61
+ /**Use a specific guild in this class for fetching the channel*/
62
+ useGuild(guild:discord.Guild|null){
63
+ this.#guild = guild
64
+ }
65
+ /**Change the channel id to another channel! */
66
+ setChannelId(id:string){
67
+ this.channelId = id
68
+ }
69
+ /**Initialize the discord.js channel of this post. */
70
+ async init(){
71
+ if (this.ready) return
72
+ if (!this.#guild) return this.channel = null
73
+ try{
74
+ this.channel = await this.#guild.channels.fetch(this.channelId) as ChannelType
75
+ }catch{
76
+ this.channel = null
77
+ }
78
+ this.ready = true
79
+ }
80
+ /**Send a message to this channel using the Open Discord builder system */
81
+ async send(msg:ODMessageBuildResult): Promise<ODMessageBuildSentResult<true>> {
82
+ if (!this.channel || !this.channel.isTextBased()) return {success:false,message:null}
83
+ try{
84
+ const sent = await this.channel.send(msg.message)
85
+ return {success:true,message:sent}
86
+ }catch{
87
+ return {success:false,message:null}
88
+ }
89
+ }
90
+ }