fix(ci): align telegram runtime and test drift

This commit is contained in:
Peter Steinberger
2026-03-28 04:40:36 +00:00
parent 7779205aa1
commit 324c621ebe
7 changed files with 59 additions and 21 deletions

View File

@@ -1,3 +1,4 @@
import type { ReactionTypeEmoji } from "@grammyjs/types";
import { resolveAckReaction } from "openclaw/plugin-sdk/agent-runtime";
import {
createStatusReactionController,
@@ -42,6 +43,40 @@ export type {
TelegramMediaRef,
} from "./bot-message-context.types.js";
type TelegramMessageContextPayload = Awaited<ReturnType<typeof buildTelegramInboundContextPayload>>;
type TelegramReactionApi = (
chatId: number | string,
messageId: number,
reactions: Array<{ type: "emoji"; emoji: ReactionTypeEmoji["emoji"] }>,
) => Promise<unknown>;
export type TelegramMessageContext = {
ctxPayload: TelegramMessageContextPayload["ctxPayload"];
primaryCtx: BuildTelegramMessageContextParams["primaryCtx"];
msg: BuildTelegramMessageContextParams["primaryCtx"]["message"];
chatId: number | string;
isGroup: boolean;
groupConfig?: ReturnType<
BuildTelegramMessageContextParams["resolveTelegramGroupConfig"]
>["groupConfig"];
resolvedThreadId?: number;
threadSpec: ReturnType<typeof resolveTelegramThreadSpec>;
replyThreadId?: number;
isForum: boolean;
historyKey?: string;
historyLimit: BuildTelegramMessageContextParams["historyLimit"];
groupHistories: BuildTelegramMessageContextParams["groupHistories"];
route: ReturnType<typeof resolveTelegramConversationRoute>["route"];
skillFilter: TelegramMessageContextPayload["skillFilter"];
sendTyping: () => Promise<void>;
sendRecordVoice: () => Promise<void>;
ackReactionPromise: Promise<boolean> | null;
reactionApi: TelegramReactionApi | null;
removeAckAfterReply: boolean;
statusReactionController: StatusReactionController | null;
accountId: string;
};
export const buildTelegramMessageContext = async ({
primaryCtx,
allMedia,
@@ -64,7 +99,7 @@ export const buildTelegramMessageContext = async ({
loadFreshConfig,
upsertPairingRequest,
sendChatActionHandler,
}: BuildTelegramMessageContextParams) => {
}: BuildTelegramMessageContextParams): Promise<TelegramMessageContext | null> => {
const msg = primaryCtx.message;
const chatId = msg.chat.id;
const isGroup = msg.chat.type === "group" || msg.chat.type === "supergroup";
@@ -378,7 +413,7 @@ export const buildTelegramMessageContext = async ({
return;
}
await reactionApi(chatId, msg.message_id, [
{ type: "emoji", emoji: resolvedEmoji },
{ type: "emoji", emoji: resolvedEmoji as ReactionTypeEmoji["emoji"] },
]);
}
},
@@ -404,7 +439,10 @@ export const buildTelegramMessageContext = async ({
: shouldAckReaction() && msg.message_id && reactionApi
? withTelegramApiErrorLogging({
operation: "setMessageReaction",
fn: () => reactionApi(chatId, msg.message_id, [{ type: "emoji", emoji: ackReaction }]),
fn: () =>
reactionApi(chatId, msg.message_id, [
{ type: "emoji", emoji: ackReaction as ReactionTypeEmoji["emoji"] },
]),
}).then(
() => true,
(err) => {
@@ -469,7 +507,3 @@ export const buildTelegramMessageContext = async ({
accountId: account.accountId,
};
};
export type TelegramMessageContext = NonNullable<
Awaited<ReturnType<typeof buildTelegramMessageContext>>
>;

View File

@@ -943,7 +943,9 @@ export const dispatchTelegramMessage = async ({
removeAfterReply: removeAckAfterReply,
ackReactionPromise,
ackReactionValue: ackReactionPromise ? "ack" : null,
remove: () => reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve(),
remove: async () => {
await (reactionApi?.(chatId, msg.message_id ?? 0, []) ?? Promise.resolve());
},
onError: (err) => {
if (!msg.message_id) {
return;

View File

@@ -5,7 +5,7 @@ export type TelegramStreamMode = "off" | "partial" | "block";
export type TelegramGetFile = () => Promise<{ file_path?: string }>;
export type TelegramChatDetails = {
available_reactions?: ChatFullInfo["available_reactions"];
available_reactions?: ChatFullInfo["available_reactions"] | null;
is_forum?: boolean;
};
export type TelegramGetChat = (chatId: number | string) => Promise<TelegramChatDetails>;

View File

@@ -12,7 +12,7 @@ const DRAFT_METHOD_UNAVAILABLE_RE =
const DRAFT_CHAT_UNSUPPORTED_RE = /(can't be used|can be used only)/i;
type TelegramSendMessageDraft = (
chatId: number,
chatId: Parameters<Bot["api"]["sendMessage"]>[0],
draftId: number,
text: string,
params?: {
@@ -105,7 +105,7 @@ type SupersededTelegramPreview = {
export function createTelegramDraftStream(params: {
api: Bot["api"];
chatId: number;
chatId: Parameters<Bot["api"]["sendMessage"]>[0];
maxChars?: number;
thread?: TelegramThreadSpec | null;
previewTransport?: "auto" | "message" | "draft";
@@ -180,8 +180,8 @@ export function createTelegramDraftStream(params: {
}
: replyParams;
const usedThreadParams =
"message_thread_id" in (sendParams ?? {}) &&
typeof sendParams?.message_thread_id === "number";
typeof (sendParams as { message_thread_id?: unknown } | undefined)?.message_thread_id ===
"number";
try {
return {
sent: await params.api.sendMessage(chatId, sendArgs.renderedText, sendParams),

View File

@@ -18,5 +18,6 @@ export function isTelegramForumServiceMessage(msg: unknown): boolean {
if (!msg || typeof msg !== "object") {
return false;
}
return TELEGRAM_FORUM_SERVICE_FIELDS.some((field) => field in msg && msg[field] != null);
const record = msg as Record<string, unknown>;
return TELEGRAM_FORUM_SERVICE_FIELDS.some((field) => field in record && record[field] != null);
}

View File

@@ -1,3 +1,4 @@
import type { ReactionTypeEmoji } from "@grammyjs/types";
import type { ChannelAccountSnapshot } from "openclaw/plugin-sdk/channel-contract";
import { describe, expect, it } from "vitest";
import { DEFAULT_EMOJIS } from "../../../src/channels/status-reactions.js";
@@ -138,7 +139,7 @@ describe("isTelegramSupportedReactionEmoji", () => {
describe("extractTelegramAllowedEmojiReactions", () => {
it("returns undefined when chat does not include available_reactions", () => {
const result = extractTelegramAllowedEmojiReactions({ id: 1 });
const result = extractTelegramAllowedEmojiReactions({});
expect(result).toBeUndefined();
});
@@ -170,11 +171,11 @@ describe("extractTelegramAllowedEmojiReactions", () => {
describe("resolveTelegramAllowedEmojiReactions", () => {
it("uses getChat lookup when message chat does not include available_reactions", async () => {
const getChat = async () => ({
available_reactions: [{ type: "emoji", emoji: "👍" }],
available_reactions: [{ type: "emoji", emoji: "👍" as ReactionTypeEmoji["emoji"] } as const],
});
const result = await resolveTelegramAllowedEmojiReactions({
chat: { id: 1 },
chat: {},
chatId: 1,
getChat,
});
@@ -188,7 +189,7 @@ describe("resolveTelegramAllowedEmojiReactions", () => {
};
const result = await resolveTelegramAllowedEmojiReactions({
chat: { id: 1 },
chat: {},
chatId: 1,
getChat,
});