twinny 0.0.0-dev.260525150705
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/LICENSE +21 -0
- package/README.md +264 -0
- package/README.zh-CN.md +252 -0
- package/configs/banner.png +0 -0
- package/configs/logo.png +0 -0
- package/dist/app/caffeinate.d.ts +28 -0
- package/dist/app/caffeinate.js +96 -0
- package/dist/app/caffeinate.js.map +1 -0
- package/dist/app/daemon.d.ts +1 -0
- package/dist/app/daemon.js +44 -0
- package/dist/app/daemon.js.map +1 -0
- package/dist/app/lark-assets.d.ts +25 -0
- package/dist/app/lark-assets.js +108 -0
- package/dist/app/lark-assets.js.map +1 -0
- package/dist/app/startup-probe.d.ts +17 -0
- package/dist/app/startup-probe.js +90 -0
- package/dist/app/startup-probe.js.map +1 -0
- package/dist/app/wiring.d.ts +122 -0
- package/dist/app/wiring.js +694 -0
- package/dist/app/wiring.js.map +1 -0
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/commands.js +47 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/install-wizard.d.ts +41 -0
- package/dist/cli/install-wizard.js +629 -0
- package/dist/cli/install-wizard.js.map +1 -0
- package/dist/codex/appserver.d.ts +109 -0
- package/dist/codex/appserver.js +308 -0
- package/dist/codex/appserver.js.map +1 -0
- package/dist/codex/goal.d.ts +64 -0
- package/dist/codex/goal.js +433 -0
- package/dist/codex/goal.js.map +1 -0
- package/dist/codex/index.d.ts +6 -0
- package/dist/codex/index.js +7 -0
- package/dist/codex/index.js.map +1 -0
- package/dist/codex/protocol.d.ts +95 -0
- package/dist/codex/protocol.js +205 -0
- package/dist/codex/protocol.js.map +1 -0
- package/dist/codex/thread-name.d.ts +3 -0
- package/dist/codex/thread-name.js +27 -0
- package/dist/codex/thread-name.js.map +1 -0
- package/dist/codex/thread.d.ts +76 -0
- package/dist/codex/thread.js +80 -0
- package/dist/codex/thread.js.map +1 -0
- package/dist/codex/turn.d.ts +166 -0
- package/dist/codex/turn.js +746 -0
- package/dist/codex/turn.js.map +1 -0
- package/dist/config/bootstrap.d.ts +14 -0
- package/dist/config/bootstrap.js +56 -0
- package/dist/config/bootstrap.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +49 -0
- package/dist/config/loader.js +467 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/paths.d.ts +11 -0
- package/dist/config/paths.js +43 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/secrets.d.ts +33 -0
- package/dist/config/secrets.js +85 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/conversation/manager.d.ts +701 -0
- package/dist/conversation/manager.js +7673 -0
- package/dist/conversation/manager.js.map +1 -0
- package/dist/conversation/queue.d.ts +8 -0
- package/dist/conversation/queue.js +28 -0
- package/dist/conversation/queue.js.map +1 -0
- package/dist/conversation/routing.d.ts +11 -0
- package/dist/conversation/routing.js +55 -0
- package/dist/conversation/routing.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/lark/auth.d.ts +41 -0
- package/dist/lark/auth.js +132 -0
- package/dist/lark/auth.js.map +1 -0
- package/dist/lark/browser-auth.d.ts +68 -0
- package/dist/lark/browser-auth.js +258 -0
- package/dist/lark/browser-auth.js.map +1 -0
- package/dist/lark/cards.d.ts +140 -0
- package/dist/lark/cards.js +1150 -0
- package/dist/lark/cards.js.map +1 -0
- package/dist/lark/contact.d.ts +41 -0
- package/dist/lark/contact.js +122 -0
- package/dist/lark/contact.js.map +1 -0
- package/dist/lark/events.d.ts +65 -0
- package/dist/lark/events.js +218 -0
- package/dist/lark/events.js.map +1 -0
- package/dist/lark/files.d.ts +36 -0
- package/dist/lark/files.js +191 -0
- package/dist/lark/files.js.map +1 -0
- package/dist/lark/filters.d.ts +73 -0
- package/dist/lark/filters.js +678 -0
- package/dist/lark/filters.js.map +1 -0
- package/dist/lark/index.d.ts +10 -0
- package/dist/lark/index.js +11 -0
- package/dist/lark/index.js.map +1 -0
- package/dist/lark/messages.d.ts +87 -0
- package/dist/lark/messages.js +428 -0
- package/dist/lark/messages.js.map +1 -0
- package/dist/lark/openapi.d.ts +58 -0
- package/dist/lark/openapi.js +206 -0
- package/dist/lark/openapi.js.map +1 -0
- package/dist/lark/redactor.d.ts +5 -0
- package/dist/lark/redactor.js +68 -0
- package/dist/lark/redactor.js.map +1 -0
- package/dist/lark/types.d.ts +49 -0
- package/dist/lark/types.js +18 -0
- package/dist/lark/types.js.map +1 -0
- package/dist/launchd/install.d.ts +22 -0
- package/dist/launchd/install.js +114 -0
- package/dist/launchd/install.js.map +1 -0
- package/dist/launchd/plist.d.ts +10 -0
- package/dist/launchd/plist.js +61 -0
- package/dist/launchd/plist.js.map +1 -0
- package/dist/lock/index.d.ts +20 -0
- package/dist/lock/index.js +74 -0
- package/dist/lock/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +11 -0
- package/dist/main.js.map +1 -0
- package/dist/markdown.d.ts +12 -0
- package/dist/markdown.js +149 -0
- package/dist/markdown.js.map +1 -0
- package/dist/observability/health.d.ts +25 -0
- package/dist/observability/health.js +187 -0
- package/dist/observability/health.js.map +1 -0
- package/dist/observability/logs.d.ts +10 -0
- package/dist/observability/logs.js +34 -0
- package/dist/observability/logs.js.map +1 -0
- package/dist/observability/system-notifications.d.ts +25 -0
- package/dist/observability/system-notifications.js +33 -0
- package/dist/observability/system-notifications.js.map +1 -0
- package/dist/profiles/guest.d.ts +19 -0
- package/dist/profiles/guest.js +241 -0
- package/dist/profiles/guest.js.map +1 -0
- package/dist/profiles/index.d.ts +5 -0
- package/dist/profiles/index.js +14 -0
- package/dist/profiles/index.js.map +1 -0
- package/dist/profiles/owner.d.ts +1 -0
- package/dist/profiles/owner.js +6 -0
- package/dist/profiles/owner.js.map +1 -0
- package/dist/store/db.d.ts +10 -0
- package/dist/store/db.js +29 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/migrations.d.ts +13 -0
- package/dist/store/migrations.js +79 -0
- package/dist/store/migrations.js.map +1 -0
- package/dist/store/repositories.d.ts +227 -0
- package/dist/store/repositories.js +1384 -0
- package/dist/store/repositories.js.map +1 -0
- package/dist/telemetry/client.d.ts +60 -0
- package/dist/telemetry/client.js +204 -0
- package/dist/telemetry/client.js.map +1 -0
- package/dist/telemetry/hash.d.ts +1 -0
- package/dist/telemetry/hash.js +8 -0
- package/dist/telemetry/hash.js.map +1 -0
- package/dist/telemetry/index.d.ts +4 -0
- package/dist/telemetry/index.js +5 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/posthog.d.ts +27 -0
- package/dist/telemetry/posthog.js +45 -0
- package/dist/telemetry/posthog.js.map +1 -0
- package/dist/telemetry/reporter.d.ts +13 -0
- package/dist/telemetry/reporter.js +29 -0
- package/dist/telemetry/reporter.js.map +1 -0
- package/dist/types.d.ts +330 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/version.js.map +1 -0
- package/dist/version.json +3 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.js +3 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/manager.d.ts +14 -0
- package/dist/workspace/manager.js +69 -0
- package/dist/workspace/manager.js.map +1 -0
- package/dist/workspace/slug.d.ts +8 -0
- package/dist/workspace/slug.js +59 -0
- package/dist/workspace/slug.js.map +1 -0
- package/migrations/0001_initial.sql +102 -0
- package/package.json +85 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
export function normalizeIncomingLarkMessage(raw, options = {}) {
|
|
2
|
+
const result = normalizeIncomingLarkMessageWithReason(raw, options);
|
|
3
|
+
return result.kind === "message" ? result.message : null;
|
|
4
|
+
}
|
|
5
|
+
export function normalizeIncomingLarkMessageWithReason(raw, options = {}) {
|
|
6
|
+
const event = unwrapReceiveEvent(raw);
|
|
7
|
+
if (!isRecord(event) || !isRecord(event.sender) || !isRecord(event.message)) {
|
|
8
|
+
return ignored("malformed_event", raw);
|
|
9
|
+
}
|
|
10
|
+
const sender = event.sender;
|
|
11
|
+
const senderId = isRecord(sender.sender_id) ? sender.sender_id : {};
|
|
12
|
+
const senderOpenId = stringValue(senderId.open_id);
|
|
13
|
+
if (!senderOpenId) {
|
|
14
|
+
return ignored("missing_sender_open_id", raw);
|
|
15
|
+
}
|
|
16
|
+
const senderType = stringValue(sender.sender_type)?.toLowerCase();
|
|
17
|
+
if (senderType === "app" || senderType === "bot" || senderOpenId === options.botOpenId) {
|
|
18
|
+
return ignored("bot_self_message", raw);
|
|
19
|
+
}
|
|
20
|
+
const senderName = firstStringValue(sender.name, sender.sender_name, sender.display_name, sender.sender_display_name);
|
|
21
|
+
const message = event.message;
|
|
22
|
+
const chatType = normalizeChatType(stringValue(message.chat_type));
|
|
23
|
+
if (!chatType) {
|
|
24
|
+
return ignored("unsupported_chat_type", raw);
|
|
25
|
+
}
|
|
26
|
+
const messageType = stringValue(message.message_type) ?? "unknown";
|
|
27
|
+
const messageId = stringValue(message.message_id);
|
|
28
|
+
if (!messageId) {
|
|
29
|
+
return ignored("missing_message_id", raw);
|
|
30
|
+
}
|
|
31
|
+
const chatId = chatType === "p2p" ? senderOpenId : stringValue(message.chat_id);
|
|
32
|
+
if (!chatId) {
|
|
33
|
+
return ignored("malformed_event", raw);
|
|
34
|
+
}
|
|
35
|
+
const content = normalizeLarkMessageContent(messageType, message.content);
|
|
36
|
+
const resources = content.resources;
|
|
37
|
+
const shouldUseRaw = content.text === null || (content.text.length === 0 && resources.length === 0);
|
|
38
|
+
const text = shouldUseRaw ? stringifyRawLarkMessageForCodex(message) : (content.text ?? "");
|
|
39
|
+
const rawForCodex = content.rawForCodex || shouldUseRaw;
|
|
40
|
+
return {
|
|
41
|
+
kind: "message",
|
|
42
|
+
message: {
|
|
43
|
+
eventId: stringValue(event.event_id) ??
|
|
44
|
+
stringValue(event.uuid) ??
|
|
45
|
+
stringValue(isRecord(raw) && isRecord(raw.header) ? raw.header.event_id : undefined) ??
|
|
46
|
+
messageId,
|
|
47
|
+
messageId,
|
|
48
|
+
chatId,
|
|
49
|
+
chatType,
|
|
50
|
+
messageType,
|
|
51
|
+
senderOpenId,
|
|
52
|
+
senderName,
|
|
53
|
+
larkGroupId: chatType === "p2p" ? undefined : chatId,
|
|
54
|
+
larkThreadId: larkThreadIdForMessage(message, messageId, chatType),
|
|
55
|
+
larkRootMessageId: firstStringValue(message.root_id),
|
|
56
|
+
larkParentMessageId: firstStringValue(message.parent_id),
|
|
57
|
+
mentions: normalizeMentions(message.mentions),
|
|
58
|
+
resources: resources.length > 0 ? resources : undefined,
|
|
59
|
+
rawForCodex: rawForCodex ? true : undefined,
|
|
60
|
+
text,
|
|
61
|
+
createTime: parseEpochMs(message.create_time ?? event.create_time),
|
|
62
|
+
raw
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function normalizeLarkBotMenuWithReason(raw) {
|
|
67
|
+
if (!isRecord(raw)) {
|
|
68
|
+
return ignoredBotMenu("malformed_event", raw);
|
|
69
|
+
}
|
|
70
|
+
const header = eventHeader(raw);
|
|
71
|
+
const event = eventPayload(raw);
|
|
72
|
+
const eventKey = stringValue(event.event_key);
|
|
73
|
+
if (!eventKey) {
|
|
74
|
+
return ignoredBotMenu("missing_event_key", raw);
|
|
75
|
+
}
|
|
76
|
+
const action = botMenuActionForEventKey(eventKey);
|
|
77
|
+
if (!action) {
|
|
78
|
+
return ignoredBotMenu("unsupported_event_key", raw);
|
|
79
|
+
}
|
|
80
|
+
const operator = isRecord(event.operator) ? event.operator : {};
|
|
81
|
+
const operatorId = isRecord(operator.operator_id) ? operator.operator_id : {};
|
|
82
|
+
const operatorOpenId = stringValue(operatorId.open_id);
|
|
83
|
+
if (!operatorOpenId) {
|
|
84
|
+
return ignoredBotMenu("missing_operator_open_id", raw);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
kind: "bot_menu",
|
|
88
|
+
action: {
|
|
89
|
+
eventId: firstStringValue(header.event_id, raw.event_id, raw.uuid, `${eventKey}:${operatorOpenId}`) ?? `${eventKey}:${operatorOpenId}`,
|
|
90
|
+
eventKey,
|
|
91
|
+
action,
|
|
92
|
+
operatorOpenId,
|
|
93
|
+
operatorName: stringValue(operator.operator_name),
|
|
94
|
+
chatId: chatIdFromBotMenuEvent(event),
|
|
95
|
+
timestamp: parseEpochMs(event.timestamp ?? header.create_time ?? raw.create_time),
|
|
96
|
+
raw
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function normalizeLarkMessageRecallWithReason(raw) {
|
|
101
|
+
if (!isRecord(raw)) {
|
|
102
|
+
return ignoredMessageChange("malformed_event", raw);
|
|
103
|
+
}
|
|
104
|
+
const header = eventHeader(raw);
|
|
105
|
+
const event = eventPayload(raw);
|
|
106
|
+
const messageId = stringValue(event.message_id);
|
|
107
|
+
if (!messageId) {
|
|
108
|
+
return ignoredMessageChange("missing_message_id", raw);
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
kind: "recall",
|
|
112
|
+
recall: {
|
|
113
|
+
eventId: firstStringValue(header.event_id, raw.event_id, raw.uuid, messageId) ?? messageId,
|
|
114
|
+
messageId,
|
|
115
|
+
chatId: stringValue(event.chat_id) ?? chatIdFromChatInfo(event.chat_info),
|
|
116
|
+
recallTime: parseEpochMs(event.recall_time),
|
|
117
|
+
raw
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export function normalizeLarkMessageContent(messageType, content) {
|
|
122
|
+
if (messageType === "text") {
|
|
123
|
+
return {
|
|
124
|
+
text: normalizeTextContent(content),
|
|
125
|
+
resources: [],
|
|
126
|
+
rawForCodex: false
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (messageType === "post") {
|
|
130
|
+
const post = normalizePostContent(content);
|
|
131
|
+
if (post) {
|
|
132
|
+
return {
|
|
133
|
+
...post,
|
|
134
|
+
rawForCodex: false
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (messageType === "interactive") {
|
|
139
|
+
const card = normalizeInteractiveCardContent(content);
|
|
140
|
+
if (card) {
|
|
141
|
+
return {
|
|
142
|
+
...card,
|
|
143
|
+
rawForCodex: false
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const resources = extractMessageResources(messageType, content);
|
|
148
|
+
return {
|
|
149
|
+
text: resources.length === 0 ? null : fallbackResourceText(resources),
|
|
150
|
+
resources,
|
|
151
|
+
rawForCodex: resources.length === 0
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function stringifyRawLarkMessageForCodex(message) {
|
|
155
|
+
const body = isRecord(message.body) ? message.body : {};
|
|
156
|
+
const compact = {
|
|
157
|
+
message_type: stringValue(message.message_type) ?? stringValue(message.msg_type) ?? "unknown",
|
|
158
|
+
content: message.content ?? body.content ?? null
|
|
159
|
+
};
|
|
160
|
+
try {
|
|
161
|
+
return JSON.stringify(compact) ?? "";
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return String(message);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function fallbackResourceText(resources) {
|
|
168
|
+
if (resources.length === 0) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
return resources
|
|
172
|
+
.map((resource) => `收到一个文件,资源 key:${resource.fileKey}`)
|
|
173
|
+
.join("\n");
|
|
174
|
+
}
|
|
175
|
+
function normalizePostContent(content) {
|
|
176
|
+
const parsed = parseContentObject(content);
|
|
177
|
+
const post = parsed ? getPostContent(parsed) : null;
|
|
178
|
+
if (!post) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const resources = [];
|
|
182
|
+
const parts = [];
|
|
183
|
+
const title = stringValue(post.title)?.trim();
|
|
184
|
+
if (title) {
|
|
185
|
+
parts.push(`# ${escapeMarkdownText(title)}`);
|
|
186
|
+
}
|
|
187
|
+
const paragraphs = Array.isArray(post.content) ? post.content : [];
|
|
188
|
+
for (const paragraph of paragraphs) {
|
|
189
|
+
if (!Array.isArray(paragraph)) {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const rendered = renderPostParagraph(paragraph, resources).trim();
|
|
193
|
+
if (rendered) {
|
|
194
|
+
parts.push(rendered);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
text: parts.join("\n\n"),
|
|
199
|
+
resources
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function getPostContent(content) {
|
|
203
|
+
if (Array.isArray(content.content)) {
|
|
204
|
+
return content;
|
|
205
|
+
}
|
|
206
|
+
for (const locale of ["zh_cn", "en_us", "ja_jp"]) {
|
|
207
|
+
const value = content[locale];
|
|
208
|
+
if (isRecord(value) && Array.isArray(value.content)) {
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const value of Object.values(content)) {
|
|
213
|
+
if (isRecord(value) && Array.isArray(value.content)) {
|
|
214
|
+
return value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
function normalizeInteractiveCardContent(content) {
|
|
220
|
+
const resolved = resolveInteractiveCardContent(content);
|
|
221
|
+
if (!resolved) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const resources = [];
|
|
225
|
+
const attributes = [];
|
|
226
|
+
const header = isRecord(resolved.header) ? resolved.header : {};
|
|
227
|
+
const title = cardHeaderTextContent(header, "title");
|
|
228
|
+
const subtitle = cardHeaderTextContent(header, "subtitle");
|
|
229
|
+
if (title) {
|
|
230
|
+
attributes.push(["title", title]);
|
|
231
|
+
}
|
|
232
|
+
if (subtitle) {
|
|
233
|
+
attributes.push(["subtitle", subtitle]);
|
|
234
|
+
}
|
|
235
|
+
const bodyParts = [];
|
|
236
|
+
const body = isRecord(resolved.body) ? resolved.body : {};
|
|
237
|
+
renderCardElements(cardChildElements(body), resources, bodyParts);
|
|
238
|
+
const openTag = attributes.length > 0
|
|
239
|
+
? `<card ${attributes.map(([name, value]) => `${name}="${escapeXmlAttribute(value)}"`).join(" ")}>`
|
|
240
|
+
: "<card>";
|
|
241
|
+
return {
|
|
242
|
+
text: [openTag, ...bodyParts, "</card>"].join("\n"),
|
|
243
|
+
resources
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function resolveInteractiveCardContent(content) {
|
|
247
|
+
const card = parseContentObject(content);
|
|
248
|
+
if (!card) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
if (isJson2InteractiveCard(card)) {
|
|
252
|
+
return card;
|
|
253
|
+
}
|
|
254
|
+
const userDsl = parseContentObject(card.user_dsl);
|
|
255
|
+
if (userDsl && isJson2InteractiveCard(userDsl)) {
|
|
256
|
+
return userDsl;
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
function isJson2InteractiveCard(card) {
|
|
261
|
+
return stringValue(card.schema) === "2.0";
|
|
262
|
+
}
|
|
263
|
+
function renderCardElements(elements, resources, parts) {
|
|
264
|
+
if (!Array.isArray(elements)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
for (const element of elements) {
|
|
268
|
+
renderCardElement(element, resources, parts);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function renderCardElement(element, resources, parts) {
|
|
272
|
+
if (!isRecord(element)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const tag = stringValue(element.tag);
|
|
276
|
+
if (tag === "markdown") {
|
|
277
|
+
pushNonEmpty(parts, cardMarkdownContent(element));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (tag === "div") {
|
|
281
|
+
pushNonEmpty(parts, cardTextContent(element.text));
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (tag === "plain_text") {
|
|
285
|
+
pushNonEmpty(parts, cardTextContent(element));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (tag === "img") {
|
|
289
|
+
const imageKey = cardImageKey(element);
|
|
290
|
+
if (imageKey) {
|
|
291
|
+
const placeholder = resourcePlaceholder(resources.length);
|
|
292
|
+
resources.push({
|
|
293
|
+
resourceType: "image",
|
|
294
|
+
fileKey: imageKey,
|
|
295
|
+
codexTag: "file",
|
|
296
|
+
textPlaceholder: placeholder
|
|
297
|
+
});
|
|
298
|
+
parts.push(placeholder);
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (tag === "media") {
|
|
303
|
+
const fileKey = stringValue(element.file_key);
|
|
304
|
+
if (fileKey) {
|
|
305
|
+
const placeholder = resourcePlaceholder(resources.length);
|
|
306
|
+
resources.push({
|
|
307
|
+
resourceType: "file",
|
|
308
|
+
fileKey,
|
|
309
|
+
codexTag: "file",
|
|
310
|
+
textPlaceholder: placeholder
|
|
311
|
+
});
|
|
312
|
+
parts.push(placeholder);
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (isDiscardedCardElementTag(tag)) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (tag === "collapsible_panel") {
|
|
320
|
+
if (isRecord(element.header)) {
|
|
321
|
+
pushNonEmpty(parts, cardHeaderTextContent(element.header, "title"));
|
|
322
|
+
}
|
|
323
|
+
renderCardElements(cardChildElements(element), resources, parts);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (tag === "column_set") {
|
|
327
|
+
renderCardColumns(element.columns, resources, parts);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (isCardContainerTag(tag)) {
|
|
331
|
+
renderCardElements(cardChildElements(element), resources, parts);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function renderCardColumns(columns, resources, parts) {
|
|
335
|
+
if (!Array.isArray(columns)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
for (const column of columns) {
|
|
339
|
+
if (isRecord(column)) {
|
|
340
|
+
renderCardElements(cardChildElements(column), resources, parts);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function cardMarkdownContent(element) {
|
|
345
|
+
return stringValue(element.content);
|
|
346
|
+
}
|
|
347
|
+
function cardHeaderTextContent(header, key) {
|
|
348
|
+
return cardTextContent(header[key]);
|
|
349
|
+
}
|
|
350
|
+
function cardChildElements(element) {
|
|
351
|
+
return element.elements;
|
|
352
|
+
}
|
|
353
|
+
function cardTextContent(value) {
|
|
354
|
+
if (!isRecord(value)) {
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
return stringValue(value.content);
|
|
358
|
+
}
|
|
359
|
+
function cardImageKey(element) {
|
|
360
|
+
return stringValue(element.img_key) ?? stringValue(element.image_key);
|
|
361
|
+
}
|
|
362
|
+
function pushNonEmpty(parts, value) {
|
|
363
|
+
if (value !== undefined && value.trim() !== "") {
|
|
364
|
+
parts.push(value);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function isCardContainerTag(tag) {
|
|
368
|
+
return tag === "column" || tag === "form" || tag === "interactive_container";
|
|
369
|
+
}
|
|
370
|
+
function isDiscardedCardElementTag(tag) {
|
|
371
|
+
return tag === "button" ||
|
|
372
|
+
tag === "select_static" ||
|
|
373
|
+
tag === "multi_select_static" ||
|
|
374
|
+
tag === "select_person" ||
|
|
375
|
+
tag === "multi_select_person" ||
|
|
376
|
+
tag === "input" ||
|
|
377
|
+
tag === "chart" ||
|
|
378
|
+
tag === "date_picker" ||
|
|
379
|
+
tag === "picker_time" ||
|
|
380
|
+
tag === "picker_datetime" ||
|
|
381
|
+
tag === "checker" ||
|
|
382
|
+
tag === "overflow" ||
|
|
383
|
+
tag === "table";
|
|
384
|
+
}
|
|
385
|
+
function renderPostParagraph(paragraph, resources) {
|
|
386
|
+
return paragraph.map((node) => renderPostNode(node, resources)).join("");
|
|
387
|
+
}
|
|
388
|
+
function renderPostNode(node, resources) {
|
|
389
|
+
if (!isRecord(node)) {
|
|
390
|
+
return "";
|
|
391
|
+
}
|
|
392
|
+
const tag = stringValue(node.tag);
|
|
393
|
+
if (tag === "text") {
|
|
394
|
+
return renderStyledMarkdownText(stringValue(node.text) ?? "", node.style);
|
|
395
|
+
}
|
|
396
|
+
if (tag === "a") {
|
|
397
|
+
const text = renderStyledMarkdownText(stringValue(node.text) ?? stringValue(node.href) ?? "", node.style);
|
|
398
|
+
const href = stringValue(node.href);
|
|
399
|
+
return href ? `[${text}](${escapeMarkdownUrl(href)})` : text;
|
|
400
|
+
}
|
|
401
|
+
if (tag === "at") {
|
|
402
|
+
const displayName = stringValue(node.user_name) ?? stringValue(node.user_id) ?? "unknown";
|
|
403
|
+
return renderStyledMarkdownText(`@${displayName}`, node.style);
|
|
404
|
+
}
|
|
405
|
+
if (tag === "img") {
|
|
406
|
+
const imageKey = stringValue(node.image_key);
|
|
407
|
+
if (!imageKey) {
|
|
408
|
+
return renderUnsupportedPostNode(node);
|
|
409
|
+
}
|
|
410
|
+
const placeholder = resourcePlaceholder(resources.length);
|
|
411
|
+
resources.push({
|
|
412
|
+
resourceType: "image",
|
|
413
|
+
fileKey: imageKey,
|
|
414
|
+
codexTag: "img",
|
|
415
|
+
textPlaceholder: placeholder
|
|
416
|
+
});
|
|
417
|
+
return placeholder;
|
|
418
|
+
}
|
|
419
|
+
if (tag === "media") {
|
|
420
|
+
const fileKey = stringValue(node.file_key);
|
|
421
|
+
if (!fileKey) {
|
|
422
|
+
return renderUnsupportedPostNode(node);
|
|
423
|
+
}
|
|
424
|
+
const placeholder = resourcePlaceholder(resources.length);
|
|
425
|
+
resources.push({
|
|
426
|
+
resourceType: "file",
|
|
427
|
+
fileKey,
|
|
428
|
+
codexTag: "video",
|
|
429
|
+
textPlaceholder: placeholder
|
|
430
|
+
});
|
|
431
|
+
return placeholder;
|
|
432
|
+
}
|
|
433
|
+
if (tag === "emotion") {
|
|
434
|
+
const emojiType = stringValue(node.emoji_type);
|
|
435
|
+
return emojiType ? `:${escapeMarkdownText(emojiType)}:` : "";
|
|
436
|
+
}
|
|
437
|
+
if (tag === "hr") {
|
|
438
|
+
return "---";
|
|
439
|
+
}
|
|
440
|
+
if (tag === "code_block") {
|
|
441
|
+
const language = (stringValue(node.language) ?? "").toLowerCase();
|
|
442
|
+
return `\`\`\`${language}\n${stringValue(node.text) ?? ""}\n\`\`\``;
|
|
443
|
+
}
|
|
444
|
+
if (tag === "md") {
|
|
445
|
+
return stringValue(node.text) ?? "";
|
|
446
|
+
}
|
|
447
|
+
return renderUnsupportedPostNode(node);
|
|
448
|
+
}
|
|
449
|
+
function renderStyledMarkdownText(text, style) {
|
|
450
|
+
let rendered = escapeMarkdownText(text);
|
|
451
|
+
const styles = Array.isArray(style) ? style.filter((value) => typeof value === "string") : [];
|
|
452
|
+
if (styles.includes("underline")) {
|
|
453
|
+
rendered = `<u>${rendered}</u>`;
|
|
454
|
+
}
|
|
455
|
+
if (styles.includes("lineThrough")) {
|
|
456
|
+
rendered = `~~${rendered}~~`;
|
|
457
|
+
}
|
|
458
|
+
if (styles.includes("bold") && styles.includes("italic")) {
|
|
459
|
+
return `***${rendered}***`;
|
|
460
|
+
}
|
|
461
|
+
if (styles.includes("bold")) {
|
|
462
|
+
rendered = `**${rendered}**`;
|
|
463
|
+
}
|
|
464
|
+
if (styles.includes("italic")) {
|
|
465
|
+
rendered = `*${rendered}*`;
|
|
466
|
+
}
|
|
467
|
+
return rendered;
|
|
468
|
+
}
|
|
469
|
+
function renderUnsupportedPostNode(node) {
|
|
470
|
+
try {
|
|
471
|
+
return `\`${JSON.stringify(node).replaceAll("`", "\\`")}\``;
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
return "";
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function resourcePlaceholder(index) {
|
|
478
|
+
return `{{TWINNY_LARK_RESOURCE_${index}}}`;
|
|
479
|
+
}
|
|
480
|
+
function escapeMarkdownText(value) {
|
|
481
|
+
return value.replace(/([\\`*_{}\[\]()#+.!|>~-])/g, "\\$1");
|
|
482
|
+
}
|
|
483
|
+
function escapeMarkdownUrl(value) {
|
|
484
|
+
return value.replace(/\)/g, "%29");
|
|
485
|
+
}
|
|
486
|
+
function escapeXmlAttribute(value) {
|
|
487
|
+
return value
|
|
488
|
+
.replace(/&/g, "&")
|
|
489
|
+
.replace(/"/g, """)
|
|
490
|
+
.replace(/</g, "<")
|
|
491
|
+
.replace(/>/g, ">");
|
|
492
|
+
}
|
|
493
|
+
function extractMessageResources(messageType, content) {
|
|
494
|
+
const parsed = parseContentObject(content);
|
|
495
|
+
if (!parsed) {
|
|
496
|
+
return [];
|
|
497
|
+
}
|
|
498
|
+
if (messageType === "image") {
|
|
499
|
+
const imageKey = stringValue(parsed.image_key);
|
|
500
|
+
return imageKey ? [{ resourceType: "image", fileKey: imageKey }] : [];
|
|
501
|
+
}
|
|
502
|
+
if (messageType === "file" || messageType === "audio" || messageType === "media" || messageType === "video") {
|
|
503
|
+
const fileKey = stringValue(parsed.file_key);
|
|
504
|
+
return fileKey
|
|
505
|
+
? [
|
|
506
|
+
{
|
|
507
|
+
resourceType: "file",
|
|
508
|
+
fileKey,
|
|
509
|
+
fileName: stringValue(parsed.file_name)
|
|
510
|
+
}
|
|
511
|
+
]
|
|
512
|
+
: [];
|
|
513
|
+
}
|
|
514
|
+
return [];
|
|
515
|
+
}
|
|
516
|
+
function parseContentObject(content) {
|
|
517
|
+
if (isRecord(content)) {
|
|
518
|
+
return content;
|
|
519
|
+
}
|
|
520
|
+
if (typeof content !== "string") {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const parsed = JSON.parse(content);
|
|
525
|
+
return isRecord(parsed) ? parsed : null;
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
export function normalizeTextContent(content) {
|
|
532
|
+
if (typeof content !== "string") {
|
|
533
|
+
if (isRecord(content) && typeof content.text === "string") {
|
|
534
|
+
return content.text;
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
const parsed = JSON.parse(content);
|
|
540
|
+
if (isRecord(parsed) && typeof parsed.text === "string") {
|
|
541
|
+
return parsed.text;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
catch {
|
|
545
|
+
// Some event sources emit already-decoded text; raw SDK events emit JSON strings.
|
|
546
|
+
}
|
|
547
|
+
return content;
|
|
548
|
+
}
|
|
549
|
+
function unwrapReceiveEvent(raw) {
|
|
550
|
+
if (isRecord(raw) && isRecord(raw.event)) {
|
|
551
|
+
return raw.event;
|
|
552
|
+
}
|
|
553
|
+
return raw;
|
|
554
|
+
}
|
|
555
|
+
function normalizeChatType(chatType) {
|
|
556
|
+
if (!chatType) {
|
|
557
|
+
return undefined;
|
|
558
|
+
}
|
|
559
|
+
switch (chatType.toLowerCase()) {
|
|
560
|
+
case "p2p":
|
|
561
|
+
case "p2p_chat":
|
|
562
|
+
case "private":
|
|
563
|
+
case "private_chat":
|
|
564
|
+
return "p2p";
|
|
565
|
+
case "group":
|
|
566
|
+
case "group_chat":
|
|
567
|
+
return "group";
|
|
568
|
+
case "topic_group":
|
|
569
|
+
case "topic":
|
|
570
|
+
return "topic_group";
|
|
571
|
+
default:
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function larkThreadIdForMessage(message, messageId, chatType) {
|
|
576
|
+
const explicitThreadId = firstStringValue(message.thread_id);
|
|
577
|
+
if (explicitThreadId) {
|
|
578
|
+
return explicitThreadId;
|
|
579
|
+
}
|
|
580
|
+
if (chatType !== "topic_group") {
|
|
581
|
+
return undefined;
|
|
582
|
+
}
|
|
583
|
+
return firstStringValue(message.root_id, message.parent_id, messageId) ?? messageId;
|
|
584
|
+
}
|
|
585
|
+
function normalizeMentions(value) {
|
|
586
|
+
if (!Array.isArray(value)) {
|
|
587
|
+
return undefined;
|
|
588
|
+
}
|
|
589
|
+
const mentions = [];
|
|
590
|
+
for (const item of value) {
|
|
591
|
+
if (!isRecord(item)) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
const key = stringValue(item.key);
|
|
595
|
+
const id = isRecord(item.id) ? item.id : {};
|
|
596
|
+
const openId = stringValue(id.open_id) ?? stringValue(item.open_id);
|
|
597
|
+
const userId = stringValue(id.user_id) ?? stringValue(item.user_id);
|
|
598
|
+
const unionId = stringValue(id.union_id) ?? stringValue(item.union_id);
|
|
599
|
+
const name = stringValue(item.name);
|
|
600
|
+
if (!key && !openId && !userId && !unionId) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
mentions.push({
|
|
604
|
+
key: key ?? "",
|
|
605
|
+
openId,
|
|
606
|
+
userId,
|
|
607
|
+
unionId,
|
|
608
|
+
name
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
return mentions.length > 0 ? mentions : undefined;
|
|
612
|
+
}
|
|
613
|
+
function parseEpochMs(value) {
|
|
614
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
615
|
+
return undefined;
|
|
616
|
+
}
|
|
617
|
+
const parsed = Number(value);
|
|
618
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
619
|
+
}
|
|
620
|
+
function ignored(reason, raw) {
|
|
621
|
+
return { kind: "ignored", reason, raw };
|
|
622
|
+
}
|
|
623
|
+
function ignoredMessageChange(reason, raw) {
|
|
624
|
+
return { kind: "ignored", reason, raw };
|
|
625
|
+
}
|
|
626
|
+
function ignoredBotMenu(reason, raw) {
|
|
627
|
+
return { kind: "ignored", reason, raw };
|
|
628
|
+
}
|
|
629
|
+
function botMenuActionForEventKey(eventKey) {
|
|
630
|
+
const normalized = eventKey.trim().toLowerCase();
|
|
631
|
+
switch (normalized) {
|
|
632
|
+
case "stop":
|
|
633
|
+
case "new":
|
|
634
|
+
case "queue":
|
|
635
|
+
case "status":
|
|
636
|
+
case "help":
|
|
637
|
+
return normalized;
|
|
638
|
+
case "new_session":
|
|
639
|
+
case "new-session":
|
|
640
|
+
case "new session":
|
|
641
|
+
case "new_conversation":
|
|
642
|
+
case "new-conversation":
|
|
643
|
+
return "new_session";
|
|
644
|
+
default:
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function eventPayload(raw) {
|
|
649
|
+
return isRecord(raw.event) ? raw.event : raw;
|
|
650
|
+
}
|
|
651
|
+
function eventHeader(raw) {
|
|
652
|
+
return isRecord(raw.header) ? raw.header : {};
|
|
653
|
+
}
|
|
654
|
+
function chatIdFromChatInfo(value) {
|
|
655
|
+
if (!isRecord(value)) {
|
|
656
|
+
return undefined;
|
|
657
|
+
}
|
|
658
|
+
return stringValue(value.chat_id);
|
|
659
|
+
}
|
|
660
|
+
function chatIdFromBotMenuEvent(event) {
|
|
661
|
+
return firstStringValue(event.chat_id, event.open_chat_id, isRecord(event.chat) ? event.chat.chat_id : undefined, isRecord(event.chat) ? event.chat.open_chat_id : undefined, chatIdFromChatInfo(event.chat_info), isRecord(event.operator) ? event.operator.chat_id : undefined, isRecord(event.operator) ? event.operator.open_chat_id : undefined);
|
|
662
|
+
}
|
|
663
|
+
function stringValue(value) {
|
|
664
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
665
|
+
}
|
|
666
|
+
function firstStringValue(...values) {
|
|
667
|
+
for (const value of values) {
|
|
668
|
+
const string = stringValue(value);
|
|
669
|
+
if (string) {
|
|
670
|
+
return string;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return undefined;
|
|
674
|
+
}
|
|
675
|
+
function isRecord(value) {
|
|
676
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
677
|
+
}
|
|
678
|
+
//# sourceMappingURL=filters.js.map
|