mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 16:24:30 +00:00
chore: merge origin/main into main
This commit is contained in:
29
src/telegram/group-access.group-policy.test.ts
Normal file
29
src/telegram/group-access.group-policy.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveTelegramRuntimeGroupPolicy } from "./group-access.js";
|
||||
|
||||
describe("resolveTelegramRuntimeGroupPolicy", () => {
|
||||
it("fails closed when channels.telegram is missing and no defaults are set", () => {
|
||||
const resolved = resolveTelegramRuntimeGroupPolicy({
|
||||
providerConfigPresent: false,
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("allowlist");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps open fallback when channels.telegram is configured", () => {
|
||||
const resolved = resolveTelegramRuntimeGroupPolicy({
|
||||
providerConfigPresent: true,
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("open");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores explicit defaults when provider config is missing", () => {
|
||||
const resolved = resolveTelegramRuntimeGroupPolicy({
|
||||
providerConfigPresent: false,
|
||||
defaultGroupPolicy: "disabled",
|
||||
});
|
||||
expect(resolved.groupPolicy).toBe("allowlist");
|
||||
expect(resolved.providerMissingFallbackApplied).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import type { ChannelGroupPolicy } from "../config/group-policy.js";
|
||||
import { resolveOpenProviderRuntimeGroupPolicy } from "../config/runtime-group-policy.js";
|
||||
import type {
|
||||
TelegramAccountConfig,
|
||||
TelegramGroupConfig,
|
||||
@@ -72,6 +73,17 @@ export type TelegramGroupPolicyAccessResult =
|
||||
groupPolicy: "open" | "disabled" | "allowlist";
|
||||
};
|
||||
|
||||
export const resolveTelegramRuntimeGroupPolicy = (params: {
|
||||
providerConfigPresent: boolean;
|
||||
groupPolicy?: TelegramAccountConfig["groupPolicy"];
|
||||
defaultGroupPolicy?: TelegramAccountConfig["groupPolicy"];
|
||||
}) =>
|
||||
resolveOpenProviderRuntimeGroupPolicy({
|
||||
providerConfigPresent: params.providerConfigPresent,
|
||||
groupPolicy: params.groupPolicy,
|
||||
defaultGroupPolicy: params.defaultGroupPolicy,
|
||||
});
|
||||
|
||||
export const evaluateTelegramGroupPolicyAccess = (params: {
|
||||
isGroup: boolean;
|
||||
chatId: string | number;
|
||||
@@ -90,20 +102,21 @@ export const evaluateTelegramGroupPolicyAccess = (params: {
|
||||
requireSenderForAllowlistAuthorization: boolean;
|
||||
checkChatAllowlist: boolean;
|
||||
}): TelegramGroupPolicyAccessResult => {
|
||||
const { groupPolicy: runtimeFallbackPolicy } = resolveTelegramRuntimeGroupPolicy({
|
||||
providerConfigPresent: params.cfg.channels?.telegram !== undefined,
|
||||
groupPolicy: params.telegramCfg.groupPolicy,
|
||||
defaultGroupPolicy: params.cfg.channels?.defaults?.groupPolicy,
|
||||
});
|
||||
const fallbackPolicy =
|
||||
firstDefined(
|
||||
params.telegramCfg.groupPolicy,
|
||||
params.cfg.channels?.defaults?.groupPolicy,
|
||||
"open",
|
||||
) ?? "open";
|
||||
firstDefined(params.telegramCfg.groupPolicy, params.cfg.channels?.defaults?.groupPolicy) ??
|
||||
runtimeFallbackPolicy;
|
||||
const groupPolicy = params.useTopicAndGroupOverrides
|
||||
? (firstDefined(
|
||||
params.topicConfig?.groupPolicy,
|
||||
params.groupConfig?.groupPolicy,
|
||||
params.telegramCfg.groupPolicy,
|
||||
params.cfg.channels?.defaults?.groupPolicy,
|
||||
"open",
|
||||
) ?? "open")
|
||||
) ?? runtimeFallbackPolicy)
|
||||
: fallbackPolicy;
|
||||
|
||||
if (!params.isGroup || !params.enforcePolicy) {
|
||||
|
||||
@@ -169,8 +169,12 @@ describe("monitorTelegramProvider (grammY)", () => {
|
||||
expect(api.sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retries on recoverable network errors", async () => {
|
||||
const networkError = Object.assign(new Error("timeout"), { code: "ETIMEDOUT" });
|
||||
it("retries on recoverable undici fetch errors", async () => {
|
||||
const networkError = Object.assign(new TypeError("fetch failed"), {
|
||||
cause: Object.assign(new Error("connect timeout"), {
|
||||
code: "UND_ERR_CONNECT_TIMEOUT",
|
||||
}),
|
||||
});
|
||||
runSpy
|
||||
.mockImplementationOnce(() => ({
|
||||
task: () => Promise.reject(networkError),
|
||||
|
||||
@@ -30,12 +30,25 @@ describe("isRecoverableTelegramNetworkError", () => {
|
||||
expect(isRecoverableTelegramNetworkError(new Error("Undici: socket failure"))).toBe(true);
|
||||
});
|
||||
|
||||
it("skips message matches for send context", () => {
|
||||
it("treats undici fetch failed errors as recoverable in send context", () => {
|
||||
const err = new TypeError("fetch failed");
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "send" })).toBe(true);
|
||||
expect(
|
||||
isRecoverableTelegramNetworkError(new Error("TypeError: fetch failed"), { context: "send" }),
|
||||
).toBe(true);
|
||||
expect(isRecoverableTelegramNetworkError(err, { context: "polling" })).toBe(true);
|
||||
});
|
||||
|
||||
it("skips broad message matches for send context", () => {
|
||||
const networkRequestErr = new Error("Network request for 'sendMessage' failed!");
|
||||
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(networkRequestErr, { context: "polling" })).toBe(true);
|
||||
|
||||
const undiciSnippetErr = new Error("Undici: socket failure");
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "send" })).toBe(false);
|
||||
expect(isRecoverableTelegramNetworkError(undiciSnippetErr, { context: "polling" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for unrelated errors", () => {
|
||||
expect(isRecoverableTelegramNetworkError(new Error("invalid token"))).toBe(false);
|
||||
});
|
||||
|
||||
@@ -27,9 +27,9 @@ const RECOVERABLE_ERROR_NAMES = new Set([
|
||||
"BodyTimeoutError",
|
||||
]);
|
||||
|
||||
const ALWAYS_RECOVERABLE_MESSAGES = new Set(["fetch failed", "typeerror: fetch failed"]);
|
||||
|
||||
const RECOVERABLE_MESSAGE_SNIPPETS = [
|
||||
"fetch failed",
|
||||
"typeerror: fetch failed",
|
||||
"undici",
|
||||
"network error",
|
||||
"network request",
|
||||
@@ -138,9 +138,12 @@ export function isRecoverableTelegramNetworkError(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allowMessageMatch) {
|
||||
const message = formatErrorMessage(candidate).toLowerCase();
|
||||
if (message && RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) {
|
||||
const message = formatErrorMessage(candidate).trim().toLowerCase();
|
||||
if (message && ALWAYS_RECOVERABLE_MESSAGES.has(message)) {
|
||||
return true;
|
||||
}
|
||||
if (allowMessageMatch && message) {
|
||||
if (RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user