fix(feishu): restore private chat pairing replies in Lark/Feishu (openclaw#31403) thanks @stakeswky

Verified:
- pnpm test -- extensions/feishu/src/bot.test.ts
- pnpm build

Co-authored-by: stakeswky <64798754+stakeswky@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
不做了睡大觉
2026-03-03 07:27:39 +08:00
committed by GitHub
parent e2483a5381
commit 66397c2855
3 changed files with 38 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Feishu/DM pairing reply target: send pairing challenge replies to `chat:<chat_id>` instead of `user:<sender_open_id>` so Lark/Feishu private chats with user-id-only sender payloads receive pairing messages reliably. (#31403) Thanks @stakeswky.
- Feishu/Lark private DM routing: treat inbound `chat_type: "private"` as direct-message context for pairing/mention-forward/reaction synthetic handling so Lark private chats behave like Feishu p2p DMs. (#31400) Thanks @stakeswky.
- Sandbox/workspace mount permissions: make primary `/workspace` bind mounts read-only whenever `workspaceAccess` is not `rw` (including `none`) across both core sandbox container and sandbox browser create flows. (#32227) Thanks @guanyu-zhang.
- Signal/message actions: allow `react` to fall back to `toolContext.currentMessageId` when `messageId` is omitted, matching Telegram behavior and unblocking agent-initiated reactions on inbound turns. (#32217) Thanks @dunamismax.

View File

@@ -366,6 +366,41 @@ describe("handleFeishuMessage command authorization", () => {
);
});
it("replies pairing challenge to DM chat_id instead of user:sender id", async () => {
const cfg: ClawdbotConfig = {
channels: {
feishu: {
dmPolicy: "pairing",
},
},
} as ClawdbotConfig;
const event: FeishuMessageEvent = {
sender: {
sender_id: {
user_id: "u_mobile_only",
},
},
message: {
message_id: "msg-pairing-chat-reply",
chat_id: "oc_dm_chat_1",
chat_type: "p2p",
message_type: "text",
content: JSON.stringify({ text: "hello" }),
},
};
mockReadAllowFromStore.mockResolvedValue([]);
mockUpsertPairingRequest.mockResolvedValue({ code: "ABCDEFGH", created: true });
await dispatchMessage({ cfg, event });
expect(mockSendMessageFeishu).toHaveBeenCalledWith(
expect.objectContaining({
to: "chat:oc_dm_chat_1",
}),
);
});
it("creates pairing request and drops unauthorized DMs in pairing mode", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);
mockReadAllowFromStore.mockResolvedValue([]);
@@ -410,7 +445,7 @@ describe("handleFeishuMessage command authorization", () => {
});
expect(mockSendMessageFeishu).toHaveBeenCalledWith(
expect.objectContaining({
to: "user:ou-unapproved",
to: "chat:oc-dm",
accountId: "default",
}),
);

View File

@@ -907,7 +907,7 @@ export async function handleFeishuMessage(params: {
try {
await sendMessageFeishu({
cfg,
to: `user:${ctx.senderOpenId}`,
to: `chat:${ctx.chatId}`,
text: core.channel.pairing.buildPairingReply({
channel: "feishu",
idLine: `Your Feishu user id: ${ctx.senderOpenId}`,