@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,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
|
+
}
|