@openclaw/feishu 2026.5.2 → 2026.5.3-beta.2
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/accounts-Ba3-WP1z.js +423 -0
- package/dist/api.js +2280 -0
- package/dist/app-registration-B8qc1MCM.js +184 -0
- package/dist/audio-preflight.runtime-BPlzkO3l.js +7 -0
- package/dist/card-interaction-BfRLgvw_.js +96 -0
- package/dist/channel-CSD_Jt8I.js +1668 -0
- package/dist/channel-entry.js +22 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-DYsXcD36.js +700 -0
- package/dist/client-DBVoQL5w.js +157 -0
- package/dist/contract-api.js +9 -0
- package/dist/conversation-id-DWS3Ep2A.js +139 -0
- package/dist/directory.static-f3EeoRJd.js +44 -0
- package/dist/drive-C5eJLJr7.js +883 -0
- package/dist/index.js +68 -0
- package/dist/monitor-CT189QfR.js +60 -0
- package/dist/monitor.account-dJV2jO8C.js +4990 -0
- package/dist/monitor.state-DYM02ipp.js +100 -0
- package/dist/policy-D6c-wMPl.js +118 -0
- package/dist/probe-BNzzU_uR.js +149 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/runtime-CG0DuRCy.js +8 -0
- package/dist/runtime-api.js +14 -0
- package/dist/secret-contract-Dm4Z_zQN.js +119 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/security-audit-DqJdocrN.js +11 -0
- package/dist/security-audit-shared-ByuMx9cJ.js +38 -0
- package/dist/security-contract-api.js +2 -0
- package/dist/send-DowxxbpH.js +1218 -0
- package/dist/session-conversation-B4nrW-vo.js +27 -0
- package/dist/session-key-api.js +2 -0
- package/dist/setup-api.js +2 -0
- package/dist/setup-entry.js +15 -0
- package/dist/subagent-hooks-C3UhPVLV.js +227 -0
- package/dist/subagent-hooks-api.js +23 -0
- package/dist/targets-JMFJRKSe.js +48 -0
- package/dist/thread-bindings-BmS6TLes.js +222 -0
- package/package.json +15 -6
- package/api.ts +0 -31
- package/channel-entry.ts +0 -20
- package/channel-plugin-api.ts +0 -1
- package/contract-api.ts +0 -16
- package/index.ts +0 -82
- package/runtime-api.ts +0 -55
- package/secret-contract-api.ts +0 -5
- package/security-contract-api.ts +0 -1
- package/session-key-api.ts +0 -1
- package/setup-api.ts +0 -3
- package/setup-entry.test.ts +0 -14
- package/setup-entry.ts +0 -13
- package/src/accounts.test.ts +0 -459
- package/src/accounts.ts +0 -326
- package/src/app-registration.ts +0 -331
- package/src/approval-auth.test.ts +0 -24
- package/src/approval-auth.ts +0 -25
- package/src/async.test.ts +0 -35
- package/src/async.ts +0 -104
- package/src/audio-preflight.runtime.ts +0 -9
- package/src/bitable.test.ts +0 -131
- package/src/bitable.ts +0 -762
- package/src/bot-content.ts +0 -474
- package/src/bot-group-name.test.ts +0 -108
- package/src/bot-runtime-api.ts +0 -12
- package/src/bot-sender-name.ts +0 -125
- package/src/bot.broadcast.test.ts +0 -463
- package/src/bot.card-action.test.ts +0 -577
- package/src/bot.checkBotMentioned.test.ts +0 -265
- package/src/bot.helpers.test.ts +0 -118
- package/src/bot.stripBotMention.test.ts +0 -126
- package/src/bot.test.ts +0 -3040
- package/src/bot.ts +0 -1559
- package/src/card-action.ts +0 -447
- package/src/card-interaction.test.ts +0 -129
- package/src/card-interaction.ts +0 -159
- package/src/card-test-helpers.ts +0 -47
- package/src/card-ux-approval.ts +0 -65
- package/src/card-ux-launcher.test.ts +0 -99
- package/src/card-ux-launcher.ts +0 -121
- package/src/card-ux-shared.ts +0 -33
- package/src/channel-runtime-api.ts +0 -16
- package/src/channel.runtime.ts +0 -47
- package/src/channel.test.ts +0 -959
- package/src/channel.ts +0 -1313
- package/src/chat-schema.ts +0 -25
- package/src/chat.test.ts +0 -196
- package/src/chat.ts +0 -188
- package/src/client.test.ts +0 -433
- package/src/client.ts +0 -290
- package/src/comment-dispatcher-runtime-api.ts +0 -6
- package/src/comment-dispatcher.test.ts +0 -169
- package/src/comment-dispatcher.ts +0 -107
- package/src/comment-handler-runtime-api.ts +0 -3
- package/src/comment-handler.test.ts +0 -486
- package/src/comment-handler.ts +0 -309
- package/src/comment-reaction.test.ts +0 -166
- package/src/comment-reaction.ts +0 -259
- package/src/comment-shared.test.ts +0 -182
- package/src/comment-shared.ts +0 -406
- package/src/comment-target.ts +0 -44
- package/src/config-schema.test.ts +0 -309
- package/src/config-schema.ts +0 -333
- package/src/conversation-id.test.ts +0 -18
- package/src/conversation-id.ts +0 -199
- package/src/dedup-runtime-api.ts +0 -1
- package/src/dedup.ts +0 -141
- package/src/directory.static.ts +0 -61
- package/src/directory.test.ts +0 -136
- package/src/directory.ts +0 -124
- package/src/doc-schema.ts +0 -182
- package/src/docx-batch-insert.test.ts +0 -91
- package/src/docx-batch-insert.ts +0 -223
- package/src/docx-color-text.ts +0 -154
- package/src/docx-table-ops.test.ts +0 -53
- package/src/docx-table-ops.ts +0 -316
- package/src/docx-types.ts +0 -38
- package/src/docx.account-selection.test.ts +0 -79
- package/src/docx.test.ts +0 -685
- package/src/docx.ts +0 -1616
- package/src/drive-schema.ts +0 -92
- package/src/drive.test.ts +0 -1219
- package/src/drive.ts +0 -829
- package/src/dynamic-agent.ts +0 -137
- package/src/event-types.ts +0 -45
- package/src/external-keys.test.ts +0 -20
- package/src/external-keys.ts +0 -19
- package/src/lifecycle.test-support.ts +0 -220
- package/src/media.test.ts +0 -900
- package/src/media.ts +0 -861
- package/src/mention-target.types.ts +0 -5
- package/src/mention.ts +0 -114
- package/src/message-action-contract.ts +0 -13
- package/src/monitor-state-runtime-api.ts +0 -7
- package/src/monitor-transport-runtime-api.ts +0 -7
- package/src/monitor.account.ts +0 -468
- package/src/monitor.acp-init-failure.lifecycle.test-support.ts +0 -219
- package/src/monitor.bot-identity.ts +0 -86
- package/src/monitor.bot-menu-handler.ts +0 -165
- package/src/monitor.bot-menu.lifecycle.test-support.ts +0 -224
- package/src/monitor.bot-menu.test.ts +0 -178
- package/src/monitor.broadcast.reply-once.lifecycle.test-support.ts +0 -264
- package/src/monitor.card-action.lifecycle.test-support.ts +0 -373
- package/src/monitor.cleanup.test.ts +0 -376
- package/src/monitor.comment-notice-handler.ts +0 -105
- package/src/monitor.comment.test.ts +0 -937
- package/src/monitor.comment.ts +0 -1386
- package/src/monitor.lifecycle.test.ts +0 -4
- package/src/monitor.message-handler.ts +0 -339
- package/src/monitor.reaction.lifecycle.test-support.ts +0 -68
- package/src/monitor.reaction.test.ts +0 -713
- package/src/monitor.startup.test.ts +0 -192
- package/src/monitor.startup.ts +0 -74
- package/src/monitor.state.defaults.test.ts +0 -46
- package/src/monitor.state.ts +0 -170
- package/src/monitor.synthetic-error.ts +0 -18
- package/src/monitor.test-mocks.ts +0 -45
- package/src/monitor.transport.ts +0 -424
- package/src/monitor.ts +0 -100
- package/src/monitor.webhook-e2e.test.ts +0 -272
- package/src/monitor.webhook-security.test.ts +0 -264
- package/src/monitor.webhook.test-helpers.ts +0 -116
- package/src/outbound-runtime-api.ts +0 -1
- package/src/outbound.test.ts +0 -935
- package/src/outbound.ts +0 -718
- package/src/perm-schema.ts +0 -52
- package/src/perm.ts +0 -170
- package/src/pins.ts +0 -108
- package/src/policy.test.ts +0 -334
- package/src/policy.ts +0 -236
- package/src/post.test.ts +0 -105
- package/src/post.ts +0 -275
- package/src/probe.test.ts +0 -275
- package/src/probe.ts +0 -166
- package/src/processing-claims.ts +0 -59
- package/src/qr-terminal.ts +0 -1
- package/src/reactions.ts +0 -123
- package/src/reasoning-preview.test.ts +0 -59
- package/src/reasoning-preview.ts +0 -20
- package/src/reply-dispatcher-runtime-api.ts +0 -7
- package/src/reply-dispatcher.test.ts +0 -1144
- package/src/reply-dispatcher.ts +0 -650
- package/src/runtime.ts +0 -9
- package/src/secret-contract.ts +0 -145
- package/src/secret-input.ts +0 -1
- package/src/security-audit-shared.ts +0 -69
- package/src/security-audit.test.ts +0 -61
- package/src/security-audit.ts +0 -1
- package/src/send-result.ts +0 -29
- package/src/send-target.test.ts +0 -80
- package/src/send-target.ts +0 -35
- package/src/send.reply-fallback.test.ts +0 -292
- package/src/send.test.ts +0 -550
- package/src/send.ts +0 -800
- package/src/sequential-key.test.ts +0 -72
- package/src/sequential-key.ts +0 -28
- package/src/sequential-queue.test.ts +0 -92
- package/src/sequential-queue.ts +0 -16
- package/src/session-conversation.ts +0 -42
- package/src/session-route.ts +0 -48
- package/src/setup-core.ts +0 -51
- package/src/setup-surface.test.ts +0 -174
- package/src/setup-surface.ts +0 -581
- package/src/streaming-card.test.ts +0 -190
- package/src/streaming-card.ts +0 -490
- package/src/subagent-hooks.test.ts +0 -603
- package/src/subagent-hooks.ts +0 -397
- package/src/targets.ts +0 -97
- package/src/test-support/lifecycle-test-support.ts +0 -453
- package/src/thread-bindings.test.ts +0 -143
- package/src/thread-bindings.ts +0 -330
- package/src/tool-account-routing.test.ts +0 -187
- package/src/tool-account.test.ts +0 -44
- package/src/tool-account.ts +0 -93
- package/src/tool-factory-test-harness.ts +0 -79
- package/src/tool-result.test.ts +0 -32
- package/src/tool-result.ts +0 -16
- package/src/tools-config.test.ts +0 -21
- package/src/tools-config.ts +0 -22
- package/src/types.ts +0 -104
- package/src/typing.test.ts +0 -144
- package/src/typing.ts +0 -214
- package/src/wiki-schema.ts +0 -55
- package/src/wiki.ts +0 -227
- package/subagent-hooks-api.ts +0 -31
- package/tsconfig.json +0 -16
|
@@ -1,339 +0,0 @@
|
|
|
1
|
-
import type { ClawdbotConfig, HistoryEntry, PluginRuntime, RuntimeEnv } from "../runtime-api.js";
|
|
2
|
-
import type { FeishuMessageEvent } from "./event-types.js";
|
|
3
|
-
import { isMentionForwardRequest } from "./mention.js";
|
|
4
|
-
import {
|
|
5
|
-
releaseFeishuMessageProcessing,
|
|
6
|
-
tryBeginFeishuMessageProcessing,
|
|
7
|
-
} from "./processing-claims.js";
|
|
8
|
-
import { createSequentialQueue } from "./sequential-queue.js";
|
|
9
|
-
import type { FeishuChatType } from "./types.js";
|
|
10
|
-
|
|
11
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
12
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function readString(value: unknown): string | undefined {
|
|
16
|
-
return typeof value === "string" ? value : undefined;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type FeishuMessageReceiveHandlerContext = {
|
|
20
|
-
cfg: ClawdbotConfig;
|
|
21
|
-
core: PluginRuntime;
|
|
22
|
-
accountId: string;
|
|
23
|
-
runtime?: RuntimeEnv;
|
|
24
|
-
chatHistories: Map<string, HistoryEntry[]>;
|
|
25
|
-
fireAndForget?: boolean;
|
|
26
|
-
handleMessage: (params: {
|
|
27
|
-
cfg: ClawdbotConfig;
|
|
28
|
-
event: FeishuMessageEvent;
|
|
29
|
-
botOpenId?: string;
|
|
30
|
-
botName?: string;
|
|
31
|
-
runtime?: RuntimeEnv;
|
|
32
|
-
chatHistories?: Map<string, HistoryEntry[]>;
|
|
33
|
-
accountId?: string;
|
|
34
|
-
processingClaimHeld?: boolean;
|
|
35
|
-
}) => Promise<void>;
|
|
36
|
-
resolveDebounceText: (params: {
|
|
37
|
-
event: FeishuMessageEvent;
|
|
38
|
-
botOpenId?: string;
|
|
39
|
-
botName?: string;
|
|
40
|
-
}) => string;
|
|
41
|
-
hasProcessedMessage: (
|
|
42
|
-
messageId: string | undefined | null,
|
|
43
|
-
namespace: string,
|
|
44
|
-
log?: (...args: unknown[]) => void,
|
|
45
|
-
) => Promise<boolean>;
|
|
46
|
-
recordProcessedMessage: (
|
|
47
|
-
messageId: string | undefined | null,
|
|
48
|
-
namespace: string,
|
|
49
|
-
log?: (...args: unknown[]) => void,
|
|
50
|
-
) => Promise<boolean>;
|
|
51
|
-
getBotOpenId?: (accountId: string) => string | undefined;
|
|
52
|
-
getBotName?: (accountId: string) => string | undefined;
|
|
53
|
-
resolveSequentialKey?: (params: {
|
|
54
|
-
accountId: string;
|
|
55
|
-
event: FeishuMessageEvent;
|
|
56
|
-
botOpenId?: string;
|
|
57
|
-
botName?: string;
|
|
58
|
-
}) => string;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function normalizeFeishuChatType(value: unknown): FeishuChatType | undefined {
|
|
62
|
-
return value === "group" || value === "topic_group" || value === "private" || value === "p2p"
|
|
63
|
-
? value
|
|
64
|
-
: undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function parseFeishuMessageEventPayload(value: unknown): FeishuMessageEvent | null {
|
|
68
|
-
if (!isRecord(value)) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
const sender = value.sender;
|
|
72
|
-
const message = value.message;
|
|
73
|
-
if (!isRecord(sender) || !isRecord(message)) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
const senderId = sender.sender_id;
|
|
77
|
-
if (!isRecord(senderId)) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
const messageId = readString(message.message_id);
|
|
81
|
-
const chatId = readString(message.chat_id);
|
|
82
|
-
const chatType = normalizeFeishuChatType(message.chat_type);
|
|
83
|
-
const messageType = readString(message.message_type);
|
|
84
|
-
const content = readString(message.content);
|
|
85
|
-
if (!messageId || !chatId || !chatType || !messageType || !content) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
return value as FeishuMessageEvent;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function mergeFeishuDebounceMentions(
|
|
92
|
-
entries: FeishuMessageEvent[],
|
|
93
|
-
): FeishuMessageEvent["message"]["mentions"] | undefined {
|
|
94
|
-
const merged = new Map<string, NonNullable<FeishuMessageEvent["message"]["mentions"]>[number]>();
|
|
95
|
-
for (const entry of entries) {
|
|
96
|
-
for (const mention of entry.message.mentions ?? []) {
|
|
97
|
-
const stableId =
|
|
98
|
-
mention.id.open_id?.trim() || mention.id.user_id?.trim() || mention.id.union_id?.trim();
|
|
99
|
-
const mentionName = mention.name?.trim();
|
|
100
|
-
const mentionKey = mention.key?.trim();
|
|
101
|
-
const fallback =
|
|
102
|
-
mentionName && mentionKey ? `${mentionName}|${mentionKey}` : mentionName || mentionKey;
|
|
103
|
-
const key = stableId || fallback;
|
|
104
|
-
if (!key || merged.has(key)) {
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
merged.set(key, mention);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return merged.size > 0 ? Array.from(merged.values()) : undefined;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function dedupeFeishuDebounceEntriesByMessageId(
|
|
114
|
-
entries: FeishuMessageEvent[],
|
|
115
|
-
): FeishuMessageEvent[] {
|
|
116
|
-
const seen = new Set<string>();
|
|
117
|
-
const deduped: FeishuMessageEvent[] = [];
|
|
118
|
-
for (const entry of entries) {
|
|
119
|
-
const messageId = entry.message.message_id?.trim();
|
|
120
|
-
if (!messageId) {
|
|
121
|
-
deduped.push(entry);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (seen.has(messageId)) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
seen.add(messageId);
|
|
128
|
-
deduped.push(entry);
|
|
129
|
-
}
|
|
130
|
-
return deduped;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function resolveFeishuDebounceMentions(params: {
|
|
134
|
-
entries: FeishuMessageEvent[];
|
|
135
|
-
botOpenId?: string;
|
|
136
|
-
}): FeishuMessageEvent["message"]["mentions"] | undefined {
|
|
137
|
-
const { entries, botOpenId } = params;
|
|
138
|
-
if (entries.length === 0) {
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
142
|
-
const entry = entries[index];
|
|
143
|
-
if (isMentionForwardRequest(entry, botOpenId)) {
|
|
144
|
-
return mergeFeishuDebounceMentions([entry]);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const merged = mergeFeishuDebounceMentions(entries);
|
|
148
|
-
if (!merged) {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
const normalizedBotOpenId = botOpenId?.trim();
|
|
152
|
-
if (!normalizedBotOpenId) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
const botMentions = merged.filter(
|
|
156
|
-
(mention) => mention.id.open_id?.trim() === normalizedBotOpenId,
|
|
157
|
-
);
|
|
158
|
-
return botMentions.length > 0 ? botMentions : undefined;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function createFeishuMessageReceiveHandler({
|
|
162
|
-
cfg,
|
|
163
|
-
core,
|
|
164
|
-
accountId,
|
|
165
|
-
runtime,
|
|
166
|
-
chatHistories,
|
|
167
|
-
fireAndForget,
|
|
168
|
-
handleMessage,
|
|
169
|
-
resolveDebounceText: resolveText,
|
|
170
|
-
hasProcessedMessage,
|
|
171
|
-
recordProcessedMessage,
|
|
172
|
-
getBotOpenId = () => undefined,
|
|
173
|
-
getBotName = () => undefined,
|
|
174
|
-
resolveSequentialKey = ({ accountId, event }) =>
|
|
175
|
-
`feishu:${accountId}:${event.message.chat_id?.trim() || "unknown"}`,
|
|
176
|
-
}: FeishuMessageReceiveHandlerContext): (data: unknown) => Promise<void> {
|
|
177
|
-
const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
|
|
178
|
-
cfg,
|
|
179
|
-
channel: "feishu",
|
|
180
|
-
});
|
|
181
|
-
const log = runtime?.log ?? console.log;
|
|
182
|
-
const error = runtime?.error ?? console.error;
|
|
183
|
-
const enqueue = createSequentialQueue();
|
|
184
|
-
|
|
185
|
-
const dispatchFeishuMessage = async (event: FeishuMessageEvent) => {
|
|
186
|
-
const sequentialKey = resolveSequentialKey({
|
|
187
|
-
accountId,
|
|
188
|
-
event,
|
|
189
|
-
botOpenId: getBotOpenId(accountId),
|
|
190
|
-
botName: getBotName(accountId),
|
|
191
|
-
});
|
|
192
|
-
const task = () =>
|
|
193
|
-
handleMessage({
|
|
194
|
-
cfg,
|
|
195
|
-
event,
|
|
196
|
-
botOpenId: getBotOpenId(accountId),
|
|
197
|
-
botName: getBotName(accountId),
|
|
198
|
-
runtime,
|
|
199
|
-
chatHistories,
|
|
200
|
-
accountId,
|
|
201
|
-
processingClaimHeld: true,
|
|
202
|
-
});
|
|
203
|
-
await enqueue(sequentialKey, task);
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
const resolveSenderDebounceId = (event: FeishuMessageEvent): string | undefined => {
|
|
207
|
-
const senderId =
|
|
208
|
-
event.sender.sender_id.open_id?.trim() || event.sender.sender_id.user_id?.trim();
|
|
209
|
-
return senderId || undefined;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const resolveDebounceText = (event: FeishuMessageEvent): string => {
|
|
213
|
-
return resolveText({
|
|
214
|
-
event,
|
|
215
|
-
botOpenId: getBotOpenId(accountId),
|
|
216
|
-
botName: getBotName(accountId),
|
|
217
|
-
}).trim();
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
const recordSuppressedMessageIds = async (
|
|
221
|
-
entries: FeishuMessageEvent[],
|
|
222
|
-
dispatchMessageId?: string,
|
|
223
|
-
) => {
|
|
224
|
-
const keepMessageId = dispatchMessageId?.trim();
|
|
225
|
-
const suppressedIds = new Set(
|
|
226
|
-
entries
|
|
227
|
-
.map((entry) => entry.message.message_id?.trim())
|
|
228
|
-
.filter((id): id is string => Boolean(id) && (!keepMessageId || id !== keepMessageId)),
|
|
229
|
-
);
|
|
230
|
-
for (const messageId of suppressedIds) {
|
|
231
|
-
try {
|
|
232
|
-
await recordProcessedMessage(messageId, accountId, log);
|
|
233
|
-
} catch (err) {
|
|
234
|
-
error(
|
|
235
|
-
`feishu[${accountId}]: failed to record merged dedupe id ${messageId}: ${String(err)}`,
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
const inboundDebouncer = core.channel.debounce.createInboundDebouncer<FeishuMessageEvent>({
|
|
242
|
-
debounceMs: inboundDebounceMs,
|
|
243
|
-
buildKey: (event) => {
|
|
244
|
-
const chatId = event.message.chat_id?.trim();
|
|
245
|
-
const senderId = resolveSenderDebounceId(event);
|
|
246
|
-
if (!chatId || !senderId) {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
const rootId = event.message.root_id?.trim();
|
|
250
|
-
const threadKey = rootId ? `thread:${rootId}` : "chat";
|
|
251
|
-
return `feishu:${accountId}:${chatId}:${threadKey}:${senderId}`;
|
|
252
|
-
},
|
|
253
|
-
shouldDebounce: (event) => {
|
|
254
|
-
if (event.message.message_type !== "text") {
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
const text = resolveDebounceText(event);
|
|
258
|
-
return Boolean(text) && !core.channel.text.hasControlCommand(text, cfg);
|
|
259
|
-
},
|
|
260
|
-
onFlush: async (entries) => {
|
|
261
|
-
const last = entries.at(-1);
|
|
262
|
-
if (!last) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (entries.length === 1) {
|
|
266
|
-
await dispatchFeishuMessage(last);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
const dedupedEntries = dedupeFeishuDebounceEntriesByMessageId(entries);
|
|
270
|
-
const freshEntries: FeishuMessageEvent[] = [];
|
|
271
|
-
for (const entry of dedupedEntries) {
|
|
272
|
-
if (!(await hasProcessedMessage(entry.message.message_id, accountId, log))) {
|
|
273
|
-
freshEntries.push(entry);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
const dispatchEntry = freshEntries.at(-1);
|
|
277
|
-
if (!dispatchEntry) {
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
await recordSuppressedMessageIds(dedupedEntries, dispatchEntry.message.message_id);
|
|
281
|
-
const combinedText = freshEntries
|
|
282
|
-
.map((entry) => resolveDebounceText(entry))
|
|
283
|
-
.filter(Boolean)
|
|
284
|
-
.join("\n");
|
|
285
|
-
const mergedMentions = resolveFeishuDebounceMentions({
|
|
286
|
-
entries: freshEntries,
|
|
287
|
-
botOpenId: getBotOpenId(accountId),
|
|
288
|
-
});
|
|
289
|
-
await dispatchFeishuMessage({
|
|
290
|
-
...dispatchEntry,
|
|
291
|
-
message: {
|
|
292
|
-
...dispatchEntry.message,
|
|
293
|
-
...(combinedText.trim()
|
|
294
|
-
? {
|
|
295
|
-
message_type: "text",
|
|
296
|
-
content: JSON.stringify({ text: combinedText }),
|
|
297
|
-
}
|
|
298
|
-
: {}),
|
|
299
|
-
mentions: mergedMentions ?? dispatchEntry.message.mentions,
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
},
|
|
303
|
-
onError: (err, entries) => {
|
|
304
|
-
for (const entry of entries) {
|
|
305
|
-
releaseFeishuMessageProcessing(entry.message.message_id, accountId);
|
|
306
|
-
}
|
|
307
|
-
error(`feishu[${accountId}]: inbound debounce flush failed: ${String(err)}`);
|
|
308
|
-
},
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
return async (data) => {
|
|
312
|
-
const event = parseFeishuMessageEventPayload(data);
|
|
313
|
-
if (!event) {
|
|
314
|
-
error(`feishu[${accountId}]: ignoring malformed message event payload`);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const messageId = event.message?.message_id?.trim();
|
|
318
|
-
if (!tryBeginFeishuMessageProcessing(messageId, accountId)) {
|
|
319
|
-
log(`feishu[${accountId}]: dropping duplicate event for message ${messageId}`);
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
const processMessage = async () => {
|
|
323
|
-
await inboundDebouncer.enqueue(event);
|
|
324
|
-
};
|
|
325
|
-
if (fireAndForget) {
|
|
326
|
-
void processMessage().catch((err) => {
|
|
327
|
-
releaseFeishuMessageProcessing(messageId, accountId);
|
|
328
|
-
error(`feishu[${accountId}]: error handling message: ${String(err)}`);
|
|
329
|
-
});
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
try {
|
|
333
|
-
await processMessage();
|
|
334
|
-
} catch (err) {
|
|
335
|
-
releaseFeishuMessageProcessing(messageId, accountId);
|
|
336
|
-
error(`feishu[${accountId}]: error handling message: ${String(err)}`);
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import type { ClawdbotConfig } from "../runtime-api.js";
|
|
3
|
-
import {
|
|
4
|
-
resolveReactionSyntheticEvent,
|
|
5
|
-
type FeishuReactionCreatedEvent,
|
|
6
|
-
} from "./monitor.account.js";
|
|
7
|
-
|
|
8
|
-
const cfg = {} as ClawdbotConfig;
|
|
9
|
-
|
|
10
|
-
function makeReactionEvent(
|
|
11
|
-
overrides: Partial<FeishuReactionCreatedEvent> = {},
|
|
12
|
-
): FeishuReactionCreatedEvent {
|
|
13
|
-
return {
|
|
14
|
-
message_id: "om_msg1",
|
|
15
|
-
reaction_type: { emoji_type: "THUMBSUP" },
|
|
16
|
-
operator_type: "user",
|
|
17
|
-
user_id: { open_id: "ou_user1" },
|
|
18
|
-
...overrides,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe("Feishu reaction lifecycle", () => {
|
|
23
|
-
it("builds a created synthetic interaction payload", async () => {
|
|
24
|
-
const result = await resolveReactionSyntheticEvent({
|
|
25
|
-
cfg,
|
|
26
|
-
accountId: "default",
|
|
27
|
-
event: makeReactionEvent({ user_id: { open_id: "ou_user1", user_id: "on_user1" } }),
|
|
28
|
-
botOpenId: "ou_bot",
|
|
29
|
-
fetchMessage: async () => ({
|
|
30
|
-
messageId: "om_msg1",
|
|
31
|
-
chatId: "oc_group_1",
|
|
32
|
-
chatType: "group",
|
|
33
|
-
senderOpenId: "ou_bot",
|
|
34
|
-
senderType: "app",
|
|
35
|
-
content: "hello",
|
|
36
|
-
contentType: "text",
|
|
37
|
-
}),
|
|
38
|
-
uuid: () => "fixed-uuid",
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
expect(result?.sender.sender_id).toEqual({ open_id: "ou_user1", user_id: "on_user1" });
|
|
42
|
-
expect(result?.message.content).toBe('{"text":"[reacted with THUMBSUP to message om_msg1]"}');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("builds a deleted synthetic interaction payload", async () => {
|
|
46
|
-
const result = await resolveReactionSyntheticEvent({
|
|
47
|
-
cfg,
|
|
48
|
-
accountId: "default",
|
|
49
|
-
event: makeReactionEvent(),
|
|
50
|
-
botOpenId: "ou_bot",
|
|
51
|
-
fetchMessage: async () => ({
|
|
52
|
-
messageId: "om_msg1",
|
|
53
|
-
chatId: "oc_group_1",
|
|
54
|
-
chatType: "group",
|
|
55
|
-
senderOpenId: "ou_bot",
|
|
56
|
-
senderType: "app",
|
|
57
|
-
content: "hello",
|
|
58
|
-
contentType: "text",
|
|
59
|
-
}),
|
|
60
|
-
uuid: () => "fixed-uuid",
|
|
61
|
-
action: "deleted",
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(result?.message.content).toBe(
|
|
65
|
-
'{"text":"[removed reaction THUMBSUP from message om_msg1]"}',
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
});
|