fix(feishu): support Lark private chats as direct messages (openclaw#31400) thanks @stakeswky

Verified:
- pnpm test -- extensions/feishu/src/bot.checkBotMentioned.test.ts
- pnpm build
- pnpm check (blocked by unrelated baseline lint errors in untouched files)
- pnpm test:macmini (not run after pnpm check blocked)

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:04:42 +08:00
committed by GitHub
parent 36c6b63ea6
commit 3043e68dfa
6 changed files with 10 additions and 7 deletions

View File

@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- 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.
- Gateway/OpenAI chat completions: honor `x-openclaw-message-channel` when building `agentCommand` input for `/v1/chat/completions`, preserving caller channel identity instead of forcing `webchat`. (#30462) Thanks @bmendonca3.

View File

@@ -3,7 +3,7 @@ import { parseFeishuMessageEvent } from "./bot.js";
// Helper to build a minimal FeishuMessageEvent for testing
function makeEvent(
chatType: "p2p" | "group",
chatType: "p2p" | "group" | "private",
mentions?: Array<{ key: string; name: string; id: { open_id?: string } }>,
text = "hello",
) {

View File

@@ -165,7 +165,7 @@ export type FeishuMessageEvent = {
root_id?: string;
parent_id?: string;
chat_id: string;
chat_type: "p2p" | "group";
chat_type: "p2p" | "group" | "private";
message_type: string;
content: string;
create_time?: string;
@@ -709,6 +709,7 @@ export async function handleFeishuMessage(params: {
let ctx = parseFeishuMessageEvent(event, botOpenId);
const isGroup = ctx.chatType === "group";
const isDirect = !isGroup;
const senderUserId = event.sender.sender_id.user_id?.trim() || undefined;
// Handle merge_forward messages: fetch full message via API then expand sub-messages
@@ -895,7 +896,7 @@ export async function handleFeishuMessage(params: {
senderName: ctx.senderName,
}).allowed;
if (!isGroup && dmPolicy !== "open" && !dmAllowed) {
if (isDirect && dmPolicy !== "open" && !dmAllowed) {
if (dmPolicy === "pairing") {
const { code, created } = await pairing.upsertPairingRequest({
id: ctx.senderOpenId,

View File

@@ -53,7 +53,7 @@ export function isMentionForwardRequest(event: FeishuMessageEvent, botOpenId?: s
return false;
}
const isDirectMessage = event.message.chat_type === "p2p";
const isDirectMessage = event.message.chat_type !== "group";
const hasOtherMention = mentions.some((m) => m.id.open_id !== botOpenId);
if (isDirectMessage) {

View File

@@ -17,7 +17,7 @@ const FEISHU_REACTION_VERIFY_TIMEOUT_MS = 1_500;
export type FeishuReactionCreatedEvent = {
message_id: string;
chat_id?: string;
chat_type?: "p2p" | "group";
chat_type?: "p2p" | "group" | "private";
reaction_type?: { emoji_type?: string };
operator_type?: string;
user_id?: { open_id?: string };
@@ -93,7 +93,8 @@ export async function resolveReactionSyntheticEvent(
const syntheticChatIdRaw = event.chat_id ?? reactedMsg.chatId;
const syntheticChatId = syntheticChatIdRaw?.trim() ? syntheticChatIdRaw : `p2p:${senderId}`;
const syntheticChatType: "p2p" | "group" = event.chat_type ?? "p2p";
const syntheticChatType: "p2p" | "group" | "private" =
event.chat_type === "group" ? "group" : "p2p";
return {
sender: {
sender_id: { open_id: senderId },

View File

@@ -36,7 +36,7 @@ export type FeishuMessageContext = {
senderId: string;
senderOpenId: string;
senderName?: string;
chatType: "p2p" | "group";
chatType: "p2p" | "group" | "private";
mentionedBot: boolean;
rootId?: string;
parentId?: string;