@jsbot-tellus/core 2.5.0 → 2.6.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 +8 -5
- package/dist/app/cli.d.ts +10 -0
- package/dist/app/cli.d.ts.map +1 -0
- package/dist/app/cli.js +37 -0
- package/dist/app/cli.js.map +1 -0
- package/dist/app/runtime.d.ts +121 -0
- package/dist/app/runtime.d.ts.map +1 -0
- package/dist/app/runtime.js +296 -0
- package/dist/app/runtime.js.map +1 -0
- package/dist/app.js +63 -273
- package/dist/app.js.map +1 -1
- package/dist/core/plugin/command-params.d.ts +3 -37
- package/dist/core/plugin/command-params.d.ts.map +1 -1
- package/dist/core/plugin/command-params.js +2 -580
- package/dist/core/plugin/command-params.js.map +1 -1
- package/dist/core/plugin/install/paths.d.ts +7 -0
- package/dist/core/plugin/install/paths.d.ts.map +1 -0
- package/dist/core/plugin/install/paths.js +49 -0
- package/dist/core/plugin/install/paths.js.map +1 -0
- package/dist/core/plugin/install/state.d.ts +23 -0
- package/dist/core/plugin/install/state.d.ts.map +1 -0
- package/dist/core/plugin/install/state.js +129 -0
- package/dist/core/plugin/install/state.js.map +1 -0
- package/dist/core/plugin/install/validate-script.d.ts +3 -0
- package/dist/core/plugin/install/validate-script.d.ts.map +1 -0
- package/dist/core/plugin/install/validate-script.js +154 -0
- package/dist/core/plugin/install/validate-script.js.map +1 -0
- package/dist/core/plugin/install-dir.d.ts +3 -28
- package/dist/core/plugin/install-dir.d.ts.map +1 -1
- package/dist/core/plugin/install-dir.js +3 -326
- package/dist/core/plugin/install-dir.js.map +1 -1
- package/dist/core/plugin/installer/checks.d.ts +6 -0
- package/dist/core/plugin/installer/checks.d.ts.map +1 -0
- package/dist/core/plugin/installer/checks.js +99 -0
- package/dist/core/plugin/installer/checks.js.map +1 -0
- package/dist/core/plugin/installer/support.d.ts +37 -0
- package/dist/core/plugin/installer/support.d.ts.map +1 -0
- package/dist/core/plugin/installer/support.js +67 -0
- package/dist/core/plugin/installer/support.js.map +1 -0
- package/dist/core/plugin/installer/warnings.d.ts +5 -0
- package/dist/core/plugin/installer/warnings.d.ts.map +1 -0
- package/dist/core/plugin/installer/warnings.js +28 -0
- package/dist/core/plugin/installer/warnings.js.map +1 -0
- package/dist/core/plugin/installer.d.ts +4 -30
- package/dist/core/plugin/installer.d.ts.map +1 -1
- package/dist/core/plugin/installer.js +21 -202
- package/dist/core/plugin/installer.js.map +1 -1
- package/dist/core/plugin/params/compile.d.ts +3 -0
- package/dist/core/plugin/params/compile.d.ts.map +1 -0
- package/dist/core/plugin/params/compile.js +23 -0
- package/dist/core/plugin/params/compile.js.map +1 -0
- package/dist/core/plugin/params/declaration.d.ts +9 -0
- package/dist/core/plugin/params/declaration.d.ts.map +1 -0
- package/dist/core/plugin/params/declaration.js +286 -0
- package/dist/core/plugin/params/declaration.js.map +1 -0
- package/dist/core/plugin/params/input.d.ts +5 -0
- package/dist/core/plugin/params/input.d.ts.map +1 -0
- package/dist/core/plugin/params/input.js +196 -0
- package/dist/core/plugin/params/input.js.map +1 -0
- package/dist/core/plugin/params/reply.d.ts +3 -0
- package/dist/core/plugin/params/reply.d.ts.map +1 -0
- package/dist/core/plugin/params/reply.js +49 -0
- package/dist/core/plugin/params/reply.js.map +1 -0
- package/dist/core/plugin/params/type-level.d.ts +24 -0
- package/dist/core/plugin/params/type-level.d.ts.map +1 -0
- package/dist/core/plugin/params/type-level.js +2 -0
- package/dist/core/plugin/params/type-level.js.map +1 -0
- package/dist/core/plugin/params/types.d.ts +33 -0
- package/dist/core/plugin/params/types.d.ts.map +1 -0
- package/dist/core/plugin/params/types.js +12 -0
- package/dist/core/plugin/params/types.js.map +1 -0
- package/dist/core/plugin/params/values.d.ts +7 -0
- package/dist/core/plugin/params/values.d.ts.map +1 -0
- package/dist/core/plugin/params/values.js +111 -0
- package/dist/core/plugin/params/values.js.map +1 -0
- package/dist/core/web/api.d.ts +2 -14
- package/dist/core/web/api.d.ts.map +1 -1
- package/dist/core/web/api.js +8 -500
- package/dist/core/web/api.js.map +1 -1
- package/dist/core/web/routes/actions.d.ts +4 -0
- package/dist/core/web/routes/actions.d.ts.map +1 -0
- package/dist/core/web/routes/actions.js +33 -0
- package/dist/core/web/routes/actions.js.map +1 -0
- package/dist/core/web/routes/config.d.ts +5 -0
- package/dist/core/web/routes/config.d.ts.map +1 -0
- package/dist/core/web/routes/config.js +30 -0
- package/dist/core/web/routes/config.js.map +1 -0
- package/dist/core/web/routes/kv.d.ts +6 -0
- package/dist/core/web/routes/kv.d.ts.map +1 -0
- package/dist/core/web/routes/kv.js +80 -0
- package/dist/core/web/routes/kv.js.map +1 -0
- package/dist/core/web/routes/plugins.d.ts +5 -0
- package/dist/core/web/routes/plugins.d.ts.map +1 -0
- package/dist/core/web/routes/plugins.js +98 -0
- package/dist/core/web/routes/plugins.js.map +1 -0
- package/dist/core/web/routes/sessions.d.ts +8 -0
- package/dist/core/web/routes/sessions.d.ts.map +1 -0
- package/dist/core/web/routes/sessions.js +130 -0
- package/dist/core/web/routes/sessions.js.map +1 -0
- package/dist/core/web/routes/shared.d.ts +18 -0
- package/dist/core/web/routes/shared.d.ts.map +1 -0
- package/dist/core/web/routes/shared.js +19 -0
- package/dist/core/web/routes/shared.js.map +1 -0
- package/dist/core/web/routes/status.d.ts +6 -0
- package/dist/core/web/routes/status.d.ts.map +1 -0
- package/dist/core/web/routes/status.js +132 -0
- package/dist/core/web/routes/status.js.map +1 -0
- package/dist/core/web/server/auth.d.ts +17 -0
- package/dist/core/web/server/auth.d.ts.map +1 -0
- package/dist/core/web/server/auth.js +68 -0
- package/dist/core/web/server/auth.js.map +1 -0
- package/dist/core/web/server/csp.d.ts +11 -0
- package/dist/core/web/server/csp.d.ts.map +1 -0
- package/dist/core/web/server/csp.js +31 -0
- package/dist/core/web/server/csp.js.map +1 -0
- package/dist/core/web/server/request.d.ts +6 -0
- package/dist/core/web/server/request.d.ts.map +1 -0
- package/dist/core/web/server/request.js +33 -0
- package/dist/core/web/server/request.js.map +1 -0
- package/dist/core/web/server/security.d.ts +4 -0
- package/dist/core/web/server/security.d.ts.map +1 -0
- package/dist/core/web/server/security.js +12 -0
- package/dist/core/web/server/security.js.map +1 -0
- package/dist/core/web/server/sse.d.ts +18 -0
- package/dist/core/web/server/sse.d.ts.map +1 -0
- package/dist/core/web/server/sse.js +49 -0
- package/dist/core/web/server/sse.js.map +1 -0
- package/dist/core/web/server/static.d.ts +16 -0
- package/dist/core/web/server/static.d.ts.map +1 -0
- package/dist/core/web/server/static.js +80 -0
- package/dist/core/web/server/static.js.map +1 -0
- package/dist/core/web/server.d.ts +0 -7
- package/dist/core/web/server.d.ts.map +1 -1
- package/dist/core/web/server.js +41 -263
- package/dist/core/web/server.js.map +1 -1
- package/docs/architecture.md +28 -11
- package/docs/configuration.md +1 -1
- package/docs/migration.md +2 -2
- package/docs/plugin-api.md +24 -11
- package/docs/plugin-development.md +1 -1
- package/package.json +19 -17
- package/src/app/cli.ts +47 -0
- package/src/app/runtime.ts +522 -0
- package/src/app.ts +87 -395
- package/src/core/plugin/command-params.ts +8 -871
- package/src/core/plugin/install/paths.ts +72 -0
- package/src/core/plugin/install/state.ts +196 -0
- package/src/core/plugin/install/validate-script.ts +154 -0
- package/src/core/plugin/install-dir.ts +19 -427
- package/src/core/plugin/installer/checks.ts +137 -0
- package/src/core/plugin/installer/support.ts +120 -0
- package/src/core/plugin/installer/warnings.ts +52 -0
- package/src/core/plugin/installer.ts +52 -301
- package/src/core/plugin/params/compile.ts +41 -0
- package/src/core/plugin/params/declaration.ts +394 -0
- package/src/core/plugin/params/input.ts +281 -0
- package/src/core/plugin/params/reply.ts +59 -0
- package/src/core/plugin/params/type-level.ts +75 -0
- package/src/core/plugin/params/types.ts +55 -0
- package/src/core/plugin/params/values.ts +150 -0
- package/src/core/web/api.ts +33 -759
- package/src/core/web/routes/actions.ts +43 -0
- package/src/core/web/routes/config.ts +42 -0
- package/src/core/web/routes/kv.ts +122 -0
- package/src/core/web/routes/plugins.ts +151 -0
- package/src/core/web/routes/sessions.ts +188 -0
- package/src/core/web/routes/shared.ts +42 -0
- package/src/core/web/routes/status.ts +191 -0
- package/src/core/web/server/auth.ts +126 -0
- package/src/core/web/server/csp.ts +33 -0
- package/src/core/web/server/request.ts +38 -0
- package/src/core/web/server/security.ts +14 -0
- package/src/core/web/server/sse.ts +74 -0
- package/src/core/web/server/static.ts +112 -0
- package/src/core/web/server.ts +41 -383
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { parseSegments, type OneBotMessageEvent } from '@snowluma/sdk'
|
|
2
|
+
|
|
3
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
4
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function toMessageId(value: unknown): number | undefined {
|
|
8
|
+
const raw = typeof value === 'string' ? value.trim() : value
|
|
9
|
+
|
|
10
|
+
if (typeof raw !== 'number' && typeof raw !== 'string') {
|
|
11
|
+
return undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const parsed =
|
|
15
|
+
typeof raw === 'string'
|
|
16
|
+
? /^-?\d+$/.test(raw)
|
|
17
|
+
? Number(raw)
|
|
18
|
+
: Number.NaN
|
|
19
|
+
: raw
|
|
20
|
+
|
|
21
|
+
return Number.isSafeInteger(parsed) && parsed !== 0 ? parsed : undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function replyIdIn(segments: readonly unknown[]): number | undefined {
|
|
25
|
+
for (const segment of segments) {
|
|
26
|
+
if (!isRecord(segment) || segment.type !== 'reply') continue
|
|
27
|
+
|
|
28
|
+
const data = isRecord(segment.data) ? segment.data : undefined
|
|
29
|
+
const id = toMessageId(data?.id)
|
|
30
|
+
|
|
31
|
+
if (id !== undefined) return id
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function quoteIdOf(
|
|
38
|
+
event: OneBotMessageEvent | undefined
|
|
39
|
+
): number | undefined {
|
|
40
|
+
if (!event) return undefined
|
|
41
|
+
|
|
42
|
+
const message = event.message
|
|
43
|
+
|
|
44
|
+
if (typeof message === 'string') {
|
|
45
|
+
const id = replyIdIn(parseSegments(message))
|
|
46
|
+
|
|
47
|
+
if (id !== undefined) return id
|
|
48
|
+
} else if (Array.isArray(message)) {
|
|
49
|
+
const id = replyIdIn(message)
|
|
50
|
+
|
|
51
|
+
if (id !== undefined) return id
|
|
52
|
+
} else if (isRecord(message)) {
|
|
53
|
+
const id = replyIdIn([message])
|
|
54
|
+
|
|
55
|
+
if (id !== undefined) return id
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return replyIdIn(parseSegments(event.raw_message))
|
|
59
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
type Whitespace = ' ' | '\n' | '\r' | '\t' | '\v' | '\f'
|
|
2
|
+
|
|
3
|
+
type TrimLeft<Source extends string> =
|
|
4
|
+
Source extends `${Whitespace}${infer Rest}` ? TrimLeft<Rest> : Source
|
|
5
|
+
|
|
6
|
+
type TrimRight<Source extends string> =
|
|
7
|
+
Source extends `${infer Rest}${Whitespace}` ? TrimRight<Rest> : Source
|
|
8
|
+
|
|
9
|
+
type Trim<Source extends string> = TrimLeft<TrimRight<Source>>
|
|
10
|
+
|
|
11
|
+
type StripLength<Type extends string> =
|
|
12
|
+
Trim<Type> extends `${infer Base}(${string})` ? Trim<Base> : Trim<Type>
|
|
13
|
+
|
|
14
|
+
type ParamValue<Type extends string> =
|
|
15
|
+
StripLength<Type> extends 'string' | 'text'
|
|
16
|
+
? string
|
|
17
|
+
: StripLength<Type> extends
|
|
18
|
+
'number' | 'integer' | 'at' | 'reply' | 'time'
|
|
19
|
+
? number
|
|
20
|
+
: StripLength<Type> extends 'boolean'
|
|
21
|
+
? boolean
|
|
22
|
+
: unknown
|
|
23
|
+
|
|
24
|
+
interface VariadicParam {
|
|
25
|
+
readonly variadic: true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type ParamType<Head extends string> = Head extends `${string}:${infer Type}`
|
|
29
|
+
? ParamValue<Type>
|
|
30
|
+
: string
|
|
31
|
+
|
|
32
|
+
type ParamElement<Body extends string, Required extends boolean> =
|
|
33
|
+
Trim<Body> extends `...${string}`
|
|
34
|
+
? VariadicParam
|
|
35
|
+
: Trim<Body> extends `${infer Head}=${string}`
|
|
36
|
+
? ParamType<Head>
|
|
37
|
+
: Required extends true
|
|
38
|
+
? ParamType<Body>
|
|
39
|
+
: ParamType<Body> | undefined
|
|
40
|
+
|
|
41
|
+
type AppendParam<Acc extends unknown[], Element> = [Element] extends [
|
|
42
|
+
VariadicParam,
|
|
43
|
+
]
|
|
44
|
+
? [...Acc, ...string[]]
|
|
45
|
+
: [...Acc, Element]
|
|
46
|
+
|
|
47
|
+
type ParseCommandParams<Source extends string, Acc extends unknown[] = []> =
|
|
48
|
+
Trim<Source> extends ''
|
|
49
|
+
? Acc
|
|
50
|
+
: Trim<Source> extends `<${infer Body}>${infer Rest}`
|
|
51
|
+
? ParamElement<Body, true> extends VariadicParam
|
|
52
|
+
? any[]
|
|
53
|
+
: ParseCommandParams<
|
|
54
|
+
Rest,
|
|
55
|
+
AppendParam<Acc, ParamElement<Body, true>>
|
|
56
|
+
>
|
|
57
|
+
: Trim<Source> extends `[${infer Body}]${infer Rest}`
|
|
58
|
+
? ParamElement<Body, false> extends VariadicParam
|
|
59
|
+
? Trim<Rest> extends ''
|
|
60
|
+
? [...Acc, ...string[]]
|
|
61
|
+
: any[]
|
|
62
|
+
: ParseCommandParams<
|
|
63
|
+
Rest,
|
|
64
|
+
AppendParam<Acc, ParamElement<Body, false>>
|
|
65
|
+
>
|
|
66
|
+
: any[]
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 把 `params` 声明字符串推导成 handler 的位置参数 tuple。
|
|
70
|
+
*
|
|
71
|
+
* 非字面量声明(例如运行时拼接的 `string`)或无法解析的声明会回退为
|
|
72
|
+
* `any[]`,以保证现有插件仍然可以编译。
|
|
73
|
+
*/
|
|
74
|
+
export type CommandParamArgs<Declaration extends string> =
|
|
75
|
+
string extends Declaration ? any[] : ParseCommandParams<Declaration>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OneBotMessageEvent } from '@snowluma/sdk'
|
|
2
|
+
|
|
3
|
+
export type CommandParamType =
|
|
4
|
+
| 'string'
|
|
5
|
+
| 'text'
|
|
6
|
+
| 'number'
|
|
7
|
+
| 'integer'
|
|
8
|
+
| 'boolean'
|
|
9
|
+
| 'at'
|
|
10
|
+
| 'reply'
|
|
11
|
+
| 'time'
|
|
12
|
+
|
|
13
|
+
export interface ParsedCommandParams {
|
|
14
|
+
args: unknown[]
|
|
15
|
+
ignored: string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CompiledCommandParams {
|
|
19
|
+
usage: string
|
|
20
|
+
parse(input: string, event?: OneBotMessageEvent): ParsedCommandParams
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class CommandParamsError extends Error {
|
|
24
|
+
readonly usage: string
|
|
25
|
+
|
|
26
|
+
constructor(message: string, usage: string) {
|
|
27
|
+
super(message)
|
|
28
|
+
this.name = 'CommandParamsError'
|
|
29
|
+
this.usage = usage
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ParamLength {
|
|
34
|
+
min?: number | undefined
|
|
35
|
+
max?: number | undefined
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ParamSpec {
|
|
39
|
+
name: string
|
|
40
|
+
type: CommandParamType
|
|
41
|
+
required: boolean
|
|
42
|
+
rest: boolean
|
|
43
|
+
hasDefault: boolean
|
|
44
|
+
defaultValue?: unknown
|
|
45
|
+
length?: ParamLength | undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface InputToken {
|
|
49
|
+
value: string
|
|
50
|
+
start: number
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function declarationError(message: string): Error {
|
|
54
|
+
return new Error(`参数声明错误:${message}`)
|
|
55
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { parseSegments } from '@snowluma/sdk'
|
|
2
|
+
import { CommandParamsError, declarationError } from './types.js'
|
|
3
|
+
|
|
4
|
+
export function unquote(value: string): string {
|
|
5
|
+
const trimmed = value.trim()
|
|
6
|
+
const quote = trimmed[0]
|
|
7
|
+
|
|
8
|
+
if (quote !== '"' && quote !== "'") {
|
|
9
|
+
return trimmed
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (trimmed.length < 2 || trimmed[trimmed.length - 1] !== quote) {
|
|
13
|
+
throw declarationError('默认值引号未闭合')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let result = ''
|
|
17
|
+
|
|
18
|
+
for (let index = 1; index < trimmed.length - 1; index += 1) {
|
|
19
|
+
const char = trimmed[index]
|
|
20
|
+
|
|
21
|
+
if (char === undefined) break
|
|
22
|
+
|
|
23
|
+
if (char === '\\') {
|
|
24
|
+
const next = trimmed[index + 1]
|
|
25
|
+
|
|
26
|
+
if (next !== undefined) {
|
|
27
|
+
result += next
|
|
28
|
+
index += 1
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
result += char
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function measureLength(value: string): number {
|
|
41
|
+
return Array.from(value).length
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseBoolean(value: string): boolean | null {
|
|
45
|
+
const normalized = value.trim().toLowerCase()
|
|
46
|
+
|
|
47
|
+
if (['true', '1', 'yes', 'y', 'on'].includes(normalized)) {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (['false', '0', 'no', 'n', 'off'].includes(normalized)) {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function parseAt(value: string, name: string, usage: string): number {
|
|
59
|
+
const raw = value.trim()
|
|
60
|
+
const segments = parseSegments(raw)
|
|
61
|
+
const segment = segments[0]
|
|
62
|
+
|
|
63
|
+
if (segments.length === 1 && segment?.type === 'at') {
|
|
64
|
+
const qq = segment.data.qq
|
|
65
|
+
|
|
66
|
+
if (qq !== 'all') {
|
|
67
|
+
const parsed = Number(qq)
|
|
68
|
+
|
|
69
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) {
|
|
70
|
+
return parsed
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} else if (/^\d+$/.test(raw)) {
|
|
74
|
+
const parsed = Number(raw)
|
|
75
|
+
|
|
76
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) {
|
|
77
|
+
return parsed
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new CommandParamsError(
|
|
82
|
+
`参数 ${name} 需要 @ 一个用户或填写 QQ 号`,
|
|
83
|
+
usage
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const TIME_UNITS: Record<string, number> = {
|
|
88
|
+
s: 1,
|
|
89
|
+
秒: 1,
|
|
90
|
+
m: 60,
|
|
91
|
+
分: 60,
|
|
92
|
+
分钟: 60,
|
|
93
|
+
h: 3600,
|
|
94
|
+
时: 3600,
|
|
95
|
+
小时: 3600,
|
|
96
|
+
d: 86400,
|
|
97
|
+
天: 86400,
|
|
98
|
+
w: 604800,
|
|
99
|
+
周: 604800,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const TIME_PART = /^(\d+(?:\.\d+)?)(分钟|小时|秒|分|时|天|周|[smhdw])/i
|
|
103
|
+
|
|
104
|
+
export function parseTimeValue(value: string): number | null {
|
|
105
|
+
const raw = value.trim()
|
|
106
|
+
|
|
107
|
+
if (/^\d+$/.test(raw)) {
|
|
108
|
+
const seconds = Number(raw)
|
|
109
|
+
|
|
110
|
+
return Number.isSafeInteger(seconds) ? seconds : null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let index = 0
|
|
114
|
+
let total = 0
|
|
115
|
+
let parsed = false
|
|
116
|
+
|
|
117
|
+
while (index < raw.length) {
|
|
118
|
+
const match = TIME_PART.exec(raw.slice(index))
|
|
119
|
+
|
|
120
|
+
if (!match) return null
|
|
121
|
+
|
|
122
|
+
const unit = TIME_UNITS[match[2]?.toLowerCase() ?? '']
|
|
123
|
+
const amount = Number(match[1])
|
|
124
|
+
|
|
125
|
+
if (unit === undefined) return null
|
|
126
|
+
|
|
127
|
+
total += amount * unit
|
|
128
|
+
index += match[0].length
|
|
129
|
+
parsed = true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!parsed || !Number.isSafeInteger(total) || total < 0) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return total
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function parseTime(value: string, name: string, usage: string): number {
|
|
140
|
+
const seconds = parseTimeValue(value)
|
|
141
|
+
|
|
142
|
+
if (seconds === null) {
|
|
143
|
+
throw new CommandParamsError(
|
|
144
|
+
`参数 ${name} 需要是时间,例如 10s、5m、1h30m`,
|
|
145
|
+
usage
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return seconds
|
|
150
|
+
}
|