mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-27 09:21:35 +07:00
fix(ci): harden telegram seams and cap job timeouts
This commit is contained in:
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@@ -21,6 +21,7 @@ jobs:
|
||||
docs-scope:
|
||||
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
docs_only: ${{ steps.check.outputs.docs_only }}
|
||||
docs_changed: ${{ steps.check.outputs.docs_changed }}
|
||||
@@ -48,6 +49,7 @@ jobs:
|
||||
needs: [docs-scope]
|
||||
if: needs.docs-scope.outputs.docs_only != 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
run_node: ${{ steps.scope.outputs.run_node }}
|
||||
run_macos: ${{ steps.scope.outputs.run_macos }}
|
||||
@@ -86,6 +88,7 @@ jobs:
|
||||
needs: [docs-scope, changed-scope]
|
||||
if: needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_node == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
has_changed_extensions: ${{ steps.changed.outputs.has_changed_extensions }}
|
||||
changed_extensions_matrix: ${{ steps.changed.outputs.changed_extensions_matrix }}
|
||||
@@ -129,6 +132,7 @@ jobs:
|
||||
secrets:
|
||||
if: github.event_name != 'pull_request' || !github.event.pull_request.draft
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -209,6 +213,7 @@ jobs:
|
||||
needs: [docs-scope, changed-scope, changed-extensions, secrets]
|
||||
if: always() && needs.docs-scope.result == 'success' && (needs.changed-scope.result == 'success' || needs.changed-scope.result == 'skipped') && (needs.changed-extensions.result == 'success' || needs.changed-extensions.result == 'skipped') && needs.secrets.result == 'success'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
outputs:
|
||||
docs_only: ${{ needs.docs-scope.outputs.docs_only }}
|
||||
docs_changed: ${{ needs.docs-scope.outputs.docs_changed }}
|
||||
@@ -230,6 +235,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: github.event_name == 'push' && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -264,6 +270,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: github.event_name == 'push' && needs.preflight.outputs.docs_only != 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -289,6 +296,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -384,6 +392,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && needs.preflight.outputs.has_changed_extensions == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.changed_extensions_matrix) }}
|
||||
@@ -410,6 +419,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -433,6 +443,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -530,6 +541,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_node == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -570,6 +582,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_changed == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -589,6 +602,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.preflight.outputs.run_skills_python == 'true')
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -615,7 +629,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: always() && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_windows == 'true' && (github.event_name != 'push' || needs.build-artifacts.result == 'success')
|
||||
runs-on: blacksmith-32vcpu-windows-2025
|
||||
timeout-minutes: 45
|
||||
timeout-minutes: 20
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
# Keep total concurrency predictable on the 32 vCPU runner.
|
||||
@@ -755,6 +769,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: github.event_name == 'pull_request' && needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_macos == 'true'
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
@@ -831,6 +846,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.docs_only != 'true' && needs.preflight.outputs.run_android == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { API_CONSTANTS } from "grammy";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveTelegramAllowedUpdates } from "./allowed-updates.js";
|
||||
import { DEFAULT_TELEGRAM_UPDATE_TYPES, resolveTelegramAllowedUpdates } from "./allowed-updates.js";
|
||||
|
||||
describe("resolveTelegramAllowedUpdates", () => {
|
||||
it("includes the default update types plus reaction and channel post support", () => {
|
||||
const updates = resolveTelegramAllowedUpdates();
|
||||
|
||||
expect(updates).toEqual(expect.arrayContaining([...API_CONSTANTS.DEFAULT_UPDATE_TYPES]));
|
||||
expect(updates).toEqual(
|
||||
expect.arrayContaining([
|
||||
...DEFAULT_TELEGRAM_UPDATE_TYPES,
|
||||
...(API_CONSTANTS?.DEFAULT_UPDATE_TYPES ?? []),
|
||||
]),
|
||||
);
|
||||
expect(updates).toContain("message_reaction");
|
||||
expect(updates).toContain("channel_post");
|
||||
expect(new Set(updates).size).toBe(updates.length);
|
||||
|
||||
@@ -1,9 +1,60 @@
|
||||
import { API_CONSTANTS } from "grammy";
|
||||
|
||||
type TelegramUpdateType = (typeof API_CONSTANTS.ALL_UPDATE_TYPES)[number];
|
||||
const FALLBACK_ALL_UPDATE_TYPES = [
|
||||
"message",
|
||||
"edited_message",
|
||||
"channel_post",
|
||||
"edited_channel_post",
|
||||
"business_connection",
|
||||
"business_message",
|
||||
"edited_business_message",
|
||||
"deleted_business_messages",
|
||||
"message_reaction",
|
||||
"message_reaction_count",
|
||||
"inline_query",
|
||||
"chosen_inline_result",
|
||||
"callback_query",
|
||||
"shipping_query",
|
||||
"pre_checkout_query",
|
||||
"poll",
|
||||
"poll_answer",
|
||||
"my_chat_member",
|
||||
"chat_member",
|
||||
"chat_join_request",
|
||||
] as const;
|
||||
|
||||
const FALLBACK_DEFAULT_UPDATE_TYPES = [
|
||||
"message",
|
||||
"edited_message",
|
||||
"channel_post",
|
||||
"edited_channel_post",
|
||||
"business_connection",
|
||||
"business_message",
|
||||
"edited_business_message",
|
||||
"deleted_business_messages",
|
||||
"message_reaction",
|
||||
"message_reaction_count",
|
||||
"inline_query",
|
||||
"chosen_inline_result",
|
||||
"callback_query",
|
||||
"shipping_query",
|
||||
"pre_checkout_query",
|
||||
"poll",
|
||||
"poll_answer",
|
||||
"my_chat_member",
|
||||
"chat_member",
|
||||
"chat_join_request",
|
||||
] as const;
|
||||
|
||||
export type TelegramUpdateType =
|
||||
| (typeof FALLBACK_ALL_UPDATE_TYPES)[number]
|
||||
| (typeof API_CONSTANTS.ALL_UPDATE_TYPES)[number];
|
||||
|
||||
export const DEFAULT_TELEGRAM_UPDATE_TYPES: ReadonlyArray<TelegramUpdateType> =
|
||||
API_CONSTANTS?.DEFAULT_UPDATE_TYPES ?? FALLBACK_DEFAULT_UPDATE_TYPES;
|
||||
|
||||
export function resolveTelegramAllowedUpdates(): ReadonlyArray<TelegramUpdateType> {
|
||||
const updates = [...API_CONSTANTS.DEFAULT_UPDATE_TYPES] as TelegramUpdateType[];
|
||||
const updates = [...DEFAULT_TELEGRAM_UPDATE_TYPES] as TelegramUpdateType[];
|
||||
if (!updates.includes("message_reaction")) {
|
||||
updates.push("message_reaction");
|
||||
}
|
||||
|
||||
@@ -8,6 +8,12 @@ vi.mock("undici", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("undici")>();
|
||||
return {
|
||||
...actual,
|
||||
Agent:
|
||||
actual.Agent ??
|
||||
class Agent {
|
||||
close() {}
|
||||
destroy() {}
|
||||
},
|
||||
fetch: undiciFetch,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ export type TelegramBotDeps = {
|
||||
upsertChannelPairingRequest: typeof upsertChannelPairingRequest;
|
||||
enqueueSystemEvent: typeof enqueueSystemEvent;
|
||||
dispatchReplyWithBufferedBlockDispatcher: typeof dispatchReplyWithBufferedBlockDispatcher;
|
||||
loadWebMedia: typeof loadWebMedia;
|
||||
loadWebMedia?: typeof loadWebMedia;
|
||||
buildModelsProviderData: typeof buildModelsProviderData;
|
||||
listSkillCommandsForAgents: typeof listSkillCommandsForAgents;
|
||||
wasSentByBot: typeof wasSentByBot;
|
||||
|
||||
@@ -43,6 +43,8 @@ import {
|
||||
|
||||
const VOICE_FORBIDDEN_RE = /VOICE_MESSAGES_FORBIDDEN/;
|
||||
const CAPTION_TOO_LONG_RE = /caption is too long/i;
|
||||
const GrammyErrorCtor: typeof GrammyError | undefined =
|
||||
typeof GrammyError === "function" ? GrammyError : undefined;
|
||||
|
||||
type DeliveryProgress = ReplyThreadDeliveryProgress & {
|
||||
deliveredCount: number;
|
||||
@@ -175,14 +177,14 @@ async function sendPendingFollowUpText(params: {
|
||||
}
|
||||
|
||||
function isVoiceMessagesForbidden(err: unknown): boolean {
|
||||
if (err instanceof GrammyError) {
|
||||
if (GrammyErrorCtor && err instanceof GrammyErrorCtor) {
|
||||
return VOICE_FORBIDDEN_RE.test(err.description);
|
||||
}
|
||||
return VOICE_FORBIDDEN_RE.test(formatErrorMessage(err));
|
||||
}
|
||||
|
||||
function isCaptionTooLong(err: unknown): boolean {
|
||||
if (err instanceof GrammyError) {
|
||||
if (GrammyErrorCtor && err instanceof GrammyErrorCtor) {
|
||||
return CAPTION_TOO_LONG_RE.test(err.description);
|
||||
}
|
||||
return CAPTION_TOO_LONG_RE.test(formatErrorMessage(err));
|
||||
|
||||
@@ -15,6 +15,9 @@ import { resolveTelegramMediaPlaceholder } from "./helpers.js";
|
||||
import type { StickerMetadata, TelegramContext } from "./types.js";
|
||||
|
||||
const FILE_TOO_BIG_RE = /file is too big/i;
|
||||
const GrammyErrorCtor: typeof GrammyError | undefined =
|
||||
typeof GrammyError === "function" ? GrammyError : undefined;
|
||||
|
||||
function buildTelegramMediaSsrfPolicy(apiRoot?: string) {
|
||||
const hostnames = ["api.telegram.org"];
|
||||
if (apiRoot) {
|
||||
@@ -41,7 +44,7 @@ function buildTelegramMediaSsrfPolicy(apiRoot?: string) {
|
||||
* Unlike network errors, this is a permanent error and should not be retried.
|
||||
*/
|
||||
function isFileTooBigError(err: unknown): boolean {
|
||||
if (err instanceof GrammyError) {
|
||||
if (GrammyErrorCtor && err instanceof GrammyErrorCtor) {
|
||||
return FILE_TOO_BIG_RE.test(err.description);
|
||||
}
|
||||
return FILE_TOO_BIG_RE.test(formatErrorMessage(err));
|
||||
|
||||
@@ -9,9 +9,11 @@ import { buildTelegramThreadParams, type TelegramThreadSpec } from "./helpers.js
|
||||
const PARSE_ERR_RE = /can't parse entities|parse entities|find end of the entity/i;
|
||||
const EMPTY_TEXT_ERR_RE = /message text is empty/i;
|
||||
const THREAD_NOT_FOUND_RE = /message thread not found/i;
|
||||
const GrammyErrorCtor: typeof GrammyError | undefined =
|
||||
typeof GrammyError === "function" ? GrammyError : undefined;
|
||||
|
||||
function isTelegramThreadNotFoundError(err: unknown): boolean {
|
||||
if (err instanceof GrammyError) {
|
||||
if (GrammyErrorCtor && err instanceof GrammyErrorCtor) {
|
||||
return THREAD_NOT_FOUND_RE.test(err.description);
|
||||
}
|
||||
return THREAD_NOT_FOUND_RE.test(formatErrorMessage(err));
|
||||
|
||||
Reference in New Issue
Block a user