Merge 6228f3cd87 into fa9ec6e854
This commit is contained in:
commit
2be4fe4863
@ -13,6 +13,7 @@ const sendStickerTelegram = vi.fn(async () => ({
|
|||||||
chatId: "123",
|
chatId: "123",
|
||||||
}));
|
}));
|
||||||
const deleteMessageTelegram = vi.fn(async () => ({ ok: true }));
|
const deleteMessageTelegram = vi.fn(async () => ({ ok: true }));
|
||||||
|
const editForumTopicTelegram = vi.fn(async () => ({ ok: true }));
|
||||||
const originalToken = process.env.TELEGRAM_BOT_TOKEN;
|
const originalToken = process.env.TELEGRAM_BOT_TOKEN;
|
||||||
|
|
||||||
vi.mock("../../telegram/send.js", () => ({
|
vi.mock("../../telegram/send.js", () => ({
|
||||||
@ -20,6 +21,7 @@ vi.mock("../../telegram/send.js", () => ({
|
|||||||
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
|
sendMessageTelegram: (...args: unknown[]) => sendMessageTelegram(...args),
|
||||||
sendStickerTelegram: (...args: unknown[]) => sendStickerTelegram(...args),
|
sendStickerTelegram: (...args: unknown[]) => sendStickerTelegram(...args),
|
||||||
deleteMessageTelegram: (...args: unknown[]) => deleteMessageTelegram(...args),
|
deleteMessageTelegram: (...args: unknown[]) => deleteMessageTelegram(...args),
|
||||||
|
editForumTopicTelegram: (...args: unknown[]) => editForumTopicTelegram(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("handleTelegramAction", () => {
|
describe("handleTelegramAction", () => {
|
||||||
@ -28,6 +30,7 @@ describe("handleTelegramAction", () => {
|
|||||||
sendMessageTelegram.mockClear();
|
sendMessageTelegram.mockClear();
|
||||||
sendStickerTelegram.mockClear();
|
sendStickerTelegram.mockClear();
|
||||||
deleteMessageTelegram.mockClear();
|
deleteMessageTelegram.mockClear();
|
||||||
|
editForumTopicTelegram.mockClear();
|
||||||
process.env.TELEGRAM_BOT_TOKEN = "tok";
|
process.env.TELEGRAM_BOT_TOKEN = "tok";
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -506,6 +509,80 @@ describe("handleTelegramAction", () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("edits a forum topic name", async () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { telegram: { botToken: "tok" } },
|
||||||
|
} as MoltbotConfig;
|
||||||
|
await handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "editForumTopic",
|
||||||
|
chatId: "-1001234567890",
|
||||||
|
messageThreadId: 42,
|
||||||
|
name: "New Topic Name",
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
);
|
||||||
|
expect(editForumTopicTelegram).toHaveBeenCalledWith(
|
||||||
|
"-1001234567890",
|
||||||
|
42,
|
||||||
|
expect.objectContaining({
|
||||||
|
token: "tok",
|
||||||
|
name: "New Topic Name",
|
||||||
|
iconCustomEmojiId: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects editForumTopic gating", async () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: {
|
||||||
|
telegram: { botToken: "tok", actions: { editForumTopic: false } },
|
||||||
|
},
|
||||||
|
} as MoltbotConfig;
|
||||||
|
await expect(
|
||||||
|
handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "editForumTopic",
|
||||||
|
chatId: "-1001234567890",
|
||||||
|
messageThreadId: 42,
|
||||||
|
name: "New Name",
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
),
|
||||||
|
).rejects.toThrow(/Telegram editForumTopic is disabled/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires at least name or iconCustomEmojiId", async () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { telegram: { botToken: "tok" } },
|
||||||
|
} as MoltbotConfig;
|
||||||
|
await expect(
|
||||||
|
handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "editForumTopic",
|
||||||
|
chatId: "-1001234567890",
|
||||||
|
messageThreadId: 42,
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
),
|
||||||
|
).rejects.toThrow(/At least one of name or iconCustomEmojiId is required/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("requires chatId and messageThreadId", async () => {
|
||||||
|
const cfg = {
|
||||||
|
channels: { telegram: { botToken: "tok" } },
|
||||||
|
} as MoltbotConfig;
|
||||||
|
await expect(
|
||||||
|
handleTelegramAction(
|
||||||
|
{
|
||||||
|
action: "editForumTopic",
|
||||||
|
name: "New Name",
|
||||||
|
},
|
||||||
|
cfg,
|
||||||
|
),
|
||||||
|
).rejects.toThrow(/chatId required/i);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("readTelegramButtons", () => {
|
describe("readTelegramButtons", () => {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { OpenClawConfig } from "../../config/config.js";
|
|||||||
import { resolveTelegramReactionLevel } from "../../telegram/reaction-level.js";
|
import { resolveTelegramReactionLevel } from "../../telegram/reaction-level.js";
|
||||||
import {
|
import {
|
||||||
deleteMessageTelegram,
|
deleteMessageTelegram,
|
||||||
|
editForumTopicTelegram,
|
||||||
editMessageTelegram,
|
editMessageTelegram,
|
||||||
reactMessageTelegram,
|
reactMessageTelegram,
|
||||||
sendMessageTelegram,
|
sendMessageTelegram,
|
||||||
@ -318,5 +319,36 @@ export async function handleTelegramAction(
|
|||||||
return jsonResult({ ok: true, ...stats });
|
return jsonResult({ ok: true, ...stats });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action === "editForumTopic") {
|
||||||
|
if (!isActionEnabled("editForumTopic")) {
|
||||||
|
throw new Error("Telegram editForumTopic is disabled.");
|
||||||
|
}
|
||||||
|
const chatId = readStringOrNumberParam(params, "chatId", {
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||||
|
required: true,
|
||||||
|
integer: true,
|
||||||
|
});
|
||||||
|
const name = readStringParam(params, "name");
|
||||||
|
const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId");
|
||||||
|
if (!name && !iconCustomEmojiId) {
|
||||||
|
throw new Error("At least one of name or iconCustomEmojiId is required.");
|
||||||
|
}
|
||||||
|
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||||
|
if (!token) {
|
||||||
|
throw new Error(
|
||||||
|
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await editForumTopicTelegram(chatId ?? "", messageThreadId ?? 0, {
|
||||||
|
token,
|
||||||
|
accountId: accountId ?? undefined,
|
||||||
|
name: name ?? undefined,
|
||||||
|
iconCustomEmojiId: iconCustomEmojiId ?? undefined,
|
||||||
|
});
|
||||||
|
return jsonResult({ ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`Unsupported Telegram action: ${action}`);
|
throw new Error(`Unsupported Telegram action: ${action}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,8 @@ export type TelegramActionConfig = {
|
|||||||
sendMessage?: boolean;
|
sendMessage?: boolean;
|
||||||
deleteMessage?: boolean;
|
deleteMessage?: boolean;
|
||||||
editMessage?: boolean;
|
editMessage?: boolean;
|
||||||
|
/** Enable editing forum topic name/icon. */
|
||||||
|
editForumTopic?: boolean;
|
||||||
/** Enable sticker actions (send and search). */
|
/** Enable sticker actions (send and search). */
|
||||||
sticker?: boolean;
|
sticker?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -630,6 +630,14 @@ function inferFilename(kind: ReturnType<typeof mediaKindFromMime>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TelegramEditForumTopicOpts = {
|
||||||
|
token?: string;
|
||||||
|
accountId?: string;
|
||||||
|
verbose?: boolean;
|
||||||
|
api?: Bot["api"];
|
||||||
|
retry?: RetryConfig;
|
||||||
|
};
|
||||||
|
|
||||||
type TelegramStickerOpts = {
|
type TelegramStickerOpts = {
|
||||||
token?: string;
|
token?: string;
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
@ -642,6 +650,62 @@ type TelegramStickerOpts = {
|
|||||||
messageThreadId?: number;
|
messageThreadId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function editForumTopicTelegram(
|
||||||
|
chatIdInput: string | number,
|
||||||
|
messageThreadId: number,
|
||||||
|
opts: TelegramEditForumTopicOpts & {
|
||||||
|
name?: string;
|
||||||
|
iconCustomEmojiId?: string;
|
||||||
|
} = {},
|
||||||
|
): Promise<{ ok: true }> {
|
||||||
|
const cfg = loadConfig();
|
||||||
|
const account = resolveTelegramAccount({
|
||||||
|
cfg,
|
||||||
|
accountId: opts.accountId,
|
||||||
|
});
|
||||||
|
const token = resolveToken(opts.token, account);
|
||||||
|
const chatId = normalizeChatId(String(chatIdInput));
|
||||||
|
const client = resolveTelegramClientOptions(account);
|
||||||
|
const api = opts.api ?? new Bot(token, client ? { client } : undefined).api;
|
||||||
|
const request = createTelegramRetryRunner({
|
||||||
|
retry: opts.retry,
|
||||||
|
configRetry: account.config.retry,
|
||||||
|
verbose: opts.verbose,
|
||||||
|
shouldRetry: (err) => isRecoverableTelegramNetworkError(err, { context: "send" }),
|
||||||
|
});
|
||||||
|
const logHttpError = createTelegramHttpLogger(cfg);
|
||||||
|
const requestWithDiag = <T>(fn: () => Promise<T>, label?: string) =>
|
||||||
|
withTelegramApiErrorLogging({
|
||||||
|
operation: label ?? "request",
|
||||||
|
fn: () => request(fn, label),
|
||||||
|
}).catch((err) => {
|
||||||
|
logHttpError(label ?? "request", err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
// The General topic (thread_id = 1) uses a different API method
|
||||||
|
if (messageThreadId === 1) {
|
||||||
|
if (!opts.name) {
|
||||||
|
throw new Error("name is required for editing the General forum topic");
|
||||||
|
}
|
||||||
|
await requestWithDiag(
|
||||||
|
() => api.editGeneralForumTopic(chatId, opts.name),
|
||||||
|
"editGeneralForumTopic",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const editParams: { name?: string; icon_custom_emoji_id?: string } = {};
|
||||||
|
if (opts.name) editParams.name = opts.name;
|
||||||
|
if (opts.iconCustomEmojiId) editParams.icon_custom_emoji_id = opts.iconCustomEmojiId;
|
||||||
|
await requestWithDiag(
|
||||||
|
() => api.editForumTopic(chatId, messageThreadId, editParams),
|
||||||
|
"editForumTopic",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
logVerbose(`[telegram] Edited forum topic ${messageThreadId} in chat ${chatId}`);
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a sticker to a Telegram chat by file_id.
|
* Send a sticker to a Telegram chat by file_id.
|
||||||
* @param to - Chat ID or username (e.g., "123456789" or "@username")
|
* @param to - Chat ID or username (e.g., "123456789" or "@username")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user