@jsbot-tellus/core 2.5.1 → 2.7.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 +16 -4
- package/dist/core/dispatch/command.d.ts +5 -2
- package/dist/core/dispatch/command.d.ts.map +1 -1
- package/dist/core/dispatch/command.js +33 -7
- package/dist/core/dispatch/command.js.map +1 -1
- package/dist/core/dispatch/handler.d.ts +5 -3
- package/dist/core/dispatch/handler.d.ts.map +1 -1
- package/dist/core/dispatch/handler.js +6 -1
- package/dist/core/dispatch/handler.js.map +1 -1
- package/dist/core/dispatch/router.d.ts +4 -2
- package/dist/core/dispatch/router.d.ts.map +1 -1
- package/dist/core/dispatch/router.js +9 -5
- package/dist/core/dispatch/router.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/kernel.d.ts +1 -1
- package/dist/core/kernel.d.ts.map +1 -1
- package/dist/core/kernel.js +4 -1
- package/dist/core/kernel.js.map +1 -1
- package/dist/core/plugin/context.d.ts.map +1 -1
- package/dist/core/plugin/context.js +12 -0
- package/dist/core/plugin/context.js.map +1 -1
- package/dist/core/plugin/loader.d.ts +1 -1
- package/dist/core/plugin/loader.d.ts.map +1 -1
- package/dist/core/plugin/loader.js +31 -46
- package/dist/core/plugin/loader.js.map +1 -1
- package/dist/core/plugin/params/declaration.d.ts.map +1 -1
- package/dist/core/plugin/params/declaration.js +67 -4
- package/dist/core/plugin/params/declaration.js.map +1 -1
- package/dist/core/plugin/params/input.d.ts.map +1 -1
- package/dist/core/plugin/params/input.js +17 -2
- package/dist/core/plugin/params/input.js.map +1 -1
- package/dist/core/plugin/params/type-level.d.ts +2 -1
- package/dist/core/plugin/params/type-level.d.ts.map +1 -1
- package/dist/core/plugin/params/types.d.ts +5 -0
- package/dist/core/plugin/params/types.d.ts.map +1 -1
- package/dist/core/plugin/params/types.js.map +1 -1
- package/dist/core/plugin/params/values.d.ts +1 -0
- package/dist/core/plugin/params/values.d.ts.map +1 -1
- package/dist/core/plugin/params/values.js +3 -0
- package/dist/core/plugin/params/values.js.map +1 -1
- package/dist/core/plugin/types.d.ts +9 -1
- package/dist/core/plugin/types.d.ts.map +1 -1
- package/dist/core/plugin/types.js.map +1 -1
- package/docs/architecture.md +3 -3
- package/docs/configuration.md +3 -3
- package/docs/docker.md +166 -0
- package/docs/feature-parity.md +2 -2
- package/docs/migration.md +1 -1
- package/docs/plugin-api.md +43 -15
- package/docs/plugin-development.md +30 -27
- package/docs/release.md +50 -8
- package/package.json +2 -2
- package/src/core/dispatch/command.ts +53 -1
- package/src/core/dispatch/handler.ts +19 -1
- package/src/core/dispatch/router.ts +20 -3
- package/src/core/index.ts +4 -0
- package/src/core/kernel.ts +8 -1
- package/src/core/plugin/context.ts +21 -0
- package/src/core/plugin/loader.ts +41 -66
- package/src/core/plugin/params/declaration.ts +112 -4
- package/src/core/plugin/params/input.ts +29 -2
- package/src/core/plugin/params/type-level.ts +7 -3
- package/src/core/plugin/params/types.ts +6 -0
- package/src/core/plugin/params/values.ts +4 -0
- package/src/core/plugin/types.ts +20 -0
|
@@ -4,7 +4,7 @@ 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 {
|
|
7
|
+
import { readJsonFile } from './install-dir.js'
|
|
8
8
|
import { NpmCliRunner, type NpmRunner } from './npm-runner.js'
|
|
9
9
|
import type { AnyPluginDefinition } from './types.js'
|
|
10
10
|
|
|
@@ -13,7 +13,7 @@ type LoaderLogger = Pick<Logger, 'warn' | 'error'> & {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface LocalPluginLoaderOptions {
|
|
16
|
-
/**
|
|
16
|
+
/** Rebuild local plugins that have a build script on every load. Defaults to true. */
|
|
17
17
|
autoBuild?: boolean | undefined
|
|
18
18
|
/** Timeout for a single local plugin build. Defaults to 120 seconds. */
|
|
19
19
|
buildTimeoutMs?: number | undefined
|
|
@@ -49,19 +49,7 @@ async function readBuildScript(directory: string): Promise<string | undefined> {
|
|
|
49
49
|
: undefined
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
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
|
-
}
|
|
52
|
+
type BuildOutcome = 'built' | 'missing-node-modules' | 'failed'
|
|
65
53
|
|
|
66
54
|
async function buildLocalPlugin(
|
|
67
55
|
directory: string,
|
|
@@ -69,22 +57,15 @@ async function buildLocalPlugin(
|
|
|
69
57
|
logger: LoaderLogger | undefined,
|
|
70
58
|
runner: NpmRunner,
|
|
71
59
|
timeoutMs: number
|
|
72
|
-
): Promise<
|
|
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
|
-
|
|
60
|
+
): Promise<BuildOutcome> {
|
|
80
61
|
if (!(await directoryExists(join(directory, 'node_modules')))) {
|
|
81
62
|
logger?.warn(
|
|
82
|
-
`
|
|
63
|
+
`cannot build local plugin ${name}: node_modules is missing; run npm install --prefix ${directory}`
|
|
83
64
|
)
|
|
84
|
-
return
|
|
65
|
+
return 'missing-node-modules'
|
|
85
66
|
}
|
|
86
67
|
|
|
87
|
-
logger?.info?.(`building local plugin ${name}
|
|
68
|
+
logger?.info?.(`building local plugin ${name}`)
|
|
88
69
|
|
|
89
70
|
try {
|
|
90
71
|
await runner.run(['run', 'build'], {
|
|
@@ -93,13 +74,12 @@ async function buildLocalPlugin(
|
|
|
93
74
|
})
|
|
94
75
|
} catch (error) {
|
|
95
76
|
logger?.error(`failed to build local plugin ${name}`, error)
|
|
96
|
-
|
|
97
|
-
return false
|
|
77
|
+
return 'failed'
|
|
98
78
|
}
|
|
99
79
|
|
|
100
80
|
logger?.info?.(`built local plugin ${name}`)
|
|
101
81
|
|
|
102
|
-
return
|
|
82
|
+
return 'built'
|
|
103
83
|
}
|
|
104
84
|
|
|
105
85
|
export async function loadPluginsFromDirectory(
|
|
@@ -127,7 +107,14 @@ export async function loadPluginsFromDirectory(
|
|
|
127
107
|
let imported: unknown
|
|
128
108
|
|
|
129
109
|
try {
|
|
130
|
-
|
|
110
|
+
const info = await stat(file).catch(() => null)
|
|
111
|
+
const url = pathToFileURL(file)
|
|
112
|
+
|
|
113
|
+
if (info !== null) {
|
|
114
|
+
url.searchParams.set('v', String(info.mtimeMs))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
imported = await import(url.href)
|
|
131
118
|
} catch (error) {
|
|
132
119
|
logger?.error(`failed to import plugin file: ${file}`, error)
|
|
133
120
|
invalid.push(
|
|
@@ -170,22 +157,7 @@ export async function loadLocalPlugins(
|
|
|
170
157
|
)
|
|
171
158
|
const plugins: AnyPluginDefinition[] = []
|
|
172
159
|
|
|
173
|
-
|
|
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
|
-
|
|
160
|
+
const autoBuild = options.autoBuild !== false
|
|
189
161
|
const runner = options.runner ?? new NpmCliRunner()
|
|
190
162
|
const timeoutMs = options.buildTimeoutMs ?? DEFAULT_BUILD_TIMEOUT_MS
|
|
191
163
|
|
|
@@ -195,24 +167,11 @@ export async function loadLocalPlugins(
|
|
|
195
167
|
|
|
196
168
|
const directory = join(absolute, entry.name)
|
|
197
169
|
const dist = join(directory, 'dist')
|
|
170
|
+
const hasBuildScript = (await readBuildScript(directory)) !== undefined
|
|
198
171
|
let distInfo = await stat(dist).catch(() => null)
|
|
199
172
|
|
|
200
|
-
if (
|
|
201
|
-
|
|
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(
|
|
173
|
+
if (autoBuild && hasBuildScript) {
|
|
174
|
+
const outcome = await buildLocalPlugin(
|
|
216
175
|
directory,
|
|
217
176
|
entry.name,
|
|
218
177
|
logger,
|
|
@@ -220,16 +179,32 @@ export async function loadLocalPlugins(
|
|
|
220
179
|
timeoutMs
|
|
221
180
|
)
|
|
222
181
|
|
|
223
|
-
if (!built) continue
|
|
224
|
-
|
|
225
182
|
distInfo = await stat(dist).catch(() => null)
|
|
226
183
|
|
|
227
184
|
if (!distInfo?.isDirectory()) {
|
|
185
|
+
const reason =
|
|
186
|
+
outcome === 'built'
|
|
187
|
+
? 'build finished but dist directory not found'
|
|
188
|
+
: outcome === 'missing-node-modules'
|
|
189
|
+
? 'node_modules is missing and dist directory not found'
|
|
190
|
+
: 'build failed and dist directory not found'
|
|
191
|
+
|
|
192
|
+
logger?.warn(`skipping local plugin ${entry.name}: ${reason}`)
|
|
193
|
+
continue
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (outcome !== 'built') {
|
|
228
197
|
logger?.warn(
|
|
229
|
-
`
|
|
198
|
+
`using existing dist for local plugin ${entry.name}: rebuild failed`
|
|
230
199
|
)
|
|
231
|
-
continue
|
|
232
200
|
}
|
|
201
|
+
} else if (!distInfo?.isDirectory()) {
|
|
202
|
+
logger?.warn(
|
|
203
|
+
hasBuildScript
|
|
204
|
+
? `skipping local plugin ${entry.name}: dist directory not found and automatic build is disabled`
|
|
205
|
+
: `skipping local plugin ${entry.name}: dist directory not found and package.json has no build script`
|
|
206
|
+
)
|
|
207
|
+
continue
|
|
233
208
|
}
|
|
234
209
|
|
|
235
210
|
plugins.push(...(await loadPluginsFromDirectory(dist, logger)))
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
declarationError,
|
|
3
3
|
type CommandParamType,
|
|
4
|
+
type ParamLength,
|
|
4
5
|
type ParamSpec,
|
|
5
6
|
} from './types.js'
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
measureLength,
|
|
9
|
+
parseBoolean,
|
|
10
|
+
parseTimeValue,
|
|
11
|
+
unquote,
|
|
12
|
+
} from './values.js'
|
|
7
13
|
|
|
8
14
|
const NAME_PATTERN = /^[\p{L}_][\p{L}\p{N}_-]*$/u
|
|
15
|
+
const LENGTH_PATTERN = /^(\d*)\.\.(\d*)$/
|
|
9
16
|
|
|
10
17
|
export function parseType(value: string): CommandParamType {
|
|
11
18
|
switch (value) {
|
|
@@ -23,6 +30,90 @@ export function parseType(value: string): CommandParamType {
|
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
interface ParsedType {
|
|
34
|
+
type: CommandParamType
|
|
35
|
+
length?: ParamLength | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseLengthNumber(raw: string, token: string): number {
|
|
39
|
+
const value = Number(raw)
|
|
40
|
+
|
|
41
|
+
if (!Number.isSafeInteger(value)) {
|
|
42
|
+
throw declarationError(`长度限制不是安全的整数:${token}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseLength(raw: string, token: string): ParamLength {
|
|
49
|
+
const value = raw.trim()
|
|
50
|
+
const match = LENGTH_PATTERN.exec(value)
|
|
51
|
+
|
|
52
|
+
if (!match) {
|
|
53
|
+
throw declarationError(`非法的长度限制 "${raw}":${token}`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const minRaw = match[1] ?? ''
|
|
57
|
+
const maxRaw = match[2] ?? ''
|
|
58
|
+
|
|
59
|
+
if (minRaw === '' && maxRaw === '') {
|
|
60
|
+
throw declarationError(`长度限制不能为空:${token}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const min = minRaw === '' ? undefined : parseLengthNumber(minRaw, token)
|
|
64
|
+
const max = maxRaw === '' ? undefined : parseLengthNumber(maxRaw, token)
|
|
65
|
+
|
|
66
|
+
if (min !== undefined && max !== undefined && min > max) {
|
|
67
|
+
throw declarationError(`长度下限不能大于上限:${token}`)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { min, max }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseTypeDeclaration(raw: string, token: string): ParsedType {
|
|
74
|
+
const value = raw.trim()
|
|
75
|
+
const open = value.indexOf('(')
|
|
76
|
+
|
|
77
|
+
if (open < 0) {
|
|
78
|
+
return { type: parseType(value) }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!value.endsWith(')')) {
|
|
82
|
+
throw declarationError(`非法的参数声明 "${token}"`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const type = parseType(value.slice(0, open).trim())
|
|
86
|
+
const length = parseLength(value.slice(open + 1, -1), token)
|
|
87
|
+
|
|
88
|
+
if (type !== 'string' && type !== 'text') {
|
|
89
|
+
throw declarationError(`只有 string/text 参数支持长度限制:${token}`)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { type, length }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function validateDefaultLength(
|
|
96
|
+
name: string,
|
|
97
|
+
value: string,
|
|
98
|
+
length: ParamLength | undefined
|
|
99
|
+
): void {
|
|
100
|
+
if (length === undefined) return
|
|
101
|
+
|
|
102
|
+
const measured = measureLength(value)
|
|
103
|
+
|
|
104
|
+
if (length.min !== undefined && measured < length.min) {
|
|
105
|
+
throw declarationError(
|
|
106
|
+
`参数 ${name} 的默认值至少需要 ${length.min} 个字符`
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (length.max !== undefined && measured > length.max) {
|
|
111
|
+
throw declarationError(
|
|
112
|
+
`参数 ${name} 的默认值不能超过 ${length.max} 个字符`
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
26
117
|
export function validateName(value: string): string {
|
|
27
118
|
const name = value.trim()
|
|
28
119
|
|
|
@@ -120,7 +211,13 @@ export function parseToken(token: string): ParamSpec {
|
|
|
120
211
|
throw declarationError('变长参数必须使用 [ ]')
|
|
121
212
|
}
|
|
122
213
|
|
|
123
|
-
const
|
|
214
|
+
const rest = inner.slice(3)
|
|
215
|
+
|
|
216
|
+
if (rest.includes('(')) {
|
|
217
|
+
throw declarationError(`变长参数不支持长度限制:${token}`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const name = validateName(rest)
|
|
124
221
|
|
|
125
222
|
return {
|
|
126
223
|
name,
|
|
@@ -140,20 +237,30 @@ export function parseToken(token: string): ParamSpec {
|
|
|
140
237
|
const defaultRaw = equals >= 0 ? inner.slice(equals + 1).trim() : undefined
|
|
141
238
|
const colon = head.indexOf(':')
|
|
142
239
|
const name = validateName(colon >= 0 ? head.slice(0, colon) : head)
|
|
143
|
-
const type
|
|
240
|
+
const { type, length } = parseTypeDeclaration(
|
|
241
|
+
colon >= 0 ? head.slice(colon + 1) : 'string',
|
|
242
|
+
token
|
|
243
|
+
)
|
|
144
244
|
|
|
145
245
|
if (required && defaultRaw !== undefined) {
|
|
146
246
|
throw declarationError(`必填参数不能有默认值:${token}`)
|
|
147
247
|
}
|
|
148
248
|
|
|
149
249
|
if (defaultRaw !== undefined) {
|
|
250
|
+
const defaultValue = parseDefaultValue(type, defaultRaw, name)
|
|
251
|
+
|
|
252
|
+
if (typeof defaultValue === 'string') {
|
|
253
|
+
validateDefaultLength(name, defaultValue, length)
|
|
254
|
+
}
|
|
255
|
+
|
|
150
256
|
return {
|
|
151
257
|
name,
|
|
152
258
|
type,
|
|
153
259
|
required,
|
|
154
260
|
rest: false,
|
|
155
261
|
hasDefault: true,
|
|
156
|
-
defaultValue
|
|
262
|
+
defaultValue,
|
|
263
|
+
length,
|
|
157
264
|
}
|
|
158
265
|
}
|
|
159
266
|
|
|
@@ -163,6 +270,7 @@ export function parseToken(token: string): ParamSpec {
|
|
|
163
270
|
required,
|
|
164
271
|
rest: false,
|
|
165
272
|
hasDefault: false,
|
|
273
|
+
length,
|
|
166
274
|
}
|
|
167
275
|
}
|
|
168
276
|
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type ParamSpec,
|
|
5
5
|
type ParsedCommandParams,
|
|
6
6
|
} from './types.js'
|
|
7
|
-
import { parseAt, parseBoolean, parseTime } from './values.js'
|
|
7
|
+
import { measureLength, parseAt, parseBoolean, parseTime } from './values.js'
|
|
8
8
|
|
|
9
9
|
export function tokenizeInput(input: string, usage: string): InputToken[] {
|
|
10
10
|
const tokens: InputToken[] = []
|
|
@@ -101,6 +101,32 @@ export function tokenizeInput(input: string, usage: string): InputToken[] {
|
|
|
101
101
|
return tokens
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
function enforceLength(spec: ParamSpec, value: string, usage: string): string {
|
|
105
|
+
const length = spec.length
|
|
106
|
+
|
|
107
|
+
if (length === undefined) {
|
|
108
|
+
return value
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const measured = measureLength(value)
|
|
112
|
+
|
|
113
|
+
if (length.min !== undefined && measured < length.min) {
|
|
114
|
+
throw new CommandParamsError(
|
|
115
|
+
`参数 ${spec.name} 至少需要 ${length.min} 个字符`,
|
|
116
|
+
usage
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (length.max !== undefined && measured > length.max) {
|
|
121
|
+
throw new CommandParamsError(
|
|
122
|
+
`参数 ${spec.name} 不能超过 ${length.max} 个字符`,
|
|
123
|
+
usage
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return value
|
|
128
|
+
}
|
|
129
|
+
|
|
104
130
|
export function parseValue(
|
|
105
131
|
spec: ParamSpec,
|
|
106
132
|
raw: string,
|
|
@@ -108,6 +134,7 @@ export function parseValue(
|
|
|
108
134
|
): unknown {
|
|
109
135
|
switch (spec.type) {
|
|
110
136
|
case 'string':
|
|
137
|
+
return enforceLength(spec, raw, usage)
|
|
111
138
|
case 'text':
|
|
112
139
|
return raw
|
|
113
140
|
case 'number': {
|
|
@@ -218,7 +245,7 @@ export function parseInput(
|
|
|
218
245
|
const value =
|
|
219
246
|
remaining === 1 ? token.value : input.slice(token.start).trim()
|
|
220
247
|
|
|
221
|
-
args.push(value)
|
|
248
|
+
args.push(enforceLength(spec, value, usage))
|
|
222
249
|
index = tokens.length
|
|
223
250
|
continue
|
|
224
251
|
}
|
|
@@ -8,12 +8,16 @@ type TrimRight<Source extends string> =
|
|
|
8
8
|
|
|
9
9
|
type Trim<Source extends string> = TrimLeft<TrimRight<Source>>
|
|
10
10
|
|
|
11
|
+
type StripLength<Type extends string> =
|
|
12
|
+
Trim<Type> extends `${infer Base}(${string})` ? Trim<Base> : Trim<Type>
|
|
13
|
+
|
|
11
14
|
type ParamValue<Type extends string> =
|
|
12
|
-
|
|
15
|
+
StripLength<Type> extends 'string' | 'text'
|
|
13
16
|
? string
|
|
14
|
-
:
|
|
17
|
+
: StripLength<Type> extends
|
|
18
|
+
'number' | 'integer' | 'at' | 'reply' | 'time'
|
|
15
19
|
? number
|
|
16
|
-
:
|
|
20
|
+
: StripLength<Type> extends 'boolean'
|
|
17
21
|
? boolean
|
|
18
22
|
: unknown
|
|
19
23
|
|
|
@@ -30,6 +30,11 @@ export class CommandParamsError extends Error {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export interface ParamLength {
|
|
34
|
+
min?: number | undefined
|
|
35
|
+
max?: number | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
export interface ParamSpec {
|
|
34
39
|
name: string
|
|
35
40
|
type: CommandParamType
|
|
@@ -37,6 +42,7 @@ export interface ParamSpec {
|
|
|
37
42
|
rest: boolean
|
|
38
43
|
hasDefault: boolean
|
|
39
44
|
defaultValue?: unknown
|
|
45
|
+
length?: ParamLength | undefined
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
export interface InputToken {
|
|
@@ -37,6 +37,10 @@ export function unquote(value: string): string {
|
|
|
37
37
|
return result
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
export function measureLength(value: string): number {
|
|
41
|
+
return Array.from(value).length
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
export function parseBoolean(value: string): boolean | null {
|
|
41
45
|
const normalized = value.trim().toLowerCase()
|
|
42
46
|
|
package/src/core/plugin/types.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
OneBotGroupMessageEvent,
|
|
2
3
|
OneBotMessageEvent,
|
|
3
4
|
OneBotNoticeEvent,
|
|
5
|
+
OneBotPrivateMessageEvent,
|
|
4
6
|
OneBotRequestEvent,
|
|
5
7
|
OutgoingMessage,
|
|
6
8
|
SnowLumaEvent,
|
|
@@ -17,6 +19,10 @@ import type { CommandParamArgs } from './command-params.js'
|
|
|
17
19
|
|
|
18
20
|
export type EventType = 'message' | 'notice' | 'request' | 'meta'
|
|
19
21
|
|
|
22
|
+
export type MessageType = 'group' | 'private'
|
|
23
|
+
|
|
24
|
+
export type MessageTypeFilter = MessageType | readonly MessageType[]
|
|
25
|
+
|
|
20
26
|
export interface CommandMatch {
|
|
21
27
|
command: string
|
|
22
28
|
prefix: string
|
|
@@ -28,6 +34,7 @@ export interface CommandInfo {
|
|
|
28
34
|
name: string
|
|
29
35
|
aliases: string[]
|
|
30
36
|
permission: Permission
|
|
37
|
+
messageTypes: MessageType[]
|
|
31
38
|
description?: string | undefined
|
|
32
39
|
usage?: string | undefined
|
|
33
40
|
pluginId: string
|
|
@@ -59,6 +66,16 @@ export type MessageHandler = (
|
|
|
59
66
|
context: EventContext<OneBotMessageEvent>
|
|
60
67
|
) => void | Promise<void>
|
|
61
68
|
|
|
69
|
+
export type GroupMessageHandler = (
|
|
70
|
+
event: OneBotGroupMessageEvent,
|
|
71
|
+
context: EventContext<OneBotGroupMessageEvent>
|
|
72
|
+
) => void | Promise<void>
|
|
73
|
+
|
|
74
|
+
export type PrivateMessageHandler = (
|
|
75
|
+
event: OneBotPrivateMessageEvent,
|
|
76
|
+
context: EventContext<OneBotPrivateMessageEvent>
|
|
77
|
+
) => void | Promise<void>
|
|
78
|
+
|
|
62
79
|
export type NoticeHandler = (
|
|
63
80
|
event: OneBotNoticeEvent,
|
|
64
81
|
context: EventContext<OneBotNoticeEvent>
|
|
@@ -80,6 +97,7 @@ export interface CommandOptions<Declaration extends string = string> {
|
|
|
80
97
|
aliases?: string[] | undefined
|
|
81
98
|
prefixes?: string[] | undefined
|
|
82
99
|
permission?: Permission | undefined
|
|
100
|
+
messageType?: MessageTypeFilter | undefined
|
|
83
101
|
description?: string | undefined
|
|
84
102
|
params?: Declaration | undefined
|
|
85
103
|
}
|
|
@@ -127,6 +145,8 @@ export interface CommandRegistrar extends CommandGroupRegistrar {
|
|
|
127
145
|
|
|
128
146
|
export interface EventRegistrar {
|
|
129
147
|
onMessage(handler: MessageHandler): () => void
|
|
148
|
+
onGroupMessage(handler: GroupMessageHandler): () => void
|
|
149
|
+
onPrivateMessage(handler: PrivateMessageHandler): () => void
|
|
130
150
|
onNotice(options: NoticeOptions, handler: NoticeHandler): () => void
|
|
131
151
|
onRequest(options: RequestOptions, handler: RequestHandler): () => void
|
|
132
152
|
}
|