fix(feishu): tolerate missing webhook defaults in older plugin-sdk (openclaw#31639) thanks @liuxiaopai-ai

Verified:
- pnpm test extensions/feishu/src/monitor.state.defaults.test.ts
- pnpm exec vitest run extensions/feishu/src/monitor.state.defaults.test.ts
- pnpm exec oxfmt --check extensions/feishu/src/monitor.state.ts extensions/feishu/src/monitor.state.defaults.test.ts CHANGELOG.md
- CI note: non-required check "check" failed on unrelated  TS errors outside this PR scope.

Co-authored-by: liuxiaopai-ai <73659136+liuxiaopai-ai@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
Mark L
2026-03-02 21:42:16 +08:00
committed by GitHub
parent 6df8bd9741
commit 097ad88f9d
3 changed files with 131 additions and 8 deletions

View File

@@ -0,0 +1,46 @@
import { describe, expect, it } from "vitest";
import {
resolveFeishuWebhookAnomalyDefaultsForTest,
resolveFeishuWebhookRateLimitDefaultsForTest,
} from "./monitor.state.js";
describe("feishu monitor state defaults", () => {
it("falls back to hard defaults when sdk defaults are missing", () => {
expect(resolveFeishuWebhookRateLimitDefaultsForTest(undefined)).toEqual({
windowMs: 60_000,
maxRequests: 120,
maxTrackedKeys: 4_096,
});
expect(resolveFeishuWebhookAnomalyDefaultsForTest(undefined)).toEqual({
maxTrackedKeys: 4_096,
ttlMs: 21_600_000,
logEvery: 25,
});
});
it("keeps valid sdk values and repairs invalid fields", () => {
expect(
resolveFeishuWebhookRateLimitDefaultsForTest({
windowMs: 45_000,
maxRequests: 0,
maxTrackedKeys: -1,
}),
).toEqual({
windowMs: 45_000,
maxRequests: 120,
maxTrackedKeys: 4_096,
});
expect(
resolveFeishuWebhookAnomalyDefaultsForTest({
maxTrackedKeys: 2048,
ttlMs: Number.NaN,
logEvery: 10,
}),
).toEqual({
maxTrackedKeys: 2048,
ttlMs: 21_600_000,
logEvery: 10,
});
});
});

View File

@@ -4,8 +4,8 @@ import {
createFixedWindowRateLimiter,
createWebhookAnomalyTracker,
type RuntimeEnv,
WEBHOOK_ANOMALY_COUNTER_DEFAULTS,
WEBHOOK_RATE_LIMIT_DEFAULTS,
WEBHOOK_ANOMALY_COUNTER_DEFAULTS as WEBHOOK_ANOMALY_COUNTER_DEFAULTS_FROM_SDK,
WEBHOOK_RATE_LIMIT_DEFAULTS as WEBHOOK_RATE_LIMIT_DEFAULTS_FROM_SDK,
} from "openclaw/plugin-sdk";
export const wsClients = new Map<string, Lark.WSClient>();
@@ -15,16 +15,92 @@ export const botOpenIds = new Map<string, string>();
export const FEISHU_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
export const FEISHU_WEBHOOK_BODY_TIMEOUT_MS = 30_000;
type WebhookRateLimitDefaults = {
windowMs: number;
maxRequests: number;
maxTrackedKeys: number;
};
type WebhookAnomalyDefaults = {
maxTrackedKeys: number;
ttlMs: number;
logEvery: number;
};
const FEISHU_WEBHOOK_RATE_LIMIT_FALLBACK_DEFAULTS: WebhookRateLimitDefaults = {
windowMs: 60_000,
maxRequests: 120,
maxTrackedKeys: 4_096,
};
const FEISHU_WEBHOOK_ANOMALY_FALLBACK_DEFAULTS: WebhookAnomalyDefaults = {
maxTrackedKeys: 4_096,
ttlMs: 6 * 60 * 60_000,
logEvery: 25,
};
function coercePositiveInt(value: unknown, fallback: number): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
return fallback;
}
const normalized = Math.floor(value);
return normalized > 0 ? normalized : fallback;
}
export function resolveFeishuWebhookRateLimitDefaultsForTest(
defaults: unknown,
): WebhookRateLimitDefaults {
const resolved = defaults as Partial<WebhookRateLimitDefaults> | null | undefined;
return {
windowMs: coercePositiveInt(
resolved?.windowMs,
FEISHU_WEBHOOK_RATE_LIMIT_FALLBACK_DEFAULTS.windowMs,
),
maxRequests: coercePositiveInt(
resolved?.maxRequests,
FEISHU_WEBHOOK_RATE_LIMIT_FALLBACK_DEFAULTS.maxRequests,
),
maxTrackedKeys: coercePositiveInt(
resolved?.maxTrackedKeys,
FEISHU_WEBHOOK_RATE_LIMIT_FALLBACK_DEFAULTS.maxTrackedKeys,
),
};
}
export function resolveFeishuWebhookAnomalyDefaultsForTest(
defaults: unknown,
): WebhookAnomalyDefaults {
const resolved = defaults as Partial<WebhookAnomalyDefaults> | null | undefined;
return {
maxTrackedKeys: coercePositiveInt(
resolved?.maxTrackedKeys,
FEISHU_WEBHOOK_ANOMALY_FALLBACK_DEFAULTS.maxTrackedKeys,
),
ttlMs: coercePositiveInt(resolved?.ttlMs, FEISHU_WEBHOOK_ANOMALY_FALLBACK_DEFAULTS.ttlMs),
logEvery: coercePositiveInt(
resolved?.logEvery,
FEISHU_WEBHOOK_ANOMALY_FALLBACK_DEFAULTS.logEvery,
),
};
}
const feishuWebhookRateLimitDefaults = resolveFeishuWebhookRateLimitDefaultsForTest(
WEBHOOK_RATE_LIMIT_DEFAULTS_FROM_SDK,
);
const feishuWebhookAnomalyDefaults = resolveFeishuWebhookAnomalyDefaultsForTest(
WEBHOOK_ANOMALY_COUNTER_DEFAULTS_FROM_SDK,
);
export const feishuWebhookRateLimiter = createFixedWindowRateLimiter({
windowMs: WEBHOOK_RATE_LIMIT_DEFAULTS.windowMs,
maxRequests: WEBHOOK_RATE_LIMIT_DEFAULTS.maxRequests,
maxTrackedKeys: WEBHOOK_RATE_LIMIT_DEFAULTS.maxTrackedKeys,
windowMs: feishuWebhookRateLimitDefaults.windowMs,
maxRequests: feishuWebhookRateLimitDefaults.maxRequests,
maxTrackedKeys: feishuWebhookRateLimitDefaults.maxTrackedKeys,
});
const feishuWebhookAnomalyTracker = createWebhookAnomalyTracker({
maxTrackedKeys: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.maxTrackedKeys,
ttlMs: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.ttlMs,
logEvery: WEBHOOK_ANOMALY_COUNTER_DEFAULTS.logEvery,
maxTrackedKeys: feishuWebhookAnomalyDefaults.maxTrackedKeys,
ttlMs: feishuWebhookAnomalyDefaults.ttlMs,
logEvery: feishuWebhookAnomalyDefaults.logEvery,
});
export function clearFeishuWebhookRateLimitStateForTest(): void {