fix(feishu): replace console.log with runtime log for typing indicator errors (openclaw#18841) thanks @Clawborn

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: Clawborn <135319479+Clawborn@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Clawborn
2026-02-28 12:57:16 +08:00
committed by GitHub
parent a5b1e86535
commit 10f1be1072
4 changed files with 44 additions and 19 deletions

View File

@@ -24,6 +24,7 @@ Docs: https://docs.openclaw.ai
- Feishu/Group session routing: add configurable group session scopes (`group`, `group_sender`, `group_topic`, `group_topic_sender`) with legacy `topicSessionMode=enabled` compatibility so Feishu group conversations can isolate sessions by sender/topic as configured. (#17798)
- Feishu/Reply-in-thread routing: add `replyInThread` config (`disabled|enabled`) for group replies, propagate `reply_in_thread` across text/card/media/streaming sends, and align topic-scoped session routing so newly created reply threads stay on the same session root. (#27325)
- Feishu/Typing backoff: re-throw Feishu typing add/remove rate-limit and quota errors (`429`, `99991400`, `99991403`) and detect SDK non-throwing backoff responses so the typing keepalive circuit breaker can stop retries instead of looping indefinitely. (#28494)
- Feishu/Zalo runtime logging: replace direct `console.log/error` usage in Feishu typing-indicator paths and Zalo monitor paths with runtime-gated logger calls so verbosity controls are respected while preserving typing backoff behavior. (#18841) Thanks @Clawborn.
- Feishu/Probe status caching: cache successful `probeFeishu()` bot-info results for 10 minutes (bounded cache with per-account keying) to reduce repeated status/onboarding probe API calls, while bypassing cache for failures and exceptions. (#28907) Thanks @Glucksberg.
- Feishu/Opus media send type: send `.opus` attachments with `msg_type: "audio"` (instead of `"media"`) so Feishu voice messages deliver correctly while `.mp4` remains `msg_type: "media"` and documents remain `msg_type: "file"`. (#28269) Thanks @Glucksberg.
- Feishu/Mobile video media type: treat inbound `message_type: "media"` as video-equivalent for media key extraction, placeholder inference, and media download resolution so mobile-app video sends ingest correctly. (#25502) Thanks @4ier.

View File

@@ -59,13 +59,18 @@ export function createFeishuReplyDispatcher(params: CreateFeishuReplyDispatcherP
if (!replyToMessageId) {
return;
}
typingState = await addTypingIndicator({ cfg, messageId: replyToMessageId, accountId });
typingState = await addTypingIndicator({
cfg,
messageId: replyToMessageId,
accountId,
runtime: params.runtime,
});
},
stop: async () => {
if (!typingState) {
return;
}
await removeTypingIndicator({ cfg, state: typingState, accountId });
await removeTypingIndicator({ cfg, state: typingState, accountId, runtime: params.runtime });
typingState = null;
},
onStartError: (err) =>

View File

@@ -1,6 +1,7 @@
import type { ClawdbotConfig } from "openclaw/plugin-sdk";
import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
import { resolveFeishuAccount } from "./accounts.js";
import { createFeishuClient } from "./client.js";
import { getFeishuRuntime } from "./runtime.js";
// Feishu emoji types for typing indicator
// See: https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce
@@ -103,8 +104,9 @@ export async function addTypingIndicator(params: {
cfg: ClawdbotConfig;
messageId: string;
accountId?: string;
runtime?: RuntimeEnv;
}): Promise<TypingIndicatorState> {
const { cfg, messageId, accountId } = params;
const { cfg, messageId, accountId, runtime } = params;
const account = resolveFeishuAccount({ cfg, accountId });
if (!account.configured) {
return { messageId, reactionId: null };
@@ -124,9 +126,11 @@ export async function addTypingIndicator(params: {
// instead of throwing. Detect backoff codes and throw to trip the breaker.
const backoffCode = getBackoffCodeFromResponse(response);
if (backoffCode !== undefined) {
console.log(
`[feishu] typing indicator response contains backoff code ${backoffCode}, stopping keepalive`,
);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.(
`[feishu] typing indicator response contains backoff code ${backoffCode}, stopping keepalive`,
);
}
throw new FeishuBackoffError(backoffCode);
}
@@ -135,11 +139,15 @@ export async function addTypingIndicator(params: {
return { messageId, reactionId };
} catch (err) {
if (isFeishuBackoffError(err)) {
console.log(`[feishu] typing indicator hit rate-limit/quota, stopping keepalive`);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.("[feishu] typing indicator hit rate-limit/quota, stopping keepalive");
}
throw err;
}
// Silently fail for other non-critical errors (e.g. message deleted, permission issues)
console.log(`[feishu] failed to add typing indicator: ${err}`);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.(`[feishu] failed to add typing indicator: ${String(err)}`);
}
return { messageId, reactionId: null };
}
}
@@ -153,8 +161,9 @@ export async function removeTypingIndicator(params: {
cfg: ClawdbotConfig;
state: TypingIndicatorState;
accountId?: string;
runtime?: RuntimeEnv;
}): Promise<void> {
const { cfg, state, accountId } = params;
const { cfg, state, accountId, runtime } = params;
if (!state.reactionId) {
return;
}
@@ -177,17 +186,25 @@ export async function removeTypingIndicator(params: {
// Check for backoff codes in non-throwing SDK responses
const backoffCode = getBackoffCodeFromResponse(result);
if (backoffCode !== undefined) {
console.log(
`[feishu] typing indicator removal response contains backoff code ${backoffCode}, stopping keepalive`,
);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.(
`[feishu] typing indicator removal response contains backoff code ${backoffCode}, stopping keepalive`,
);
}
throw new FeishuBackoffError(backoffCode);
}
} catch (err) {
if (isFeishuBackoffError(err)) {
console.log(`[feishu] typing indicator removal hit rate-limit/quota, stopping keepalive`);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.(
"[feishu] typing indicator removal hit rate-limit/quota, stopping keepalive",
);
}
throw err;
}
// Silently fail for other non-critical errors
console.log(`[feishu] failed to remove typing indicator: ${err}`);
if (getFeishuRuntime().logging.shouldLogVerbose()) {
runtime?.log?.(`[feishu] failed to remove typing indicator: ${String(err)}`);
}
}
}

View File

@@ -143,7 +143,7 @@ function startPollingLoop(params: {
if (err instanceof ZaloApiError && err.isPollingTimeout) {
// no updates
} else if (!isStopped() && !abortSignal.aborted) {
console.error(`[${account.accountId}] Zalo polling error:`, err);
runtime.error?.(`[${account.accountId}] Zalo polling error: ${String(err)}`);
await new Promise((resolve) => setTimeout(resolve, 5000));
}
}
@@ -190,10 +190,12 @@ async function processUpdate(
);
break;
case "message.sticker.received":
console.log(`[${account.accountId}] Received sticker from ${message.from.id}`);
logVerbose(core, runtime, `[${account.accountId}] Received sticker from ${message.from.id}`);
break;
case "message.unsupported.received":
console.log(
logVerbose(
core,
runtime,
`[${account.accountId}] Received unsupported message type from ${message.from.id}`,
);
break;
@@ -259,7 +261,7 @@ async function handleImageMessage(
mediaPath = saved.path;
mediaType = saved.contentType;
} catch (err) {
console.error(`[${account.accountId}] Failed to download Zalo image:`, err);
runtime.error?.(`[${account.accountId}] Failed to download Zalo image: ${String(err)}`);
}
}