OpenClaw 飞书API调用次数耗尽解决办法

本质原因:每分钟调用一次/open-apis/bot/v3/info接口,24小时调用1440次,导致飞书API额度消耗殆尽

飞书API调用次数耗尽

根据飞书开放平台文档,企业自建应用API额度1W/月,每月1日重置

官方文档地址: https://open.feishu.cn/document/platform-notices/platform-updates-/custom-app-api-call-limit

原因

OpenClaw Gateway 
    ↓ 每分钟健康检查
调用插件的 status.probeAccount()
    ↓ (在 channel.ts 中定义)
probeAccount() { return await probeFeishu(cfg) }
    ↓ (在 probe.ts 中定义)
probeFeishu() {
    // 没有缓存 → 调用飞书 API /open-apis/bot/v3/info
}
    ↓
HTTP 请求到飞书服务器

排查你的情况

如下图所示

飞书API调用日志

处理办法

- 推荐做法:修改.openclaw/extensions/feishu/src/probe.ts增加缓存

修改probe.ts增加缓存

- 再次查看你飞书后台日志检索,查看是否频繁调用/open-apis/bot/v3/info

import type { FeishuConfig, FeishuProbeResult } from "./types.js";
import { createFeishuClient } from "./client.js";
import { resolveFeishuCredentials } from "./accounts.js";

// Cache probe results to avoid hitting API rate limits
// Cache for 24 hours (86400 seconds)
const PROBE_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const probeCache = new Map();

function getCacheKey(cfg?: FeishuConfig): string {
  if (!cfg?.appId) return "no-creds";
  return `${cfg.appId}:${cfg.domain ?? "feishu"}`;
}

export async function probeFeishu(cfg?: FeishuConfig): Promise {
  const creds = resolveFeishuCredentials(cfg);
  if (!creds) {
    return {
      ok: false,
      error: "missing credentials (appId, appSecret)",
    };
  }

  // Check cache first
  const cacheKey = getCacheKey(cfg);
  const cached = probeCache.get(cacheKey);
  if (cached && Date.now() - cached.timestamp < PROBE_CACHE_TTL_MS) {
    return cached.result;
  }

  try {
    const client = createFeishuClient(cfg!);
    // Use im.chat.list as a simple connectivity test
    // The bot info API path varies by SDK version
    const response = await (client as any).request({
      method: "GET",
      url: "/open-apis/bot/v3/info",
      data: {},
    });

    if (response.code !== 0) {
      const result = {
        ok: false,
        appId: creds.appId,
        error: `API error: ${response.msg || `code ${response.code}`}`,
      };
      probeCache.set(cacheKey, { result, timestamp: Date.now() });
      return result;
    }

    const bot = response.bot || response.data?.bot;
    const result = {
      ok: true,
      appId: creds.appId,
      botName: bot?.bot_name,
      botOpenId: bot?.open_id,
    };
    probeCache.set(cacheKey, { result, timestamp: Date.now() });
    return result;
  } catch (err) {
    const result = {
      ok: false,
      appId: creds.appId,
      error: err instanceof Error ? err.message : String(err),
    };
    probeCache.set(cacheKey, { result, timestamp: Date.now() });
    return result;
  }
}

// Clear the probe cache (useful for testing or when credentials change)
export function clearProbeCache(): void {
  probeCache.clear();
}

// Export for testing
export { PROBE_CACHE_TTL_MS };