@jsbot-tellus/core 2.0.3 → 2.2.0
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/README.md +23 -27
- package/dist/app.js +14 -40
- package/dist/app.js.map +1 -1
- package/dist/core/config/schema.d.ts +0 -2
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +0 -1
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/dispatch/command.d.ts +24 -2
- package/dist/core/dispatch/command.d.ts.map +1 -1
- package/dist/core/dispatch/command.js +297 -60
- package/dist/core/dispatch/command.js.map +1 -1
- package/dist/core/dispatch/router.d.ts +17 -3
- package/dist/core/dispatch/router.d.ts.map +1 -1
- package/dist/core/dispatch/router.js +38 -2
- package/dist/core/dispatch/router.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/kernel.d.ts.map +1 -1
- package/dist/core/kernel.js +5 -1
- package/dist/core/kernel.js.map +1 -1
- package/dist/core/plugin/command-params.d.ts +37 -0
- package/dist/core/plugin/command-params.d.ts.map +1 -0
- package/dist/core/plugin/command-params.js +427 -0
- package/dist/core/plugin/command-params.js.map +1 -0
- package/dist/core/plugin/context.d.ts.map +1 -1
- package/dist/core/plugin/context.js +27 -0
- package/dist/core/plugin/context.js.map +1 -1
- package/dist/core/plugin/install-dir.d.ts +6 -9
- package/dist/core/plugin/install-dir.d.ts.map +1 -1
- package/dist/core/plugin/install-dir.js +99 -79
- package/dist/core/plugin/install-dir.js.map +1 -1
- package/dist/core/plugin/install-validate.d.ts +2 -1
- package/dist/core/plugin/install-validate.d.ts.map +1 -1
- package/dist/core/plugin/install-validate.js +25 -17
- package/dist/core/plugin/install-validate.js.map +1 -1
- package/dist/core/plugin/installer.d.ts +3 -3
- package/dist/core/plugin/installer.d.ts.map +1 -1
- package/dist/core/plugin/installer.js +143 -140
- package/dist/core/plugin/installer.js.map +1 -1
- package/dist/core/plugin/loader.d.ts +13 -1
- package/dist/core/plugin/loader.d.ts.map +1 -1
- package/dist/core/plugin/loader.js +106 -2
- package/dist/core/plugin/loader.js.map +1 -1
- package/dist/core/plugin/types.d.ts +14 -5
- package/dist/core/plugin/types.d.ts.map +1 -1
- package/dist/core/plugin/types.js.map +1 -1
- package/dist/plugins/admin/index.d.ts.map +1 -1
- package/dist/plugins/admin/index.js +119 -86
- package/dist/plugins/admin/index.js.map +1 -1
- package/dist/plugins/core/index.d.ts.map +1 -1
- package/dist/plugins/core/index.js +5 -2
- package/dist/plugins/core/index.js.map +1 -1
- package/docs/architecture.md +22 -24
- package/docs/configuration.md +19 -23
- package/docs/feature-parity.md +4 -4
- package/docs/migration.md +13 -13
- package/docs/plugin-api.md +164 -27
- package/docs/plugin-development.md +400 -0
- package/docs/release.md +2 -2
- package/examples/plugins/hello/README.md +10 -5
- package/examples/plugins/hello/index.js +3 -6
- package/package.json +4 -1
- package/src/app.ts +18 -55
- package/src/core/config/schema.ts +0 -1
- package/src/core/dispatch/command.ts +478 -70
- package/src/core/dispatch/router.ts +77 -3
- package/src/core/index.ts +3 -0
- package/src/core/kernel.ts +8 -1
- package/src/core/plugin/command-params.ts +667 -0
- package/src/core/plugin/context.ts +51 -2
- package/src/core/plugin/install-dir.ts +117 -111
- package/src/core/plugin/install-validate.ts +36 -34
- package/src/core/plugin/installer.ts +228 -184
- package/src/core/plugin/loader.ts +180 -3
- package/src/core/plugin/types.ts +31 -5
- package/src/plugins/admin/index.ts +302 -233
- package/src/plugins/core/index.ts +6 -2
- package/dist/core/plugin/installed.d.ts +0 -9
- package/dist/core/plugin/installed.d.ts.map +0 -1
- package/dist/core/plugin/installed.js +0 -113
- package/dist/core/plugin/installed.js.map +0 -1
- package/src/core/plugin/installed.ts +0 -165
|
@@ -1,10 +1,21 @@
|
|
|
1
|
+
import type { OneBotMessageEvent } from '@snowluma/sdk'
|
|
2
|
+
import {
|
|
3
|
+
compileCommandParams,
|
|
4
|
+
type CommandParamArgs,
|
|
5
|
+
type CompiledCommandParams,
|
|
6
|
+
} from '../plugin/command-params.js'
|
|
1
7
|
import type {
|
|
8
|
+
CommandGroupOptions,
|
|
9
|
+
CommandGroupRegistrar,
|
|
2
10
|
CommandHandler,
|
|
3
11
|
CommandInfo,
|
|
4
12
|
CommandMatch,
|
|
13
|
+
CommandOptions,
|
|
5
14
|
} from '../plugin/types.js'
|
|
6
15
|
import type { Permission } from '../security/permission.js'
|
|
7
16
|
|
|
17
|
+
type AnyCommandHandler = CommandHandler<OneBotMessageEvent, any[]>
|
|
18
|
+
|
|
8
19
|
export interface CommandRegistration {
|
|
9
20
|
pluginId: string
|
|
10
21
|
name: string
|
|
@@ -12,7 +23,21 @@ export interface CommandRegistration {
|
|
|
12
23
|
prefixes: string[]
|
|
13
24
|
permission: Permission
|
|
14
25
|
description?: string | undefined
|
|
15
|
-
|
|
26
|
+
params?: string | undefined
|
|
27
|
+
paramsParser?: CompiledCommandParams | undefined
|
|
28
|
+
usage?: string | undefined
|
|
29
|
+
handler: AnyCommandHandler
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface CommandGroupRegistration {
|
|
33
|
+
pluginId: string
|
|
34
|
+
name: string
|
|
35
|
+
aliases: string[]
|
|
36
|
+
prefixes: string[]
|
|
37
|
+
permission: Permission
|
|
38
|
+
description?: string | undefined
|
|
39
|
+
params?: string | undefined
|
|
40
|
+
handler?: AnyCommandHandler | undefined
|
|
16
41
|
}
|
|
17
42
|
|
|
18
43
|
export interface RegisteredCommand {
|
|
@@ -20,80 +45,167 @@ export interface RegisteredCommand {
|
|
|
20
45
|
match: CommandMatch
|
|
21
46
|
}
|
|
22
47
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
interface GroupChild {
|
|
49
|
+
name: string
|
|
50
|
+
description?: string | undefined
|
|
51
|
+
usage?: string | undefined
|
|
52
|
+
}
|
|
27
53
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
),
|
|
35
|
-
]
|
|
36
|
-
const prefixes = [...new Set(registration.prefixes)]
|
|
54
|
+
interface GroupScope {
|
|
55
|
+
registrar: CommandGroupRegistrar
|
|
56
|
+
disposeAll: () => void
|
|
57
|
+
track: (dispose: () => void, cleanup?: () => void) => () => void
|
|
58
|
+
}
|
|
37
59
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const existing = this.index.get(prefix)?.get(name)
|
|
60
|
+
function normalizeSegment(label: string, value: string): string {
|
|
61
|
+
const trimmed = value.trim()
|
|
41
62
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
}
|
|
63
|
+
if (!trimmed) {
|
|
64
|
+
throw new Error(`${label} must not be empty`)
|
|
65
|
+
}
|
|
47
66
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
67
|
+
if (/\s/.test(trimmed)) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`${label} must be a single segment; use commands.group() for multi-level commands`
|
|
70
|
+
)
|
|
71
|
+
}
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
73
|
+
return trimmed
|
|
74
|
+
}
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
76
|
+
function uniqueStrings(values: string[]): string[] {
|
|
77
|
+
const seen = new Set<string>()
|
|
78
|
+
const result: string[] = []
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
for (const value of values) {
|
|
81
|
+
const key = value.toLowerCase()
|
|
60
82
|
|
|
61
|
-
|
|
62
|
-
this.byPlugin.get(registration.pluginId) ??
|
|
63
|
-
new Set<CommandRegistration>()
|
|
64
|
-
pluginSet.add(registration)
|
|
65
|
-
this.byPlugin.set(registration.pluginId, pluginSet)
|
|
83
|
+
if (seen.has(key)) continue
|
|
66
84
|
|
|
67
|
-
|
|
85
|
+
seen.add(key)
|
|
86
|
+
result.push(value)
|
|
87
|
+
}
|
|
68
88
|
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
return result
|
|
90
|
+
}
|
|
71
91
|
|
|
72
|
-
|
|
92
|
+
function groupBases(name: string, aliases: string[]): string[] {
|
|
93
|
+
return uniqueStrings([
|
|
94
|
+
normalizeSegment('group name', name),
|
|
95
|
+
...aliases.map((alias) => normalizeSegment('group alias', alias)),
|
|
96
|
+
])
|
|
97
|
+
}
|
|
73
98
|
|
|
74
|
-
|
|
75
|
-
|
|
99
|
+
function composedBases(
|
|
100
|
+
parentBases: string[],
|
|
101
|
+
name: string,
|
|
102
|
+
aliases: string[],
|
|
103
|
+
kind: 'command' | 'group'
|
|
104
|
+
): string[] {
|
|
105
|
+
const variants = uniqueStrings([
|
|
106
|
+
normalizeSegment(`${kind} name`, name),
|
|
107
|
+
...aliases.map((alias) => normalizeSegment(`${kind} alias`, alias)),
|
|
108
|
+
])
|
|
109
|
+
const bases: string[] = []
|
|
110
|
+
const seen = new Set<string>()
|
|
111
|
+
|
|
112
|
+
for (const parent of parentBases) {
|
|
113
|
+
for (const variant of variants) {
|
|
114
|
+
const path = `${parent} ${variant}`
|
|
115
|
+
const key = path.toLowerCase()
|
|
116
|
+
|
|
117
|
+
if (seen.has(key)) continue
|
|
118
|
+
|
|
119
|
+
seen.add(key)
|
|
120
|
+
bases.push(path)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
76
123
|
|
|
77
|
-
|
|
124
|
+
return bases
|
|
125
|
+
}
|
|
78
126
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
127
|
+
function createGroupUsageHandler(children: GroupChild[]): AnyCommandHandler {
|
|
128
|
+
return async (_event, context, match) => {
|
|
129
|
+
const root = `${match.prefix}${match.command}`
|
|
130
|
+
const names = children.map((child) => child.name)
|
|
131
|
+
const lines = [
|
|
132
|
+
names.length > 0
|
|
133
|
+
? `用法:${root} <${names.join('|')}>`
|
|
134
|
+
: `用法:${root}`,
|
|
135
|
+
]
|
|
84
136
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
137
|
+
for (const child of children) {
|
|
138
|
+
const usage = child.usage !== undefined ? ` ${child.usage}` : ''
|
|
139
|
+
const command = `${root} ${child.name}${usage}`
|
|
89
140
|
|
|
90
|
-
|
|
91
|
-
|
|
141
|
+
lines.push(
|
|
142
|
+
child.description !== undefined
|
|
143
|
+
? `${command} - ${child.description}`
|
|
144
|
+
: command
|
|
145
|
+
)
|
|
146
|
+
}
|
|
92
147
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
148
|
+
await context.send(lines.join('\n'))
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export class CommandRegistry {
|
|
153
|
+
private readonly index = new Map<string, Map<string, CommandRegistration>>()
|
|
154
|
+
private readonly registrations = new Set<CommandRegistration>()
|
|
155
|
+
private readonly byPlugin = new Map<string, Set<CommandRegistration>>()
|
|
156
|
+
|
|
157
|
+
register(registration: CommandRegistration): () => void {
|
|
158
|
+
const name = normalizeSegment('command name', registration.name)
|
|
159
|
+
const aliases = uniqueStrings(
|
|
160
|
+
registration.aliases.map((alias) =>
|
|
161
|
+
normalizeSegment('command alias', alias)
|
|
162
|
+
)
|
|
163
|
+
).filter((alias) => alias.toLowerCase() !== name.toLowerCase())
|
|
164
|
+
const normalized = this.withParams({
|
|
165
|
+
...registration,
|
|
166
|
+
name,
|
|
167
|
+
aliases,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
return this.insert(normalized, [name, ...aliases])
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
group(
|
|
174
|
+
registration: CommandGroupRegistration,
|
|
175
|
+
setup: (group: CommandGroupRegistrar) => void
|
|
176
|
+
): () => void {
|
|
177
|
+
const prefixes = uniqueStrings(registration.prefixes)
|
|
178
|
+
const bases = groupBases(registration.name, registration.aliases)
|
|
179
|
+
const children: GroupChild[] = []
|
|
180
|
+
const scope = this.createGroupScope(
|
|
181
|
+
registration.pluginId,
|
|
182
|
+
bases,
|
|
183
|
+
prefixes,
|
|
184
|
+
registration.permission,
|
|
185
|
+
children
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
setup(scope.registrar)
|
|
190
|
+
|
|
191
|
+
const dispose = this.registerGroupRoot(
|
|
192
|
+
registration.pluginId,
|
|
193
|
+
bases,
|
|
194
|
+
prefixes,
|
|
195
|
+
registration.permission,
|
|
196
|
+
registration.description,
|
|
197
|
+
registration.params,
|
|
198
|
+
registration.handler,
|
|
199
|
+
children
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
scope.track(dispose)
|
|
203
|
+
} catch (error) {
|
|
204
|
+
scope.disposeAll()
|
|
205
|
+
throw error
|
|
96
206
|
}
|
|
207
|
+
|
|
208
|
+
return scope.disposeAll
|
|
97
209
|
}
|
|
98
210
|
|
|
99
211
|
removePlugin(pluginId: string): void {
|
|
@@ -102,12 +214,11 @@ export class CommandRegistry {
|
|
|
102
214
|
if (!registrations) return
|
|
103
215
|
|
|
104
216
|
for (const registration of [...registrations]) {
|
|
105
|
-
const prefixes = registration.prefixes
|
|
106
217
|
const names = [registration.name, ...registration.aliases].map(
|
|
107
218
|
(name) => name.toLowerCase()
|
|
108
219
|
)
|
|
109
220
|
|
|
110
|
-
for (const prefix of prefixes) {
|
|
221
|
+
for (const prefix of registration.prefixes) {
|
|
111
222
|
const bucket = this.index.get(prefix)
|
|
112
223
|
|
|
113
224
|
if (!bucket) continue
|
|
@@ -139,6 +250,9 @@ export class CommandRegistry {
|
|
|
139
250
|
...(registration.description !== undefined
|
|
140
251
|
? { description: registration.description }
|
|
141
252
|
: {}),
|
|
253
|
+
...(registration.usage !== undefined
|
|
254
|
+
? { usage: registration.usage }
|
|
255
|
+
: {}),
|
|
142
256
|
pluginId: registration.pluginId,
|
|
143
257
|
}))
|
|
144
258
|
}
|
|
@@ -159,22 +273,316 @@ export class CommandRegistry {
|
|
|
159
273
|
|
|
160
274
|
if (!text) continue
|
|
161
275
|
|
|
162
|
-
const
|
|
163
|
-
|
|
276
|
+
const tokens = [...text.matchAll(/\S+/g)]
|
|
277
|
+
|
|
278
|
+
if (tokens.length === 0) continue
|
|
279
|
+
|
|
280
|
+
for (let count = tokens.length; count >= 1; count -= 1) {
|
|
281
|
+
const matched = tokens.slice(0, count)
|
|
282
|
+
const first = matched[0]
|
|
283
|
+
const last = matched[matched.length - 1]
|
|
284
|
+
|
|
285
|
+
if (!first || !last) continue
|
|
286
|
+
|
|
287
|
+
const key = matched
|
|
288
|
+
.map((token) => token[0])
|
|
289
|
+
.join(' ')
|
|
290
|
+
.toLowerCase()
|
|
291
|
+
const registration = this.index.get(prefix)?.get(key)
|
|
164
292
|
|
|
165
|
-
|
|
293
|
+
if (!registration) continue
|
|
166
294
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
295
|
+
const start = first.index ?? 0
|
|
296
|
+
const end = (last.index ?? 0) + last[0].length
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
registration,
|
|
300
|
+
match: {
|
|
301
|
+
command: text
|
|
302
|
+
.slice(start, end)
|
|
303
|
+
.replace(/\s+/g, ' ')
|
|
304
|
+
.trim(),
|
|
305
|
+
prefix,
|
|
306
|
+
args: tokens.slice(count).map((token) => token[0]),
|
|
307
|
+
rest: text.slice(end).trim(),
|
|
308
|
+
},
|
|
309
|
+
}
|
|
175
310
|
}
|
|
176
311
|
}
|
|
177
312
|
|
|
178
313
|
return null
|
|
179
314
|
}
|
|
315
|
+
|
|
316
|
+
private withParams(registration: CommandRegistration): CommandRegistration {
|
|
317
|
+
if (registration.params === undefined) return registration
|
|
318
|
+
|
|
319
|
+
const compiled = compileCommandParams(registration.params)
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
...registration,
|
|
323
|
+
paramsParser: compiled,
|
|
324
|
+
usage: compiled.usage,
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private insert(
|
|
329
|
+
registration: CommandRegistration,
|
|
330
|
+
paths: string[]
|
|
331
|
+
): () => void {
|
|
332
|
+
const prefixes = uniqueStrings(registration.prefixes)
|
|
333
|
+
const names = uniqueStrings(paths)
|
|
334
|
+
|
|
335
|
+
for (const prefix of prefixes) {
|
|
336
|
+
for (const name of names) {
|
|
337
|
+
const existing = this.index.get(prefix)?.get(name.toLowerCase())
|
|
338
|
+
|
|
339
|
+
if (existing) {
|
|
340
|
+
throw new Error(`command conflict: ${prefix}${name}`)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (const prefix of prefixes) {
|
|
346
|
+
const bucket =
|
|
347
|
+
this.index.get(prefix) ?? new Map<string, CommandRegistration>()
|
|
348
|
+
|
|
349
|
+
for (const name of names) {
|
|
350
|
+
bucket.set(name.toLowerCase(), registration)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.index.set(prefix, bucket)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
this.registrations.add(registration)
|
|
357
|
+
|
|
358
|
+
const pluginSet =
|
|
359
|
+
this.byPlugin.get(registration.pluginId) ??
|
|
360
|
+
new Set<CommandRegistration>()
|
|
361
|
+
pluginSet.add(registration)
|
|
362
|
+
this.byPlugin.set(registration.pluginId, pluginSet)
|
|
363
|
+
|
|
364
|
+
let disposed = false
|
|
365
|
+
|
|
366
|
+
return () => {
|
|
367
|
+
if (disposed) return
|
|
368
|
+
|
|
369
|
+
disposed = true
|
|
370
|
+
|
|
371
|
+
for (const prefix of prefixes) {
|
|
372
|
+
const bucket = this.index.get(prefix)
|
|
373
|
+
|
|
374
|
+
if (!bucket) continue
|
|
375
|
+
|
|
376
|
+
for (const name of names) {
|
|
377
|
+
if (bucket.get(name.toLowerCase()) === registration) {
|
|
378
|
+
bucket.delete(name.toLowerCase())
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (bucket.size === 0) {
|
|
383
|
+
this.index.delete(prefix)
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
this.registrations.delete(registration)
|
|
388
|
+
pluginSet.delete(registration)
|
|
389
|
+
|
|
390
|
+
if (pluginSet.size === 0) {
|
|
391
|
+
this.byPlugin.delete(registration.pluginId)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private createGroupScope(
|
|
397
|
+
pluginId: string,
|
|
398
|
+
bases: string[],
|
|
399
|
+
prefixes: string[],
|
|
400
|
+
permission: Permission,
|
|
401
|
+
children: GroupChild[]
|
|
402
|
+
): GroupScope {
|
|
403
|
+
const disposers: Array<() => void> = []
|
|
404
|
+
let disposed = false
|
|
405
|
+
|
|
406
|
+
const track = (
|
|
407
|
+
dispose: () => void,
|
|
408
|
+
cleanup?: () => void
|
|
409
|
+
): (() => void) => {
|
|
410
|
+
let done = false
|
|
411
|
+
|
|
412
|
+
const tracked = (): void => {
|
|
413
|
+
if (done) return
|
|
414
|
+
|
|
415
|
+
done = true
|
|
416
|
+
cleanup?.()
|
|
417
|
+
dispose()
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
disposers.push(tracked)
|
|
421
|
+
|
|
422
|
+
return tracked
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const disposeAll = (): void => {
|
|
426
|
+
if (disposed) return
|
|
427
|
+
|
|
428
|
+
disposed = true
|
|
429
|
+
|
|
430
|
+
for (const dispose of disposers.splice(0).reverse()) {
|
|
431
|
+
dispose()
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const registrar: CommandGroupRegistrar = {
|
|
436
|
+
register: <Declaration extends string = string>(
|
|
437
|
+
options: CommandOptions<Declaration>,
|
|
438
|
+
handler: CommandHandler<
|
|
439
|
+
OneBotMessageEvent,
|
|
440
|
+
CommandParamArgs<Declaration>
|
|
441
|
+
>
|
|
442
|
+
) => {
|
|
443
|
+
const childBases = composedBases(
|
|
444
|
+
bases,
|
|
445
|
+
options.name,
|
|
446
|
+
options.aliases ?? [],
|
|
447
|
+
'command'
|
|
448
|
+
)
|
|
449
|
+
const name = childBases[0]
|
|
450
|
+
|
|
451
|
+
if (!name) {
|
|
452
|
+
throw new Error('command name must not be empty')
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const registration = this.withParams({
|
|
456
|
+
pluginId,
|
|
457
|
+
name,
|
|
458
|
+
aliases: childBases.slice(1),
|
|
459
|
+
prefixes: options.prefixes ?? prefixes,
|
|
460
|
+
permission: options.permission ?? permission,
|
|
461
|
+
...(options.description !== undefined
|
|
462
|
+
? { description: options.description }
|
|
463
|
+
: {}),
|
|
464
|
+
...(options.params !== undefined
|
|
465
|
+
? { params: options.params }
|
|
466
|
+
: {}),
|
|
467
|
+
handler,
|
|
468
|
+
})
|
|
469
|
+
const dispose = this.insert(registration, childBases)
|
|
470
|
+
const record: GroupChild = {
|
|
471
|
+
name: options.name.trim(),
|
|
472
|
+
...(options.description !== undefined
|
|
473
|
+
? { description: options.description }
|
|
474
|
+
: {}),
|
|
475
|
+
...(registration.usage !== undefined
|
|
476
|
+
? { usage: registration.usage }
|
|
477
|
+
: {}),
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
children.push(record)
|
|
481
|
+
|
|
482
|
+
return track(dispose, () => {
|
|
483
|
+
const index = children.indexOf(record)
|
|
484
|
+
|
|
485
|
+
if (index >= 0) {
|
|
486
|
+
children.splice(index, 1)
|
|
487
|
+
}
|
|
488
|
+
})
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
group: <Declaration extends string = string>(
|
|
492
|
+
options: CommandGroupOptions<Declaration>,
|
|
493
|
+
nestedSetup: (group: CommandGroupRegistrar) => void
|
|
494
|
+
) => {
|
|
495
|
+
const nestedBases = composedBases(
|
|
496
|
+
bases,
|
|
497
|
+
options.name,
|
|
498
|
+
options.aliases ?? [],
|
|
499
|
+
'group'
|
|
500
|
+
)
|
|
501
|
+
const nestedPrefixes = options.prefixes ?? prefixes
|
|
502
|
+
const nestedPermission = options.permission ?? permission
|
|
503
|
+
const nestedChildren: GroupChild[] = []
|
|
504
|
+
const nestedScope = this.createGroupScope(
|
|
505
|
+
pluginId,
|
|
506
|
+
nestedBases,
|
|
507
|
+
nestedPrefixes,
|
|
508
|
+
nestedPermission,
|
|
509
|
+
nestedChildren
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
nestedSetup(nestedScope.registrar)
|
|
514
|
+
|
|
515
|
+
const dispose = this.registerGroupRoot(
|
|
516
|
+
pluginId,
|
|
517
|
+
nestedBases,
|
|
518
|
+
nestedPrefixes,
|
|
519
|
+
nestedPermission,
|
|
520
|
+
options.description,
|
|
521
|
+
options.params,
|
|
522
|
+
options.handler,
|
|
523
|
+
nestedChildren
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
nestedScope.track(dispose)
|
|
527
|
+
} catch (error) {
|
|
528
|
+
nestedScope.disposeAll()
|
|
529
|
+
throw error
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const record: GroupChild = {
|
|
533
|
+
name: options.name.trim(),
|
|
534
|
+
...(options.description !== undefined
|
|
535
|
+
? { description: options.description }
|
|
536
|
+
: {}),
|
|
537
|
+
...(options.params !== undefined
|
|
538
|
+
? {
|
|
539
|
+
usage: compileCommandParams(options.params).usage,
|
|
540
|
+
}
|
|
541
|
+
: {}),
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
children.push(record)
|
|
545
|
+
|
|
546
|
+
return track(nestedScope.disposeAll, () => {
|
|
547
|
+
const index = children.indexOf(record)
|
|
548
|
+
|
|
549
|
+
if (index >= 0) {
|
|
550
|
+
children.splice(index, 1)
|
|
551
|
+
}
|
|
552
|
+
})
|
|
553
|
+
},
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return { registrar, disposeAll, track }
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private registerGroupRoot(
|
|
560
|
+
pluginId: string,
|
|
561
|
+
bases: string[],
|
|
562
|
+
prefixes: string[],
|
|
563
|
+
permission: Permission,
|
|
564
|
+
description: string | undefined,
|
|
565
|
+
params: string | undefined,
|
|
566
|
+
handler: AnyCommandHandler | undefined,
|
|
567
|
+
children: GroupChild[]
|
|
568
|
+
): () => void {
|
|
569
|
+
const name = bases[0]
|
|
570
|
+
|
|
571
|
+
if (!name) {
|
|
572
|
+
throw new Error('group name must not be empty')
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const registration = this.withParams({
|
|
576
|
+
pluginId,
|
|
577
|
+
name,
|
|
578
|
+
aliases: bases.slice(1),
|
|
579
|
+
prefixes,
|
|
580
|
+
permission,
|
|
581
|
+
...(description !== undefined ? { description } : {}),
|
|
582
|
+
...(params !== undefined ? { params } : {}),
|
|
583
|
+
handler: handler ?? createGroupUsageHandler(children),
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
return this.insert(registration, bases)
|
|
587
|
+
}
|
|
180
588
|
}
|