@jsbot-tellus/core 2.1.0 → 2.3.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 +14 -14
- package/dist/app.js +4 -2
- package/dist/app.js.map +1 -1
- package/dist/core/dispatch/command.d.ts.map +1 -1
- package/dist/core/dispatch/command.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- 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 +23 -1
- package/dist/core/plugin/command-params.d.ts.map +1 -1
- package/dist/core/plugin/command-params.js +35 -0
- package/dist/core/plugin/command-params.js.map +1 -1
- package/dist/core/plugin/context.d.ts.map +1 -1
- package/dist/core/plugin/context.js.map +1 -1
- package/dist/core/plugin/loader.d.ts +13 -2
- package/dist/core/plugin/loader.d.ts.map +1 -1
- package/dist/core/plugin/loader.js +87 -5
- package/dist/core/plugin/loader.js.map +1 -1
- package/dist/core/plugin/types.d.ts +8 -7
- package/dist/core/plugin/types.d.ts.map +1 -1
- package/dist/core/plugin/types.js.map +1 -1
- package/dist/plugins/core/index.js +1 -1
- package/dist/plugins/core/index.js.map +1 -1
- package/docs/architecture.md +3 -3
- package/docs/configuration.md +3 -3
- package/docs/feature-parity.md +2 -2
- package/docs/plugin-api.md +12 -4
- package/docs/plugin-development.md +34 -24
- package/package.json +3 -1
- package/src/app.ts +4 -2
- package/src/core/dispatch/command.ts +9 -5
- package/src/core/index.ts +1 -0
- package/src/core/kernel.ts +8 -1
- package/src/core/plugin/command-params.ts +119 -1
- package/src/core/plugin/context.ts +9 -5
- package/src/core/plugin/loader.ts +150 -7
- package/src/core/plugin/types.ts +17 -11
- package/src/plugins/core/index.ts +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { parseSegments } from '@snowluma/sdk'
|
|
2
|
+
|
|
1
3
|
export type CommandParamType =
|
|
2
|
-
'string' | 'text' | 'number' | 'integer' | 'boolean'
|
|
4
|
+
'string' | 'text' | 'number' | 'integer' | 'boolean' | 'at'
|
|
3
5
|
|
|
4
6
|
export interface ParsedCommandParams {
|
|
5
7
|
args: unknown[]
|
|
@@ -48,6 +50,7 @@ function parseType(value: string): CommandParamType {
|
|
|
48
50
|
case 'number':
|
|
49
51
|
case 'integer':
|
|
50
52
|
case 'boolean':
|
|
53
|
+
case 'at':
|
|
51
54
|
return value
|
|
52
55
|
default:
|
|
53
56
|
throw declarationError(`未知的参数类型 "${value}"`)
|
|
@@ -177,6 +180,35 @@ function parseBoolean(value: string): boolean | null {
|
|
|
177
180
|
return null
|
|
178
181
|
}
|
|
179
182
|
|
|
183
|
+
function parseAt(value: string, name: string, usage: string): number {
|
|
184
|
+
const raw = value.trim()
|
|
185
|
+
const segments = parseSegments(raw)
|
|
186
|
+
const segment = segments[0]
|
|
187
|
+
|
|
188
|
+
if (segments.length === 1 && segment?.type === 'at') {
|
|
189
|
+
const qq = segment.data.qq
|
|
190
|
+
|
|
191
|
+
if (qq !== 'all') {
|
|
192
|
+
const parsed = Number(qq)
|
|
193
|
+
|
|
194
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) {
|
|
195
|
+
return parsed
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} else if (/^\d+$/.test(raw)) {
|
|
199
|
+
const parsed = Number(raw)
|
|
200
|
+
|
|
201
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) {
|
|
202
|
+
return parsed
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
throw new CommandParamsError(
|
|
207
|
+
`参数 ${name} 需要 @ 一个用户或填写 QQ 号`,
|
|
208
|
+
usage
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
180
212
|
function parseDefaultValue(
|
|
181
213
|
type: CommandParamType,
|
|
182
214
|
raw: string,
|
|
@@ -215,6 +247,8 @@ function parseDefaultValue(
|
|
|
215
247
|
|
|
216
248
|
return boolean
|
|
217
249
|
}
|
|
250
|
+
case 'at':
|
|
251
|
+
throw declarationError(`参数 ${name} 不支持默认值`)
|
|
218
252
|
}
|
|
219
253
|
}
|
|
220
254
|
|
|
@@ -405,6 +439,16 @@ function tokenizeInput(input: string, usage: string): InputToken[] {
|
|
|
405
439
|
|
|
406
440
|
if (/\s/.test(current)) break
|
|
407
441
|
|
|
442
|
+
if (current === '[' && input.startsWith('[CQ:', index)) {
|
|
443
|
+
const close = input.indexOf(']', index + 4)
|
|
444
|
+
|
|
445
|
+
if (close >= 0) {
|
|
446
|
+
value += input.slice(index, close + 1)
|
|
447
|
+
index = close + 1
|
|
448
|
+
continue
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
408
452
|
if (current === '"' || current === "'") {
|
|
409
453
|
quote = current
|
|
410
454
|
index += 1
|
|
@@ -480,6 +524,8 @@ function parseValue(spec: ParamSpec, raw: string, usage: string): unknown {
|
|
|
480
524
|
|
|
481
525
|
return value
|
|
482
526
|
}
|
|
527
|
+
case 'at':
|
|
528
|
+
return parseAt(raw, spec.name, usage)
|
|
483
529
|
}
|
|
484
530
|
}
|
|
485
531
|
|
|
@@ -593,3 +639,75 @@ export function compileCommandParams(
|
|
|
593
639
|
},
|
|
594
640
|
}
|
|
595
641
|
}
|
|
642
|
+
|
|
643
|
+
type Whitespace = ' ' | '\n' | '\r' | '\t' | '\v' | '\f'
|
|
644
|
+
|
|
645
|
+
type TrimLeft<Source extends string> =
|
|
646
|
+
Source extends `${Whitespace}${infer Rest}` ? TrimLeft<Rest> : Source
|
|
647
|
+
|
|
648
|
+
type TrimRight<Source extends string> =
|
|
649
|
+
Source extends `${infer Rest}${Whitespace}` ? TrimRight<Rest> : Source
|
|
650
|
+
|
|
651
|
+
type Trim<Source extends string> = TrimLeft<TrimRight<Source>>
|
|
652
|
+
|
|
653
|
+
type ParamValue<Type extends string> =
|
|
654
|
+
Trim<Type> extends 'string' | 'text'
|
|
655
|
+
? string
|
|
656
|
+
: Trim<Type> extends 'number' | 'integer' | 'at'
|
|
657
|
+
? number
|
|
658
|
+
: Trim<Type> extends 'boolean'
|
|
659
|
+
? boolean
|
|
660
|
+
: unknown
|
|
661
|
+
|
|
662
|
+
interface VariadicParam {
|
|
663
|
+
readonly variadic: true
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
type ParamType<Head extends string> = Head extends `${string}:${infer Type}`
|
|
667
|
+
? ParamValue<Type>
|
|
668
|
+
: string
|
|
669
|
+
|
|
670
|
+
type ParamElement<Body extends string, Required extends boolean> =
|
|
671
|
+
Trim<Body> extends `...${string}`
|
|
672
|
+
? VariadicParam
|
|
673
|
+
: Trim<Body> extends `${infer Head}=${string}`
|
|
674
|
+
? ParamType<Head>
|
|
675
|
+
: Required extends true
|
|
676
|
+
? ParamType<Body>
|
|
677
|
+
: ParamType<Body> | undefined
|
|
678
|
+
|
|
679
|
+
type AppendParam<Acc extends unknown[], Element> = [Element] extends [
|
|
680
|
+
VariadicParam,
|
|
681
|
+
]
|
|
682
|
+
? [...Acc, ...string[]]
|
|
683
|
+
: [...Acc, Element]
|
|
684
|
+
|
|
685
|
+
type ParseCommandParams<Source extends string, Acc extends unknown[] = []> =
|
|
686
|
+
Trim<Source> extends ''
|
|
687
|
+
? Acc
|
|
688
|
+
: Trim<Source> extends `<${infer Body}>${infer Rest}`
|
|
689
|
+
? ParamElement<Body, true> extends VariadicParam
|
|
690
|
+
? any[]
|
|
691
|
+
: ParseCommandParams<
|
|
692
|
+
Rest,
|
|
693
|
+
AppendParam<Acc, ParamElement<Body, true>>
|
|
694
|
+
>
|
|
695
|
+
: Trim<Source> extends `[${infer Body}]${infer Rest}`
|
|
696
|
+
? ParamElement<Body, false> extends VariadicParam
|
|
697
|
+
? Trim<Rest> extends ''
|
|
698
|
+
? [...Acc, ...string[]]
|
|
699
|
+
: any[]
|
|
700
|
+
: ParseCommandParams<
|
|
701
|
+
Rest,
|
|
702
|
+
AppendParam<Acc, ParamElement<Body, false>>
|
|
703
|
+
>
|
|
704
|
+
: any[]
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* 把 `params` 声明字符串推导成 handler 的位置参数 tuple。
|
|
708
|
+
*
|
|
709
|
+
* 非字面量声明(例如运行时拼接的 `string`)或无法解析的声明会回退为
|
|
710
|
+
* `any[]`,以保证现有插件仍然可以编译。
|
|
711
|
+
*/
|
|
712
|
+
export type CommandParamArgs<Declaration extends string> =
|
|
713
|
+
string extends Declaration ? any[] : ParseCommandParams<Declaration>
|
|
@@ -4,6 +4,7 @@ import type { Router } from '../dispatch/router.js'
|
|
|
4
4
|
import type { TaskQueue } from '../dispatch/task-queue.js'
|
|
5
5
|
import type { Logger } from '../logging/logger.js'
|
|
6
6
|
import type { SessionStore } from '../storage/session.js'
|
|
7
|
+
import type { CommandParamArgs } from './command-params.js'
|
|
7
8
|
import type { PluginKV } from './kv.js'
|
|
8
9
|
import type {
|
|
9
10
|
CommandGroupOptions,
|
|
@@ -43,9 +44,12 @@ export function createPluginContext<TConfig>(
|
|
|
43
44
|
core: deps.core,
|
|
44
45
|
|
|
45
46
|
commands: {
|
|
46
|
-
register<
|
|
47
|
-
options: CommandOptions
|
|
48
|
-
handler: CommandHandler<
|
|
47
|
+
register<Declaration extends string = string>(
|
|
48
|
+
options: CommandOptions<Declaration>,
|
|
49
|
+
handler: CommandHandler<
|
|
50
|
+
OneBotMessageEvent,
|
|
51
|
+
CommandParamArgs<Declaration>
|
|
52
|
+
>
|
|
49
53
|
) {
|
|
50
54
|
return deps.router.registerCommand({
|
|
51
55
|
pluginId: id,
|
|
@@ -68,8 +72,8 @@ export function createPluginContext<TConfig>(
|
|
|
68
72
|
handler,
|
|
69
73
|
})
|
|
70
74
|
},
|
|
71
|
-
group<
|
|
72
|
-
options: CommandGroupOptions<
|
|
75
|
+
group<Declaration extends string = string>(
|
|
76
|
+
options: CommandGroupOptions<Declaration>,
|
|
73
77
|
setup: (group: CommandGroupRegistrar) => void
|
|
74
78
|
) {
|
|
75
79
|
return deps.router.registerCommandGroup(
|
|
@@ -4,9 +4,103 @@ import { pathToFileURL } from 'node:url'
|
|
|
4
4
|
import fg from 'fast-glob'
|
|
5
5
|
import type { Logger } from '../logging/logger.js'
|
|
6
6
|
import { collectPluginExports } from './exports.js'
|
|
7
|
+
import { readInstallState, readJsonFile } from './install-dir.js'
|
|
8
|
+
import { NpmCliRunner, type NpmRunner } from './npm-runner.js'
|
|
7
9
|
import type { AnyPluginDefinition } from './types.js'
|
|
8
10
|
|
|
9
|
-
type LoaderLogger = Pick<Logger, 'warn' | 'error'>
|
|
11
|
+
type LoaderLogger = Pick<Logger, 'warn' | 'error'> & {
|
|
12
|
+
info?: Logger['info'] | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LocalPluginLoaderOptions {
|
|
16
|
+
/** Build local plugins that have a build script but no dist. Defaults to true. */
|
|
17
|
+
autoBuild?: boolean | undefined
|
|
18
|
+
/** Timeout for a single local plugin build. Defaults to 120 seconds. */
|
|
19
|
+
buildTimeoutMs?: number | undefined
|
|
20
|
+
/** npm runner override, mainly for tests. */
|
|
21
|
+
runner?: NpmRunner | undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_BUILD_TIMEOUT_MS = 120_000
|
|
25
|
+
|
|
26
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
27
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function directoryExists(path: string): Promise<boolean> {
|
|
31
|
+
const info = await stat(path).catch(() => null)
|
|
32
|
+
|
|
33
|
+
return info?.isDirectory() ?? false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function readBuildScript(directory: string): Promise<string | undefined> {
|
|
37
|
+
const manifest = await readJsonFile(join(directory, 'package.json')).catch(
|
|
38
|
+
() => undefined
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if (!isRecord(manifest) || !isRecord(manifest['scripts'])) {
|
|
42
|
+
return undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const build = manifest['scripts']['build']
|
|
46
|
+
|
|
47
|
+
return typeof build === 'string' && build.trim().length > 0
|
|
48
|
+
? build
|
|
49
|
+
: undefined
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readManagedDirectories(
|
|
53
|
+
root: string
|
|
54
|
+
): Promise<Set<string> | null> {
|
|
55
|
+
try {
|
|
56
|
+
const state = await readInstallState(root)
|
|
57
|
+
|
|
58
|
+
return new Set(
|
|
59
|
+
Object.values(state.packages).map((record) => record.dir)
|
|
60
|
+
)
|
|
61
|
+
} catch {
|
|
62
|
+
return null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function buildLocalPlugin(
|
|
67
|
+
directory: string,
|
|
68
|
+
name: string,
|
|
69
|
+
logger: LoaderLogger | undefined,
|
|
70
|
+
runner: NpmRunner,
|
|
71
|
+
timeoutMs: number
|
|
72
|
+
): Promise<boolean> {
|
|
73
|
+
if ((await readBuildScript(directory)) === undefined) {
|
|
74
|
+
logger?.warn(
|
|
75
|
+
`skipping local plugin ${name}: dist directory not found and package.json has no build script`
|
|
76
|
+
)
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!(await directoryExists(join(directory, 'node_modules')))) {
|
|
81
|
+
logger?.warn(
|
|
82
|
+
`skipping local plugin ${name}: dist directory not found and node_modules is missing; run npm install --prefix ${directory}`
|
|
83
|
+
)
|
|
84
|
+
return false
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
logger?.info?.(`building local plugin ${name}: dist directory not found`)
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
await runner.run(['run', 'build'], {
|
|
91
|
+
cwd: directory,
|
|
92
|
+
timeoutMs,
|
|
93
|
+
})
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger?.error(`failed to build local plugin ${name}`, error)
|
|
96
|
+
logger?.warn(`skipping local plugin ${name}: build failed`)
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger?.info?.(`built local plugin ${name}`)
|
|
101
|
+
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
10
104
|
|
|
11
105
|
export async function loadPluginsFromDirectory(
|
|
12
106
|
directory: string,
|
|
@@ -63,7 +157,8 @@ export async function loadPluginsFromDirectory(
|
|
|
63
157
|
|
|
64
158
|
export async function loadLocalPlugins(
|
|
65
159
|
root: string,
|
|
66
|
-
logger?: LoaderLogger | undefined
|
|
160
|
+
logger?: LoaderLogger | undefined,
|
|
161
|
+
options: LocalPluginLoaderOptions = {}
|
|
67
162
|
): Promise<AnyPluginDefinition[]> {
|
|
68
163
|
const absolute = resolve(root)
|
|
69
164
|
const info = await stat(absolute).catch(() => null)
|
|
@@ -75,18 +170,66 @@ export async function loadLocalPlugins(
|
|
|
75
170
|
)
|
|
76
171
|
const plugins: AnyPluginDefinition[] = []
|
|
77
172
|
|
|
173
|
+
let autoBuild = options.autoBuild !== false
|
|
174
|
+
let managed = new Set<string>()
|
|
175
|
+
|
|
176
|
+
if (autoBuild) {
|
|
177
|
+
const directories = await readManagedDirectories(absolute)
|
|
178
|
+
|
|
179
|
+
if (directories === null) {
|
|
180
|
+
logger?.warn(
|
|
181
|
+
'cannot read plugin install state; automatic local plugin build is disabled'
|
|
182
|
+
)
|
|
183
|
+
autoBuild = false
|
|
184
|
+
} else {
|
|
185
|
+
managed = directories
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const runner = options.runner ?? new NpmCliRunner()
|
|
190
|
+
const timeoutMs = options.buildTimeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS
|
|
191
|
+
|
|
78
192
|
for (const entry of entries) {
|
|
79
193
|
if (!entry.isDirectory() || entry.name.startsWith('.')) continue
|
|
80
194
|
if (entry.name === 'node_modules') continue
|
|
81
195
|
|
|
82
|
-
const
|
|
83
|
-
const
|
|
196
|
+
const directory = join(absolute, entry.name)
|
|
197
|
+
const dist = join(directory, 'dist')
|
|
198
|
+
let distInfo = await stat(dist).catch(() => null)
|
|
84
199
|
|
|
85
200
|
if (!distInfo?.isDirectory()) {
|
|
86
|
-
|
|
87
|
-
|
|
201
|
+
if (!autoBuild) {
|
|
202
|
+
logger?.warn(
|
|
203
|
+
`skipping local plugin ${entry.name}: dist directory not found`
|
|
204
|
+
)
|
|
205
|
+
continue
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (managed.has(entry.name)) {
|
|
209
|
+
logger?.warn(
|
|
210
|
+
`skipping local plugin ${entry.name}: dist directory not found (installed package is expected to include dist)`
|
|
211
|
+
)
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const built = await buildLocalPlugin(
|
|
216
|
+
directory,
|
|
217
|
+
entry.name,
|
|
218
|
+
logger,
|
|
219
|
+
runner,
|
|
220
|
+
timeoutMs
|
|
88
221
|
)
|
|
89
|
-
|
|
222
|
+
|
|
223
|
+
if (!built) continue
|
|
224
|
+
|
|
225
|
+
distInfo = await stat(dist).catch(() => null)
|
|
226
|
+
|
|
227
|
+
if (!distInfo?.isDirectory()) {
|
|
228
|
+
logger?.warn(
|
|
229
|
+
`skipping local plugin ${entry.name}: build finished but dist directory not found`
|
|
230
|
+
)
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
90
233
|
}
|
|
91
234
|
|
|
92
235
|
plugins.push(...(await loadPluginsFromDirectory(dist, logger)))
|
package/src/core/plugin/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { SendOptions } from '../messaging/sender.js'
|
|
|
13
13
|
import type { PluginKV } from '../plugin/kv.js'
|
|
14
14
|
import type { Permission } from '../security/permission.js'
|
|
15
15
|
import type { SessionStore } from '../storage/session.js'
|
|
16
|
+
import type { CommandParamArgs } from './command-params.js'
|
|
16
17
|
|
|
17
18
|
export type EventType = 'message' | 'notice' | 'request' | 'meta'
|
|
18
19
|
|
|
@@ -74,28 +75,33 @@ export type PluginMiddleware = (
|
|
|
74
75
|
next: () => Promise<void>
|
|
75
76
|
) => void | Promise<void>
|
|
76
77
|
|
|
77
|
-
export interface CommandOptions {
|
|
78
|
+
export interface CommandOptions<Declaration extends string = string> {
|
|
78
79
|
name: string
|
|
79
80
|
aliases?: string[] | undefined
|
|
80
81
|
prefixes?: string[] | undefined
|
|
81
82
|
permission?: Permission | undefined
|
|
82
83
|
description?: string | undefined
|
|
83
|
-
params?:
|
|
84
|
+
params?: Declaration | undefined
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
export interface CommandGroupOptions<
|
|
87
|
-
|
|
88
|
-
> extends CommandOptions {
|
|
89
|
-
handler?:
|
|
88
|
+
Declaration extends string = string,
|
|
89
|
+
> extends CommandOptions<Declaration> {
|
|
90
|
+
handler?:
|
|
91
|
+
| CommandHandler<OneBotMessageEvent, CommandParamArgs<Declaration>>
|
|
92
|
+
| undefined
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
export interface CommandGroupRegistrar {
|
|
93
|
-
register<
|
|
94
|
-
options: CommandOptions
|
|
95
|
-
handler: CommandHandler<
|
|
96
|
+
register<Declaration extends string = string>(
|
|
97
|
+
options: CommandOptions<Declaration>,
|
|
98
|
+
handler: CommandHandler<
|
|
99
|
+
OneBotMessageEvent,
|
|
100
|
+
CommandParamArgs<Declaration>
|
|
101
|
+
>
|
|
96
102
|
): () => void
|
|
97
|
-
group<
|
|
98
|
-
options: CommandGroupOptions<
|
|
103
|
+
group<Declaration extends string = string>(
|
|
104
|
+
options: CommandGroupOptions<Declaration>,
|
|
99
105
|
setup: (group: CommandGroupRegistrar) => void
|
|
100
106
|
): () => void
|
|
101
107
|
}
|
|
@@ -184,7 +190,7 @@ export interface CoreServices {
|
|
|
184
190
|
can(actual: Permission, required: Permission): boolean
|
|
185
191
|
}
|
|
186
192
|
commands: {
|
|
187
|
-
list(): ReadonlyArray<CommandInfo>
|
|
193
|
+
list(event?: SnowLumaEvent): ReadonlyArray<CommandInfo>
|
|
188
194
|
}
|
|
189
195
|
plugins: PluginStatus
|
|
190
196
|
installer: PluginInstaller
|
|
@@ -38,7 +38,7 @@ export const corePlugin = definePlugin({
|
|
|
38
38
|
async (event, context) => {
|
|
39
39
|
const role = ctx.core.roles.roleOf(event.user_id)
|
|
40
40
|
const commands = ctx.core.commands
|
|
41
|
-
.list()
|
|
41
|
+
.list(event)
|
|
42
42
|
.filter((command) =>
|
|
43
43
|
ctx.core.roles.can(role, command.permission)
|
|
44
44
|
)
|