@zhin.js/core 1.4.3 → 1.5.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 +3 -3
- package/lib/built/generic-segment-mapper.d.ts +2 -4
- package/lib/built/generic-segment-mapper.js +64 -106
- package/lib/built/segment-contract/assert.d.ts +4 -1
- package/lib/built/segment-contract/assert.js +15 -6
- package/lib/built/segment-contract/index.d.ts +3 -3
- package/lib/built/segment-contract/index.js +2 -2
- package/lib/built/segment-contract/media.d.ts +1 -7
- package/lib/built/segment-contract/media.js +3 -50
- package/lib/built/segment-contract/preview.js +2 -2
- package/lib/built/segment-contract/types.d.ts +28 -3
- package/lib/built/segment-contract/validate.d.ts +91 -0
- package/lib/built/segment-contract/validate.js +49 -0
- package/lib/plugin-runtime/im/contracts.d.ts +34 -4
- package/lib/plugin-runtime/im/contracts.js +24 -2
- package/lib/plugin-runtime/im/im-runtime.d.ts +28 -9
- package/lib/plugin-runtime/im/im-runtime.js +175 -70
- package/lib/plugin-runtime/im/outbound-renderer.js +4 -1
- package/lib/plugin-runtime/im/outbound-segments.d.ts +13 -14
- package/lib/plugin-runtime/im/outbound-segments.js +37 -65
- package/package.json +8 -7
|
@@ -5,48 +5,16 @@ import { DEFAULT_INTERACTIVE_POLICY, isKeyboardSegment, } from '../../built/inte
|
|
|
5
5
|
import { isMediaRef } from '../../built/segment-contract/index.js';
|
|
6
6
|
const DEFAULT_CARD_WIDTH = 540;
|
|
7
7
|
const DEFAULT_CARD_FILENAME = 'card.png';
|
|
8
|
-
const DEFAULT_MEDIA_POLICY = '
|
|
9
|
-
/** 平台名 → 出站媒体策略(任务 C 声明式 policy 落地前的内置来源)。 */
|
|
10
|
-
const OUTBOUND_MEDIA_POLICY_BY_ADAPTER = { qq: 'base64',
|
|
11
|
-
icqq: 'base64',
|
|
12
|
-
slack: 'base64',
|
|
13
|
-
'weixin-ilink': 'base64',
|
|
14
|
-
telegram: 'url-or-text',
|
|
15
|
-
line: 'url-or-text',
|
|
16
|
-
lark: 'url-or-text',
|
|
17
|
-
kook: 'url-or-text',
|
|
18
|
-
dingtalk: 'url-or-text',
|
|
19
|
-
'wechat-mp': 'url-or-text',
|
|
20
|
-
wecom: 'url-or-text',
|
|
21
|
-
email: 'url-or-text',
|
|
22
|
-
github: 'url-or-text',
|
|
23
|
-
milky: 'url-or-text',
|
|
24
|
-
napcat: 'passthrough',
|
|
25
|
-
onebot11: 'passthrough',
|
|
26
|
-
onebot12: 'passthrough',
|
|
27
|
-
};
|
|
28
|
-
export function isOutboundSegment(value) {
|
|
29
|
-
return typeof value === 'object'
|
|
30
|
-
&& value !== null
|
|
31
|
-
&& !Array.isArray(value)
|
|
32
|
-
&& typeof value.type === 'string';
|
|
33
|
-
}
|
|
8
|
+
const DEFAULT_MEDIA_POLICY = 'url-or-text';
|
|
34
9
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* 未知平台回退 `base64`(历史行为)。
|
|
10
|
+
* 解析端点的出站媒体策略:读 adapter definition 声明的 `segments.outboundMedia`
|
|
11
|
+
* (多 endpoint 展开的 `slot~entry` id 回退到 slot id 查声明);
|
|
12
|
+
* 未声明回退 `url-or-text`(仅 URL 媒体可达,其余文本降级)。
|
|
39
13
|
*/
|
|
40
14
|
export function resolveOutboundMediaPolicy(adapter, snapshot) {
|
|
41
15
|
const slot = snapshot.capabilities.get(adapter)
|
|
42
16
|
?? snapshot.capabilities.get(baseSlotCapabilityId(adapter));
|
|
43
|
-
|
|
44
|
-
if (declared)
|
|
45
|
-
return declared;
|
|
46
|
-
const packageName = slot ? snapshot.tree.get(slot.owner)?.packageName : undefined;
|
|
47
|
-
const adapterType = adapterTypeName(packageName);
|
|
48
|
-
return (adapterType ? OUTBOUND_MEDIA_POLICY_BY_ADAPTER[adapterType] : undefined)
|
|
49
|
-
?? DEFAULT_MEDIA_POLICY;
|
|
17
|
+
return readDeclaredMediaPolicy(slot?.definition) ?? DEFAULT_MEDIA_POLICY;
|
|
50
18
|
}
|
|
51
19
|
function readDeclaredMediaPolicy(definition) {
|
|
52
20
|
if (!definition || typeof definition !== 'object')
|
|
@@ -55,23 +23,22 @@ function readDeclaredMediaPolicy(definition) {
|
|
|
55
23
|
if (!segments || typeof segments !== 'object')
|
|
56
24
|
return undefined;
|
|
57
25
|
const declared = segments.outboundMedia;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (declared.includes('base64'))
|
|
67
|
-
return 'base64';
|
|
68
|
-
if (declared.includes('upload') || declared.includes('path'))
|
|
69
|
-
return 'passthrough';
|
|
70
|
-
if (declared.includes('url'))
|
|
71
|
-
return 'url-or-text';
|
|
72
|
-
}
|
|
26
|
+
if (!Array.isArray(declared))
|
|
27
|
+
return undefined;
|
|
28
|
+
if (declared.includes('base64'))
|
|
29
|
+
return 'base64';
|
|
30
|
+
if (declared.includes('upload') || declared.includes('path'))
|
|
31
|
+
return 'passthrough';
|
|
32
|
+
if (declared.includes('url'))
|
|
33
|
+
return 'url-or-text';
|
|
73
34
|
return undefined;
|
|
74
35
|
}
|
|
36
|
+
export function isOutboundSegment(value) {
|
|
37
|
+
return typeof value === 'object'
|
|
38
|
+
&& value !== null
|
|
39
|
+
&& !Array.isArray(value)
|
|
40
|
+
&& typeof value.type === 'string';
|
|
41
|
+
}
|
|
75
42
|
/**
|
|
76
43
|
* 平台名 → 出站 interactive 策略(adapter 未声明 `segments.interactive` 时
|
|
77
44
|
* 的内置来源):telegram / discord 端点自行把 keyboard 编码为原生按钮;
|
|
@@ -181,11 +148,12 @@ async function renderHtmlSegment(segment, renderer, mediaPolicy) {
|
|
|
181
148
|
return {
|
|
182
149
|
type: 'image',
|
|
183
150
|
data: {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
151
|
+
media: {
|
|
152
|
+
kind: 'base64',
|
|
153
|
+
value: base64,
|
|
154
|
+
mime_type: 'image/png',
|
|
155
|
+
file_name: typeof data.fileName === 'string' ? data.fileName : DEFAULT_CARD_FILENAME,
|
|
156
|
+
},
|
|
189
157
|
},
|
|
190
158
|
};
|
|
191
159
|
}
|
|
@@ -197,22 +165,26 @@ async function renderHtmlSegment(segment, renderer, mediaPolicy) {
|
|
|
197
165
|
return { type: 'text', data: { text: htmlSegmentFallbackText(data, html) } };
|
|
198
166
|
}
|
|
199
167
|
/**
|
|
200
|
-
* 媒体协商:仅 `url-or-text`
|
|
201
|
-
* (base64 / 本地路径)无法投递,降级为文本(alt 优先)。
|
|
168
|
+
* 媒体协商:仅 `url-or-text` 需要在本层动作——媒体段的非 URL MediaRef
|
|
169
|
+
* (base64 / 本地路径)无法投递,降级为文本(alt / file_name 优先)。
|
|
202
170
|
*/
|
|
171
|
+
const MEDIA_SEGMENT_TYPES = new Set(['image', 'audio', 'video', 'file']);
|
|
203
172
|
function applyOutboundMediaPolicy(segments, mediaPolicy) {
|
|
204
173
|
if (mediaPolicy !== 'url-or-text')
|
|
205
174
|
return segments;
|
|
206
175
|
return segments.map((segment) => {
|
|
207
|
-
if (segment.type
|
|
176
|
+
if (!MEDIA_SEGMENT_TYPES.has(segment.type))
|
|
208
177
|
return segment;
|
|
209
|
-
const
|
|
178
|
+
const data = segment.data;
|
|
179
|
+
const media = data.media;
|
|
210
180
|
if (!isMediaRef(media) || media.kind === 'url')
|
|
211
181
|
return segment;
|
|
212
|
-
const
|
|
213
|
-
?
|
|
214
|
-
: '
|
|
215
|
-
|
|
182
|
+
const label = typeof data.alt === 'string' && data.alt
|
|
183
|
+
? data.alt
|
|
184
|
+
: typeof data.name === 'string' && data.name
|
|
185
|
+
? data.name
|
|
186
|
+
: media.file_name ?? `[${segment.type}]`;
|
|
187
|
+
return { type: 'text', data: { text: label } };
|
|
216
188
|
});
|
|
217
189
|
}
|
|
218
190
|
function htmlSegmentFallbackText(data, html) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Zhin机器人核心框架",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -68,12 +68,13 @@
|
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"qrcode": "^1.5.4",
|
|
70
70
|
"segment-matcher": "^1.0.5",
|
|
71
|
-
"smol-toml": "^1.7.
|
|
71
|
+
"smol-toml": "^1.7.1",
|
|
72
72
|
"yaml": "^2.9.0",
|
|
73
|
-
"@zhin.js/adapter": "1.1.
|
|
74
|
-
"@zhin.js/command": "1.0.
|
|
73
|
+
"@zhin.js/adapter": "1.1.3",
|
|
74
|
+
"@zhin.js/command": "1.0.5",
|
|
75
75
|
"@zhin.js/component": "1.0.4",
|
|
76
76
|
"@zhin.js/database": "1.0.78",
|
|
77
|
+
"@zhin.js/im-contract": "1.0.0",
|
|
77
78
|
"@zhin.js/kernel": "1.0.5",
|
|
78
79
|
"@zhin.js/logger": "1.0.75",
|
|
79
80
|
"@zhin.js/middleware": "1.0.4",
|
|
@@ -89,11 +90,11 @@
|
|
|
89
90
|
}
|
|
90
91
|
},
|
|
91
92
|
"devDependencies": {
|
|
92
|
-
"@types/node": "^26.1.
|
|
93
|
-
"@types/qrcode": "^1.5.
|
|
93
|
+
"@types/node": "^26.1.2",
|
|
94
|
+
"@types/qrcode": "^1.5.6",
|
|
94
95
|
"ajv": "8.18.0",
|
|
95
96
|
"typescript": "^6.0.3",
|
|
96
|
-
"@zhin.js/ai": "1.
|
|
97
|
+
"@zhin.js/ai": "1.5.0"
|
|
97
98
|
},
|
|
98
99
|
"repository": {
|
|
99
100
|
"type": "git",
|