weaver-work-cli 0.1.4 → 0.1.5
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/dist/internal/e10/request-runtime.js +3 -2
- package/dist/shortcuts/index.js +4 -0
- package/dist/shortcuts/meeting/continuation.js +70 -0
- package/dist/shortcuts/meeting/errors.js +63 -0
- package/dist/shortcuts/meeting/host.js +167 -0
- package/dist/shortcuts/meeting/index.js +123 -0
- package/dist/shortcuts/meeting/manifest.js +64 -0
- package/dist/shortcuts/meeting/operations/catalogs.js +58 -0
- package/dist/shortcuts/meeting/operations/change.js +242 -0
- package/dist/shortcuts/meeting/operations/conflicts.js +142 -0
- package/dist/shortcuts/meeting/operations/create.js +349 -0
- package/dist/shortcuts/meeting/operations/env.js +191 -0
- package/dist/shortcuts/meeting/operations/lifecycle.js +139 -0
- package/dist/shortcuts/meeting/operations/meetings.js +73 -0
- package/dist/shortcuts/meeting/operations/receipts.js +195 -0
- package/dist/shortcuts/meeting/operations/registry.js +47 -0
- package/dist/shortcuts/meeting/operations/rooms.js +132 -0
- package/dist/shortcuts/meeting/operations/shared.js +112 -0
- package/dist/shortcuts/meeting/operations/signs.js +41 -0
- package/dist/shortcuts/meeting/operations/types.js +25 -0
- package/dist/shortcuts/meeting/operations.js +19 -0
- package/dist/shortcuts/yimiaoban/continuation.js +70 -0
- package/dist/shortcuts/yimiaoban/errors.js +61 -0
- package/dist/shortcuts/yimiaoban/host.js +469 -0
- package/dist/shortcuts/yimiaoban/index.js +271 -0
- package/dist/shortcuts/yimiaoban/manifest.js +28 -0
- package/dist/shortcuts/yimiaoban/operations/file-user.js +288 -0
- package/dist/shortcuts/yimiaoban/operations/group-read.js +477 -0
- package/dist/shortcuts/yimiaoban/operations/group-write.js +591 -0
- package/dist/shortcuts/yimiaoban/operations/msg-parse.js +429 -0
- package/dist/shortcuts/yimiaoban/operations/msg-sync.js +519 -0
- package/dist/shortcuts/yimiaoban/operations/msg-write.js +593 -0
- package/dist/shortcuts/yimiaoban/operations/person.js +102 -0
- package/dist/shortcuts/yimiaoban/operations/registry.js +38 -0
- package/dist/shortcuts/yimiaoban/operations/session.js +437 -0
- package/dist/shortcuts/yimiaoban/operations/shared.js +236 -0
- package/dist/shortcuts/yimiaoban/operations/types.js +27 -0
- package/dist/shortcuts/yimiaoban/operations/write-util.js +78 -0
- package/dist/shortcuts/yimiaoban/operations.js +8 -0
- package/docs/_catalog.md +6 -0
- package/docs/meeting.md +48 -0
- package/docs/yimiaoban.md +86 -0
- package/package.json +1 -1
- package/skill-template/business-info.json +4 -1
- package/skill-template/domains/meeting.md +3 -0
- package/skill-template/domains/yimiaoban.md +21 -0
- package/skills/weaver-e10-calendar/SKILL.md +1 -0
- package/skills/weaver-e10-esb/SKILL.md +1 -0
- package/skills/weaver-e10-esb/references/source-manifest.json +3 -3
- package/skills/weaver-e10-hrm/SKILL.md +1 -0
- package/skills/weaver-e10-jiuchuanhui/SKILL.md +1 -0
- package/skills/weaver-e10-jucailin/SKILL.md +1 -0
- package/skills/weaver-e10-mail/SKILL.md +1 -0
- package/skills/weaver-e10-meeting/SKILL.md +106 -0
- package/skills/weaver-e10-meeting/product.json +8 -0
- package/skills/weaver-e10-meeting/references/meeting-change.md +67 -0
- package/skills/weaver-e10-meeting/references/meeting-create.md +52 -0
- package/skills/weaver-e10-meeting/references/meeting-lifecycle.md +57 -0
- package/skills/weaver-e10-meeting/references/meeting-queries.md +61 -0
- package/skills/weaver-e10-meeting/references/meeting-receipts-signs.md +48 -0
- package/skills/weaver-e10-meeting/references/meeting-update.md +7 -0
- package/skills/weaver-e10-meeting/references/source-manifest.json +50 -0
- package/skills/weaver-e10-plan/SKILL.md +1 -0
- package/skills/weaver-e10-plan/references/source-manifest.json +3 -3
- package/skills/weaver-e10-qiyecheng/SKILL.md +1 -0
- package/skills/weaver-e10-qiyecheng/references/source-manifest.json +3 -3
- package/skills/weaver-e10-shared/SKILL.md +4 -0
- package/skills/weaver-e10-skill-maker/SKILL.md +1 -0
- package/skills/weaver-e10-skill-maker/references/weaver-skill-style.md +1 -1
- package/skills/weaver-e10-wenshuding/SKILL.md +1 -0
- package/skills/weaver-e10-wenshuding/references/source-manifest.json +3 -3
- package/skills/weaver-e10-yepiaotong/references/source-manifest.json +5 -5
- package/skills/weaver-e10-yimiaoban/SKILL.md +104 -0
- package/skills/weaver-e10-yimiaoban/product.json +8 -0
- package/skills/weaver-e10-yimiaoban/references/ding-write.md +68 -0
- package/skills/weaver-e10-yimiaoban/references/error-codes.md +85 -0
- package/skills/weaver-e10-yimiaoban/references/field-resolution.md +46 -0
- package/skills/weaver-e10-yimiaoban/references/file-user-i18n.md +54 -0
- package/skills/weaver-e10-yimiaoban/references/group-read.md +55 -0
- package/skills/weaver-e10-yimiaoban/references/group-write.md +72 -0
- package/skills/weaver-e10-yimiaoban/references/msg-read.md +59 -0
- package/skills/weaver-e10-yimiaoban/references/msg-write.md +64 -0
- package/skills/weaver-e10-yimiaoban/references/person.md +44 -0
- package/skills/weaver-e10-yimiaoban/references/safety-boundaries.md +61 -0
- package/skills/weaver-e10-yimiaoban/references/session-sysmsg.md +47 -0
- package/skills/weaver-e10-yimiaoban/references/source-manifest.json +485 -0
- package/skills/weaver-e10-ziguanjia/SKILL.md +1 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息内容解析核心(msgParser 的 TS 移植,面向 CLI 结构化 data)。
|
|
3
|
+
*
|
|
4
|
+
* 只做解析与结构化(忠实还原展示所需字段),不做 markdown 渲染:
|
|
5
|
+
* - 文本 / 图片 / 文件 / 视频 / @提及 / i18n / 系统消息 / 必达识别
|
|
6
|
+
* - 媒体段提取(供预览/下载按钮 URL 构造)
|
|
7
|
+
* - 发送者姓名回退规则
|
|
8
|
+
*/
|
|
9
|
+
export const MSG_TYPES = {
|
|
10
|
+
1: '文本',
|
|
11
|
+
2: '图片',
|
|
12
|
+
3: '语音',
|
|
13
|
+
4: '文件',
|
|
14
|
+
5: '位置',
|
|
15
|
+
6: '图文',
|
|
16
|
+
7: '视频',
|
|
17
|
+
8: '事项',
|
|
18
|
+
9: '系统消息',
|
|
19
|
+
10: '撤回',
|
|
20
|
+
11: '抖一抖',
|
|
21
|
+
12: '屏蔽',
|
|
22
|
+
14: '通知',
|
|
23
|
+
15: '自定义',
|
|
24
|
+
16: '语音通话',
|
|
25
|
+
17: '聊天记录',
|
|
26
|
+
18: '视频通话',
|
|
27
|
+
19: '群公告',
|
|
28
|
+
20: '分享地址',
|
|
29
|
+
21: '表情',
|
|
30
|
+
22: '强提醒',
|
|
31
|
+
23: '链接',
|
|
32
|
+
24: '群接龙',
|
|
33
|
+
25: '+1回复',
|
|
34
|
+
26: '富文本',
|
|
35
|
+
};
|
|
36
|
+
export function getTypeName(type) {
|
|
37
|
+
const t = Number(type);
|
|
38
|
+
return MSG_TYPES[t] || '未知';
|
|
39
|
+
}
|
|
40
|
+
/** 解析消息 JSON 字符串或对象。 */
|
|
41
|
+
export function parseMsg(value) {
|
|
42
|
+
if (!value)
|
|
43
|
+
return null;
|
|
44
|
+
if (typeof value === 'string') {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(value);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (typeof value === 'object')
|
|
53
|
+
return value;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function normalizeKey(key) {
|
|
57
|
+
if (key.startsWith('%') && key.endsWith('%'))
|
|
58
|
+
return key;
|
|
59
|
+
return `%${key}%`;
|
|
60
|
+
}
|
|
61
|
+
function formatPersonToken(value, nameMap) {
|
|
62
|
+
const uidRaw = value?.u !== undefined && value.u !== null ? String(value.u)
|
|
63
|
+
: value?.uid !== undefined && value.uid !== null ? String(value.uid) : '';
|
|
64
|
+
const cRaw = value?.c !== undefined && value.c !== null ? String(value.c) : '';
|
|
65
|
+
if (uidRaw && nameMap?.[uidRaw])
|
|
66
|
+
return nameMap[uidRaw];
|
|
67
|
+
if (uidRaw)
|
|
68
|
+
return uidRaw;
|
|
69
|
+
return cRaw;
|
|
70
|
+
}
|
|
71
|
+
function formatI18nVal(value, nameMap) {
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return value.map((item) => formatI18nVal(item, nameMap)).filter(Boolean).join('、');
|
|
74
|
+
}
|
|
75
|
+
if (value && typeof value === 'object') {
|
|
76
|
+
if (value.u !== undefined || value.uid !== undefined)
|
|
77
|
+
return formatPersonToken(value, nameMap);
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
if (value === undefined || value === null)
|
|
81
|
+
return '';
|
|
82
|
+
return String(value);
|
|
83
|
+
}
|
|
84
|
+
/** %KEY% 占位符替换(kvs/jvs 合并,jvs 优先)。 */
|
|
85
|
+
export function substituteTemplate(template, kvs, jvs, nameMap) {
|
|
86
|
+
const merged = { ...(kvs || {}), ...(jvs || {}) };
|
|
87
|
+
let text = String(template || '');
|
|
88
|
+
for (const [rawKey, value] of Object.entries(merged)) {
|
|
89
|
+
const key = normalizeKey(rawKey);
|
|
90
|
+
const replacement = formatI18nVal(value, nameMap);
|
|
91
|
+
text = text.split(key).join(replacement);
|
|
92
|
+
}
|
|
93
|
+
return text;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 收集消息体中的 @ 提及映射:递归扫描所有 `aus` 条目(`{id: uid, nm: 姓名}`)→ uid → 姓名。
|
|
97
|
+
* `aus` 由发送端随消息下发,姓名无需再走 hrm 解析;nm 缺失的条目不收集(展示回退 uid)。
|
|
98
|
+
*/
|
|
99
|
+
export function collectMentionMap(msg) {
|
|
100
|
+
const map = new Map();
|
|
101
|
+
const walk = (value) => {
|
|
102
|
+
if (!value || typeof value !== 'object')
|
|
103
|
+
return;
|
|
104
|
+
if (Array.isArray(value)) {
|
|
105
|
+
for (const item of value)
|
|
106
|
+
walk(item);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(value.aus)) {
|
|
110
|
+
for (const item of value.aus) {
|
|
111
|
+
if (item && item.id !== undefined && item.id !== null && item.nm)
|
|
112
|
+
map.set(String(item.id), String(item.nm));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const key of Object.keys(value)) {
|
|
116
|
+
if (key === 'aus')
|
|
117
|
+
continue; // 已处理,避免重复遍历
|
|
118
|
+
walk(value[key]);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
walk(msg);
|
|
122
|
+
return map;
|
|
123
|
+
}
|
|
124
|
+
/** @提及还原:文本中 :@<uid>; 占位符 → @姓名(aus 优先,nameMap 兜底,最后回退 uid)。 */
|
|
125
|
+
export function replaceMentions(text, mentionMap, nameMap) {
|
|
126
|
+
let output = String(text || '');
|
|
127
|
+
if (!output.includes(':@'))
|
|
128
|
+
return output;
|
|
129
|
+
output = output.replace(/:@(\d{1,30});/g, (_, uidRaw) => {
|
|
130
|
+
const uid = uidRaw;
|
|
131
|
+
const name = mentionMap?.get(uid) || nameMap?.[uid] || uid;
|
|
132
|
+
return `@${name}`;
|
|
133
|
+
});
|
|
134
|
+
return output;
|
|
135
|
+
}
|
|
136
|
+
/** 文本段提取:txt.v → i18n 模板替换 → id18n 翻译 → 回退模板原文。 */
|
|
137
|
+
export function extractTxt(seg, opts = {}) {
|
|
138
|
+
const txt = seg?.txt;
|
|
139
|
+
if (!txt)
|
|
140
|
+
return '';
|
|
141
|
+
const mentionMap = opts.mentionMap ?? collectMentionMap(seg);
|
|
142
|
+
if (typeof txt === 'string')
|
|
143
|
+
return replaceMentions(txt, mentionMap, opts.nameMap);
|
|
144
|
+
if (typeof txt !== 'object')
|
|
145
|
+
return '';
|
|
146
|
+
if (txt.v !== undefined && txt.v !== null && String(txt.v) !== '') {
|
|
147
|
+
return replaceMentions(String(txt.v), mentionMap, opts.nameMap);
|
|
148
|
+
}
|
|
149
|
+
const kvs = txt.kvs || {};
|
|
150
|
+
const jvs = txt.jvs || {};
|
|
151
|
+
if (txt.i18n && typeof txt.i18n === 'string' && txt.i18n.includes('%')) {
|
|
152
|
+
return replaceMentions(substituteTemplate(txt.i18n, kvs, jvs, opts.nameMap), mentionMap, opts.nameMap);
|
|
153
|
+
}
|
|
154
|
+
if (txt.id18n !== undefined && txt.id18n !== null) {
|
|
155
|
+
const translated = opts.i18nMap?.[String(txt.id18n)];
|
|
156
|
+
if (translated) {
|
|
157
|
+
return replaceMentions(substituteTemplate(translated, kvs, jvs, opts.nameMap), mentionMap, opts.nameMap);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (txt.i18n && typeof txt.i18n === 'string') {
|
|
161
|
+
return replaceMentions(substituteTemplate(txt.i18n, kvs, jvs, opts.nameMap), mentionMap, opts.nameMap);
|
|
162
|
+
}
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
/** 提取系统消息信息(msg.dt[].sys)。 */
|
|
166
|
+
export function parseSysMsg(msg) {
|
|
167
|
+
const sys = msg?.dt?.[0]?.sys;
|
|
168
|
+
if (!sys)
|
|
169
|
+
return {};
|
|
170
|
+
return {
|
|
171
|
+
group: sys.group !== undefined ? String(sys.group) : undefined,
|
|
172
|
+
type: sys.type !== undefined ? String(sys.type) : undefined,
|
|
173
|
+
tpl: sys.tpl !== undefined ? String(sys.tpl) : undefined,
|
|
174
|
+
etName: sys.et?.nm !== undefined && sys.et.nm !== null ? String(sys.et.nm) : undefined,
|
|
175
|
+
id18n: sys.id18n !== undefined ? String(sys.id18n) : undefined,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/** 消息内容解析主入口。 */
|
|
179
|
+
export function parseContent(value, opts = {}) {
|
|
180
|
+
const msg = parseMsg(value) || {};
|
|
181
|
+
const type = Number(msg.type || 0);
|
|
182
|
+
const typeName = getTypeName(type);
|
|
183
|
+
const dt = Array.isArray(msg.dt) ? msg.dt : [];
|
|
184
|
+
const data = dt[0] || {};
|
|
185
|
+
const sys = parseSysMsg(msg);
|
|
186
|
+
const tf = Number(msg.tf || 0);
|
|
187
|
+
const must = (tf & 0x01) !== 0;
|
|
188
|
+
const localOpts = { ...opts, mentionMap: opts.mentionMap ?? collectMentionMap(msg) };
|
|
189
|
+
let content = '';
|
|
190
|
+
let fileName = '';
|
|
191
|
+
switch (type) {
|
|
192
|
+
case 1:
|
|
193
|
+
content = extractTxt(data, localOpts);
|
|
194
|
+
break;
|
|
195
|
+
case 2: {
|
|
196
|
+
content = parseSegment(data, localOpts) || '[图片]';
|
|
197
|
+
fileName = data?.img?.nm ? String(data.img.nm) : '';
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
case 4:
|
|
201
|
+
fileName = data?.file?.nm ? String(data.file.nm) : '';
|
|
202
|
+
content = fileName ? `[文件]${fileName}` : '[文件]';
|
|
203
|
+
break;
|
|
204
|
+
case 7: {
|
|
205
|
+
content = parseSegment(data, localOpts) || '[视频]';
|
|
206
|
+
fileName = data?.media?.nm ? String(data.media.nm)
|
|
207
|
+
: data?.video?.nm ? String(data.video.nm) : '';
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case 5:
|
|
211
|
+
content = data?.location?.name ? String(data.location.name) : '';
|
|
212
|
+
break;
|
|
213
|
+
case 8:
|
|
214
|
+
content = data?.entity?.entityName ? String(data.entity.entityName) : '';
|
|
215
|
+
break;
|
|
216
|
+
case 20:
|
|
217
|
+
content = data?.shareaddr?.name ? String(data.shareaddr.name) : extractTxt(data, localOpts);
|
|
218
|
+
break;
|
|
219
|
+
case 26:
|
|
220
|
+
content = msg.vv ? String(msg.vv) : dt.map((d) => parseSegment(d, localOpts)).join('');
|
|
221
|
+
break;
|
|
222
|
+
default:
|
|
223
|
+
content = extractTxt(data, localOpts);
|
|
224
|
+
}
|
|
225
|
+
// 系统消息覆盖:sys.tpl=1 且有实体名 → 用实体名
|
|
226
|
+
if (sys.tpl === '1' && sys.etName)
|
|
227
|
+
content = sys.etName;
|
|
228
|
+
// @ 提及兜底还原:富文本 vv / 文件等路径可能仍带 :@<uid>; 占位符,统一还原为 @姓名
|
|
229
|
+
if (content && String(content).includes(':@')) {
|
|
230
|
+
content = replaceMentions(content, localOpts.mentionMap, localOpts.nameMap);
|
|
231
|
+
}
|
|
232
|
+
if (must)
|
|
233
|
+
content = `[必达]${content}`;
|
|
234
|
+
// title:优先 sys.et.nm,否则 i18n 翻译文案
|
|
235
|
+
let title = '';
|
|
236
|
+
if (sys.etName)
|
|
237
|
+
title = sys.etName;
|
|
238
|
+
else if (sys.id18n && opts.i18nMap?.[sys.id18n])
|
|
239
|
+
title = opts.i18nMap[sys.id18n];
|
|
240
|
+
return { type, typeName, content, title, must, fileName, sys };
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 单段解析:图片/文件/视频/媒体/语音/位置 段 → 展示文本,文本段兜底。
|
|
244
|
+
* 展示统一为 `[类型]名称` 形式(名称缺失时只保留 `[类型]`),供 agent 直接渲染。
|
|
245
|
+
*/
|
|
246
|
+
function parseSegment(seg, opts = {}) {
|
|
247
|
+
if (!seg)
|
|
248
|
+
return '';
|
|
249
|
+
const name = (value) => (value?.nm !== undefined && value.nm !== null ? String(value.nm) : '');
|
|
250
|
+
const hasFileId = (value) => Boolean(value) && (value.src !== undefined || value.id !== undefined || value.nm !== undefined);
|
|
251
|
+
if (hasFileId(seg.img))
|
|
252
|
+
return `[图片]${name(seg.img)}`;
|
|
253
|
+
if (hasFileId(seg.file))
|
|
254
|
+
return `[文件]${name(seg.file)}`;
|
|
255
|
+
if (hasFileId(seg.video))
|
|
256
|
+
return `[视频]${name(seg.video)}`;
|
|
257
|
+
if (hasFileId(seg.media))
|
|
258
|
+
return `[视频]${name(seg.media)}`;
|
|
259
|
+
if (hasFileId(seg.voice))
|
|
260
|
+
return `[语音]${name(seg.voice)}`;
|
|
261
|
+
if (seg.location?.name)
|
|
262
|
+
return String(seg.location.name);
|
|
263
|
+
if (seg.txt)
|
|
264
|
+
return extractTxt(seg, opts);
|
|
265
|
+
return '';
|
|
266
|
+
}
|
|
267
|
+
/** 发送者姓名回退规则:自己(authUid)→「我」,其次消息 sname,再次 hrm 姓名,最后 uid。 */
|
|
268
|
+
export function senderName(msg, fromUid, authUid, nameMap = {}) {
|
|
269
|
+
const uid = String(fromUid ?? '');
|
|
270
|
+
if (authUid && uid === authUid)
|
|
271
|
+
return '我';
|
|
272
|
+
const sname = msg?.sname ? String(msg.sname) : '';
|
|
273
|
+
if (sname)
|
|
274
|
+
return sname;
|
|
275
|
+
if (uid && nameMap[uid])
|
|
276
|
+
return nameMap[uid];
|
|
277
|
+
if (uid)
|
|
278
|
+
return uid;
|
|
279
|
+
return '';
|
|
280
|
+
}
|
|
281
|
+
/** 提取图片段。 */
|
|
282
|
+
export function extractImgs(msg) {
|
|
283
|
+
const dt = Array.isArray(msg?.dt) ? msg.dt : [];
|
|
284
|
+
const items = [];
|
|
285
|
+
for (const seg of dt) {
|
|
286
|
+
const img = seg?.img;
|
|
287
|
+
if (!img)
|
|
288
|
+
continue;
|
|
289
|
+
const fileId = img.src !== undefined && img.src !== null ? String(img.src) : img.id !== undefined ? String(img.id) : '';
|
|
290
|
+
if (!fileId)
|
|
291
|
+
continue;
|
|
292
|
+
items.push({
|
|
293
|
+
fileId,
|
|
294
|
+
name: img.nm !== undefined ? String(img.nm) : '',
|
|
295
|
+
w: img.w !== undefined ? String(img.w) : undefined,
|
|
296
|
+
h: img.h !== undefined ? String(img.h) : undefined,
|
|
297
|
+
size: img.s !== undefined ? String(img.s) : undefined,
|
|
298
|
+
mime: img.t !== undefined ? String(img.t) : undefined,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return items;
|
|
302
|
+
}
|
|
303
|
+
/** 提取视频段。 */
|
|
304
|
+
export function extractVideos(msg) {
|
|
305
|
+
const dt = Array.isArray(msg?.dt) ? msg.dt : [];
|
|
306
|
+
const items = [];
|
|
307
|
+
for (const seg of dt) {
|
|
308
|
+
const media = seg?.media || seg?.video;
|
|
309
|
+
if (!media)
|
|
310
|
+
continue;
|
|
311
|
+
const fileId = media.src !== undefined && media.src !== null ? String(media.src) : media.id !== undefined ? String(media.id) : '';
|
|
312
|
+
if (!fileId)
|
|
313
|
+
continue;
|
|
314
|
+
items.push({
|
|
315
|
+
fileId,
|
|
316
|
+
name: media.nm !== undefined ? String(media.nm) : '',
|
|
317
|
+
ext: media.ext !== undefined ? String(media.ext) : undefined,
|
|
318
|
+
w: media.w !== undefined ? String(media.w) : undefined,
|
|
319
|
+
h: media.h !== undefined ? String(media.h) : undefined,
|
|
320
|
+
size: media.s !== undefined ? String(media.s) : undefined,
|
|
321
|
+
mime: media.t !== undefined ? String(media.t) : undefined,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
return items;
|
|
325
|
+
}
|
|
326
|
+
/** 提取文件段。 */
|
|
327
|
+
export function extractFiles(msg) {
|
|
328
|
+
const dt = Array.isArray(msg?.dt) ? msg.dt : [];
|
|
329
|
+
const items = [];
|
|
330
|
+
for (const seg of dt) {
|
|
331
|
+
const file = seg?.file;
|
|
332
|
+
if (!file)
|
|
333
|
+
continue;
|
|
334
|
+
const fileId = file.id !== undefined && file.id !== null ? String(file.id) : file.src !== undefined ? String(file.src) : '';
|
|
335
|
+
if (!fileId)
|
|
336
|
+
continue;
|
|
337
|
+
items.push({
|
|
338
|
+
fileId,
|
|
339
|
+
name: file.nm !== undefined ? String(file.nm) : '',
|
|
340
|
+
ext: file.ext !== undefined ? String(file.ext) : undefined,
|
|
341
|
+
size: file.s !== undefined ? String(file.s) : undefined,
|
|
342
|
+
mime: file.t !== undefined ? String(file.t) : undefined,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return items;
|
|
346
|
+
}
|
|
347
|
+
function walkCollect(value, kind, out) {
|
|
348
|
+
if (value === undefined || value === null)
|
|
349
|
+
return;
|
|
350
|
+
if (Array.isArray(value)) {
|
|
351
|
+
for (const item of value)
|
|
352
|
+
walkCollect(item, kind, out);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (typeof value === 'object') {
|
|
356
|
+
for (const key of Object.keys(value)) {
|
|
357
|
+
const child = value[key];
|
|
358
|
+
if (kind === 'id18n' && key === 'id18n' && child !== undefined && child !== null && /^\d+$/.test(String(child))) {
|
|
359
|
+
out.add(String(child));
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (kind === 'person') {
|
|
363
|
+
if ((key === 'u' || key === 'uid') && child !== undefined && child !== null) {
|
|
364
|
+
const uid = String(child);
|
|
365
|
+
if (/^\d+$/.test(uid))
|
|
366
|
+
out.add(uid);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (key === 'fd' && child === '@') {
|
|
370
|
+
const id = value.id;
|
|
371
|
+
if (id !== undefined && id !== null)
|
|
372
|
+
out.add(String(id));
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (key === 'txt' && typeof child === 'string') {
|
|
376
|
+
const matches = child.match(/:@(\d+);/g);
|
|
377
|
+
if (matches) {
|
|
378
|
+
for (const m of matches) {
|
|
379
|
+
const uid = m.slice(2, -1);
|
|
380
|
+
if (/^\d+$/.test(uid))
|
|
381
|
+
out.add(uid);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
walkCollect(child, kind, out);
|
|
388
|
+
}
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (kind === 'person' && typeof value === 'string') {
|
|
392
|
+
const matches = value.match(/:@(\d+);/g);
|
|
393
|
+
if (matches) {
|
|
394
|
+
for (const m of matches) {
|
|
395
|
+
const uid = m.slice(2, -1);
|
|
396
|
+
if (/^\d+$/.test(uid))
|
|
397
|
+
out.add(uid);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/** 收集消息集合中待翻译 i18n id。 */
|
|
403
|
+
export function collectI18nIds(msgs) {
|
|
404
|
+
const out = new Set();
|
|
405
|
+
for (const item of msgs) {
|
|
406
|
+
const msg = parseMsg(item?.msg);
|
|
407
|
+
if (msg)
|
|
408
|
+
walkCollect(msg, 'id18n', out);
|
|
409
|
+
}
|
|
410
|
+
return [...out];
|
|
411
|
+
}
|
|
412
|
+
/** 收集消息集合中人员 uid(@提及 + 人员对象 + 占位符)。 */
|
|
413
|
+
export function collectPersonIds(msgs) {
|
|
414
|
+
const out = new Set();
|
|
415
|
+
for (const item of msgs) {
|
|
416
|
+
const msg = parseMsg(item?.msg);
|
|
417
|
+
if (msg)
|
|
418
|
+
walkCollect(msg, 'person', out);
|
|
419
|
+
}
|
|
420
|
+
return [...out];
|
|
421
|
+
}
|
|
422
|
+
/** 多行换行转 ⏎(展示单行化)。 */
|
|
423
|
+
export function collapseNewlines(text) {
|
|
424
|
+
return String(text || '').replace(/\r\n+/g, '\n').replace(/\n+/g, '⏎');
|
|
425
|
+
}
|
|
426
|
+
/** 表格 | 转义。 */
|
|
427
|
+
export function escapePipe(text) {
|
|
428
|
+
return String(text || '').replace(/\|/g, '\\|');
|
|
429
|
+
}
|