mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-31 11:51:22 +00:00
When OpenClaw restarts under load, the Feishu bot-info probe (`/open-apis/bot/v3/info`) can exceed the 10-second timeout due to event-loop contention during channel initialization. This leaves `botOpenId` empty, causing `checkBotMentioned()` to return `false` for every group message — silently dropping them all while DMs continue to work fine. Two fixes: 1. **Increase startup probe timeout from 10s to 30s** and make it configurable via `OPENCLAW_FEISHU_STARTUP_PROBE_TIMEOUT_MS` env var. The previous 10s budget was too tight when multiple channels (Slack, Discord, Feishu) initialize concurrently. 2. **Graceful degradation in `checkBotMentioned()`**: when `botOpenId` is unknown, return `true` (assume mentioned) instead of `false`. This prevents group messages from being silently discarded when the probe fails for any reason. The trade-off is that the bot may respond to non-@-mentioned messages temporarily until the next successful probe, which is far preferable to total silence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import type { RuntimeEnv } from "../runtime-api.js";
|
|
import { probeFeishu } from "./probe.js";
|
|
import type { ResolvedFeishuAccount } from "./types.js";
|
|
|
|
const FEISHU_STARTUP_BOT_INFO_TIMEOUT_DEFAULT_MS = 30_000;
|
|
const FEISHU_STARTUP_BOT_INFO_TIMEOUT_ENV = "OPENCLAW_FEISHU_STARTUP_PROBE_TIMEOUT_MS";
|
|
|
|
function resolveStartupProbeTimeoutMs(): number {
|
|
const raw = process.env[FEISHU_STARTUP_BOT_INFO_TIMEOUT_ENV];
|
|
if (raw) {
|
|
const parsed = Number(raw);
|
|
if (Number.isFinite(parsed) && parsed > 0) {
|
|
return Math.floor(parsed);
|
|
}
|
|
console.warn(
|
|
`[feishu] ${FEISHU_STARTUP_BOT_INFO_TIMEOUT_ENV}="${raw}" is invalid; using default ${FEISHU_STARTUP_BOT_INFO_TIMEOUT_DEFAULT_MS}ms`,
|
|
);
|
|
}
|
|
return FEISHU_STARTUP_BOT_INFO_TIMEOUT_DEFAULT_MS;
|
|
}
|
|
|
|
export const FEISHU_STARTUP_BOT_INFO_TIMEOUT_MS = resolveStartupProbeTimeoutMs();
|
|
|
|
type FetchBotOpenIdOptions = {
|
|
runtime?: RuntimeEnv;
|
|
abortSignal?: AbortSignal;
|
|
timeoutMs?: number;
|
|
};
|
|
|
|
export type FeishuMonitorBotIdentity = {
|
|
botOpenId?: string;
|
|
botName?: string;
|
|
};
|
|
|
|
function isTimeoutErrorMessage(message: string | undefined): boolean {
|
|
return message?.toLowerCase().includes("timeout") || message?.toLowerCase().includes("timed out")
|
|
? true
|
|
: false;
|
|
}
|
|
|
|
function isAbortErrorMessage(message: string | undefined): boolean {
|
|
return message?.toLowerCase().includes("aborted") ?? false;
|
|
}
|
|
|
|
export async function fetchBotIdentityForMonitor(
|
|
account: ResolvedFeishuAccount,
|
|
options: FetchBotOpenIdOptions = {},
|
|
): Promise<FeishuMonitorBotIdentity> {
|
|
if (options.abortSignal?.aborted) {
|
|
return {};
|
|
}
|
|
|
|
const timeoutMs = options.timeoutMs ?? FEISHU_STARTUP_BOT_INFO_TIMEOUT_MS;
|
|
const result = await probeFeishu(account, {
|
|
timeoutMs,
|
|
abortSignal: options.abortSignal,
|
|
});
|
|
if (result.ok) {
|
|
return { botOpenId: result.botOpenId, botName: result.botName };
|
|
}
|
|
|
|
if (options.abortSignal?.aborted || isAbortErrorMessage(result.error)) {
|
|
return {};
|
|
}
|
|
|
|
if (isTimeoutErrorMessage(result.error)) {
|
|
const error = options.runtime?.error ?? console.error;
|
|
error(
|
|
`feishu[${account.accountId}]: bot info probe timed out after ${timeoutMs}ms; continuing startup`,
|
|
);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
export async function fetchBotOpenIdForMonitor(
|
|
account: ResolvedFeishuAccount,
|
|
options: FetchBotOpenIdOptions = {},
|
|
): Promise<string | undefined> {
|
|
const identity = await fetchBotIdentityForMonitor(account, options);
|
|
return identity.botOpenId;
|
|
}
|