Merge c617fcbcc9 into 9025da2296
This commit is contained in:
commit
6212c73534
@ -188,6 +188,27 @@ function buildGatewaySchema() {
|
||||
};
|
||||
}
|
||||
|
||||
function buildForumTopicSchema() {
|
||||
return {
|
||||
iconColor: Type.Optional(
|
||||
Type.Number({
|
||||
description:
|
||||
"Topic icon color in RGB (e.g. 0x6FB9F0). Only for Telegram forum topic-create.",
|
||||
}),
|
||||
),
|
||||
iconCustomEmojiId: Type.Optional(
|
||||
Type.String({
|
||||
description: "Custom emoji id for the topic icon. For Telegram topic-create/topic-edit.",
|
||||
}),
|
||||
),
|
||||
messageThreadId: Type.Optional(
|
||||
Type.Number({
|
||||
description: "Forum topic thread id. For Telegram topic-edit/close/reopen/delete.",
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function buildChannelManagementSchema() {
|
||||
return {
|
||||
name: Type.Optional(Type.String()),
|
||||
@ -220,6 +241,7 @@ function buildMessageToolSchemaProps(options: { includeButtons: boolean; include
|
||||
...buildModerationSchema(),
|
||||
...buildGatewaySchema(),
|
||||
...buildChannelManagementSchema(),
|
||||
...buildForumTopicSchema(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,14 @@ import type { AgentToolResult } from "@mariozechner/pi-agent-core";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { resolveTelegramReactionLevel } from "../../telegram/reaction-level.js";
|
||||
import {
|
||||
closeForumTopicTelegram,
|
||||
createForumTopicTelegram,
|
||||
deleteForumTopicTelegram,
|
||||
deleteMessageTelegram,
|
||||
editForumTopicTelegram,
|
||||
editMessageTelegram,
|
||||
reactMessageTelegram,
|
||||
reopenForumTopicTelegram,
|
||||
sendMessageTelegram,
|
||||
sendStickerTelegram,
|
||||
} from "../../telegram/send.js";
|
||||
@ -318,5 +323,146 @@ export async function handleTelegramAction(
|
||||
return jsonResult({ ok: true, ...stats });
|
||||
}
|
||||
|
||||
if (action === "createForumTopic") {
|
||||
if (!isActionEnabled("forumTopics", false)) {
|
||||
throw new Error(
|
||||
"Telegram forum topic actions are disabled. Set channels.telegram.actions.forumTopics to true.",
|
||||
);
|
||||
}
|
||||
const chatId = readStringOrNumberParam(params, "chatId", {
|
||||
required: true,
|
||||
});
|
||||
const name = readStringParam(params, "name", { required: true });
|
||||
const iconColor = readNumberParam(params, "iconColor", { integer: true });
|
||||
const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId");
|
||||
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||
);
|
||||
}
|
||||
const result = await createForumTopicTelegram(chatId ?? "", name, {
|
||||
token,
|
||||
accountId: accountId ?? undefined,
|
||||
iconColor: iconColor ?? undefined,
|
||||
iconCustomEmojiId: iconCustomEmojiId ?? undefined,
|
||||
});
|
||||
return jsonResult({
|
||||
ok: true,
|
||||
messageThreadId: result.messageThreadId,
|
||||
name: result.name,
|
||||
iconColor: result.iconColor,
|
||||
iconCustomEmojiId: result.iconCustomEmojiId,
|
||||
});
|
||||
}
|
||||
|
||||
if (action === "editForumTopic") {
|
||||
if (!isActionEnabled("forumTopics", false)) {
|
||||
throw new Error(
|
||||
"Telegram forum topic actions are disabled. Set channels.telegram.actions.forumTopics to true.",
|
||||
);
|
||||
}
|
||||
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");
|
||||
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, edited: true });
|
||||
}
|
||||
|
||||
if (action === "closeForumTopic") {
|
||||
if (!isActionEnabled("forumTopics", false)) {
|
||||
throw new Error(
|
||||
"Telegram forum topic actions are disabled. Set channels.telegram.actions.forumTopics to true.",
|
||||
);
|
||||
}
|
||||
const chatId = readStringOrNumberParam(params, "chatId", {
|
||||
required: true,
|
||||
});
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||
);
|
||||
}
|
||||
await closeForumTopicTelegram(chatId ?? "", messageThreadId ?? 0, {
|
||||
token,
|
||||
accountId: accountId ?? undefined,
|
||||
});
|
||||
return jsonResult({ ok: true, closed: true });
|
||||
}
|
||||
|
||||
if (action === "reopenForumTopic") {
|
||||
if (!isActionEnabled("forumTopics", false)) {
|
||||
throw new Error(
|
||||
"Telegram forum topic actions are disabled. Set channels.telegram.actions.forumTopics to true.",
|
||||
);
|
||||
}
|
||||
const chatId = readStringOrNumberParam(params, "chatId", {
|
||||
required: true,
|
||||
});
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||
);
|
||||
}
|
||||
await reopenForumTopicTelegram(chatId ?? "", messageThreadId ?? 0, {
|
||||
token,
|
||||
accountId: accountId ?? undefined,
|
||||
});
|
||||
return jsonResult({ ok: true, reopened: true });
|
||||
}
|
||||
|
||||
if (action === "deleteForumTopic") {
|
||||
if (!isActionEnabled("forumTopics", false)) {
|
||||
throw new Error(
|
||||
"Telegram forum topic actions are disabled. Set channels.telegram.actions.forumTopics to true.",
|
||||
);
|
||||
}
|
||||
const chatId = readStringOrNumberParam(params, "chatId", {
|
||||
required: true,
|
||||
});
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
const token = resolveTelegramToken(cfg, { accountId }).token;
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
"Telegram bot token missing. Set TELEGRAM_BOT_TOKEN or channels.telegram.botToken.",
|
||||
);
|
||||
}
|
||||
await deleteForumTopicTelegram(chatId ?? "", messageThreadId ?? 0, {
|
||||
token,
|
||||
accountId: accountId ?? undefined,
|
||||
});
|
||||
return jsonResult({ ok: true, deleted: true });
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported Telegram action: ${action}`);
|
||||
}
|
||||
|
||||
@ -118,4 +118,209 @@ describe("telegramMessageActions", () => {
|
||||
|
||||
expect(handleTelegramAction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("excludes forum topic actions when not enabled", () => {
|
||||
const cfg = { channels: { telegram: { botToken: "tok" } } } as MoltbotConfig;
|
||||
const actions = telegramMessageActions.listActions({ cfg });
|
||||
expect(actions).not.toContain("topic-create");
|
||||
expect(actions).not.toContain("topic-edit");
|
||||
expect(actions).not.toContain("topic-close");
|
||||
expect(actions).not.toContain("topic-reopen");
|
||||
expect(actions).not.toContain("topic-delete");
|
||||
});
|
||||
|
||||
it("includes forum topic actions when forumTopics is enabled", () => {
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
const actions = telegramMessageActions.listActions({ cfg });
|
||||
expect(actions).toContain("topic-create");
|
||||
expect(actions).toContain("topic-edit");
|
||||
expect(actions).toContain("topic-close");
|
||||
expect(actions).toContain("topic-reopen");
|
||||
expect(actions).toContain("topic-delete");
|
||||
});
|
||||
|
||||
it("maps topic-create action to createForumTopic", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await telegramMessageActions.handleAction({
|
||||
action: "topic-create",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
name: "My New Topic",
|
||||
iconColor: 0x6fb9f0,
|
||||
iconCustomEmojiId: "emoji123",
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
});
|
||||
|
||||
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||
{
|
||||
action: "createForumTopic",
|
||||
chatId: "-1001234567890",
|
||||
name: "My New Topic",
|
||||
iconColor: 0x6fb9f0,
|
||||
iconCustomEmojiId: "emoji123",
|
||||
accountId: undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
});
|
||||
|
||||
it("maps topic-edit action to editForumTopic", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await telegramMessageActions.handleAction({
|
||||
action: "topic-edit",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
name: "Renamed Topic",
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
});
|
||||
|
||||
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||
{
|
||||
action: "editForumTopic",
|
||||
chatId: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
name: "Renamed Topic",
|
||||
iconCustomEmojiId: undefined,
|
||||
accountId: undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
});
|
||||
|
||||
it("maps topic-close action to closeForumTopic", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await telegramMessageActions.handleAction({
|
||||
action: "topic-close",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
});
|
||||
|
||||
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||
{
|
||||
action: "closeForumTopic",
|
||||
chatId: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
accountId: undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
});
|
||||
|
||||
it("maps topic-reopen action to reopenForumTopic", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await telegramMessageActions.handleAction({
|
||||
action: "topic-reopen",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
});
|
||||
|
||||
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||
{
|
||||
action: "reopenForumTopic",
|
||||
chatId: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
accountId: undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
});
|
||||
|
||||
it("maps topic-delete action to deleteForumTopic", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await telegramMessageActions.handleAction({
|
||||
action: "topic-delete",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
});
|
||||
|
||||
expect(handleTelegramAction).toHaveBeenCalledWith(
|
||||
{
|
||||
action: "deleteForumTopic",
|
||||
chatId: "-1001234567890",
|
||||
messageThreadId: 42,
|
||||
accountId: undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
});
|
||||
|
||||
it("requires messageThreadId for topic-edit", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await expect(
|
||||
telegramMessageActions.handleAction({
|
||||
action: "topic-edit",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
name: "Renamed",
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
}),
|
||||
).rejects.toThrow();
|
||||
|
||||
expect(handleTelegramAction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requires name for topic-create", async () => {
|
||||
handleTelegramAction.mockClear();
|
||||
const cfg = {
|
||||
channels: { telegram: { botToken: "tok", actions: { forumTopics: true } } },
|
||||
} as MoltbotConfig;
|
||||
|
||||
await expect(
|
||||
telegramMessageActions.handleAction({
|
||||
action: "topic-create",
|
||||
params: {
|
||||
to: "-1001234567890",
|
||||
},
|
||||
cfg,
|
||||
accountId: undefined,
|
||||
}),
|
||||
).rejects.toThrow();
|
||||
|
||||
expect(handleTelegramAction).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -52,6 +52,13 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
|
||||
actions.add("sticker");
|
||||
actions.add("sticker-search");
|
||||
}
|
||||
if (gate("forumTopics", false)) {
|
||||
actions.add("topic-create");
|
||||
actions.add("topic-edit");
|
||||
actions.add("topic-close");
|
||||
actions.add("topic-reopen");
|
||||
actions.add("topic-delete");
|
||||
}
|
||||
return Array.from(actions);
|
||||
},
|
||||
supportsButtons: ({ cfg }) => {
|
||||
@ -183,6 +190,106 @@ export const telegramMessageActions: ChannelMessageActionAdapter = {
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "topic-create") {
|
||||
const chatId =
|
||||
readStringOrNumberParam(params, "chatId") ??
|
||||
readStringParam(params, "to", { required: true });
|
||||
const name = readStringParam(params, "name", { required: true });
|
||||
const iconColor = readNumberParam(params, "iconColor", { integer: true });
|
||||
const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId");
|
||||
return await handleTelegramAction(
|
||||
{
|
||||
action: "createForumTopic",
|
||||
chatId,
|
||||
name,
|
||||
iconColor: iconColor ?? undefined,
|
||||
iconCustomEmojiId: iconCustomEmojiId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "topic-edit") {
|
||||
const chatId =
|
||||
readStringOrNumberParam(params, "chatId") ??
|
||||
readStringParam(params, "to", { required: true });
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
const name = readStringParam(params, "name");
|
||||
const iconCustomEmojiId = readStringParam(params, "iconCustomEmojiId");
|
||||
return await handleTelegramAction(
|
||||
{
|
||||
action: "editForumTopic",
|
||||
chatId,
|
||||
messageThreadId,
|
||||
name: name ?? undefined,
|
||||
iconCustomEmojiId: iconCustomEmojiId ?? undefined,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "topic-close") {
|
||||
const chatId =
|
||||
readStringOrNumberParam(params, "chatId") ??
|
||||
readStringParam(params, "to", { required: true });
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
return await handleTelegramAction(
|
||||
{
|
||||
action: "closeForumTopic",
|
||||
chatId,
|
||||
messageThreadId,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "topic-reopen") {
|
||||
const chatId =
|
||||
readStringOrNumberParam(params, "chatId") ??
|
||||
readStringParam(params, "to", { required: true });
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
return await handleTelegramAction(
|
||||
{
|
||||
action: "reopenForumTopic",
|
||||
chatId,
|
||||
messageThreadId,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
if (action === "topic-delete") {
|
||||
const chatId =
|
||||
readStringOrNumberParam(params, "chatId") ??
|
||||
readStringParam(params, "to", { required: true });
|
||||
const messageThreadId = readNumberParam(params, "messageThreadId", {
|
||||
required: true,
|
||||
integer: true,
|
||||
});
|
||||
return await handleTelegramAction(
|
||||
{
|
||||
action: "deleteForumTopic",
|
||||
chatId,
|
||||
messageThreadId,
|
||||
accountId: accountId ?? undefined,
|
||||
},
|
||||
cfg,
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(`Action ${action} is not supported for provider ${providerId}.`);
|
||||
},
|
||||
};
|
||||
|
||||
@ -48,6 +48,11 @@ export const CHANNEL_MESSAGE_ACTION_NAMES = [
|
||||
"timeout",
|
||||
"kick",
|
||||
"ban",
|
||||
"topic-create",
|
||||
"topic-edit",
|
||||
"topic-close",
|
||||
"topic-reopen",
|
||||
"topic-delete",
|
||||
] as const;
|
||||
|
||||
export type ChannelMessageActionName = (typeof CHANNEL_MESSAGE_ACTION_NAMES)[number];
|
||||
|
||||
@ -18,6 +18,8 @@ export type TelegramActionConfig = {
|
||||
editMessage?: boolean;
|
||||
/** Enable sticker actions (send and search). */
|
||||
sticker?: boolean;
|
||||
/** Enable forum topic management actions (create, edit, close, reopen, delete). */
|
||||
forumTopics?: boolean;
|
||||
};
|
||||
|
||||
export type TelegramNetworkConfig = {
|
||||
|
||||
@ -53,6 +53,11 @@ export const MESSAGE_ACTION_TARGET_MODE: Record<ChannelMessageActionName, Messag
|
||||
timeout: "none",
|
||||
kick: "none",
|
||||
ban: "none",
|
||||
"topic-create": "to",
|
||||
"topic-edit": "to",
|
||||
"topic-close": "to",
|
||||
"topic-reopen": "to",
|
||||
"topic-delete": "to",
|
||||
};
|
||||
|
||||
const ACTION_TARGET_ALIASES: Partial<Record<ChannelMessageActionName, string[]>> = {
|
||||
|
||||
@ -722,3 +722,236 @@ export async function sendStickerTelegram(
|
||||
|
||||
return { messageId, chatId: resolvedChatId };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Forum topic management
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
type TelegramForumTopicOpts = {
|
||||
token?: string;
|
||||
accountId?: string;
|
||||
verbose?: boolean;
|
||||
api?: Bot["api"];
|
||||
retry?: RetryConfig;
|
||||
};
|
||||
|
||||
type TelegramCreateForumTopicResult = {
|
||||
messageThreadId: number;
|
||||
name: string;
|
||||
iconColor: number;
|
||||
iconCustomEmojiId?: string;
|
||||
};
|
||||
|
||||
export async function createForumTopicTelegram(
|
||||
chatIdInput: string | number,
|
||||
name: string,
|
||||
opts: TelegramForumTopicOpts & {
|
||||
iconColor?: number;
|
||||
iconCustomEmojiId?: string;
|
||||
} = {},
|
||||
): Promise<TelegramCreateForumTopicResult> {
|
||||
if (!name?.trim()) {
|
||||
throw new Error("Forum topic name is required (1-128 characters)");
|
||||
}
|
||||
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;
|
||||
});
|
||||
|
||||
const other: Record<string, unknown> = {};
|
||||
if (opts.iconColor != null) other.icon_color = opts.iconColor;
|
||||
if (opts.iconCustomEmojiId) other.icon_custom_emoji_id = opts.iconCustomEmojiId;
|
||||
|
||||
const result = await requestWithDiag(
|
||||
() =>
|
||||
api.createForumTopic(chatId, name.trim(), Object.keys(other).length > 0 ? other : undefined),
|
||||
"createForumTopic",
|
||||
);
|
||||
|
||||
logVerbose(`[telegram] Created forum topic "${name}" in chat ${chatId}`);
|
||||
return {
|
||||
messageThreadId: result.message_thread_id,
|
||||
name: result.name,
|
||||
iconColor: result.icon_color,
|
||||
iconCustomEmojiId: result.icon_custom_emoji_id,
|
||||
};
|
||||
}
|
||||
|
||||
export async function editForumTopicTelegram(
|
||||
chatIdInput: string | number,
|
||||
messageThreadId: number,
|
||||
opts: TelegramForumTopicOpts & {
|
||||
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;
|
||||
});
|
||||
|
||||
const other: Record<string, unknown> = {};
|
||||
if (opts.name != null) other.name = opts.name.trim();
|
||||
if (opts.iconCustomEmojiId != null) other.icon_custom_emoji_id = opts.iconCustomEmojiId;
|
||||
|
||||
await requestWithDiag(
|
||||
() =>
|
||||
api.editForumTopic(
|
||||
chatId,
|
||||
messageThreadId,
|
||||
Object.keys(other).length > 0 ? other : undefined,
|
||||
),
|
||||
"editForumTopic",
|
||||
);
|
||||
|
||||
logVerbose(`[telegram] Edited forum topic ${messageThreadId} in chat ${chatId}`);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function closeForumTopicTelegram(
|
||||
chatIdInput: string | number,
|
||||
messageThreadId: number,
|
||||
opts: TelegramForumTopicOpts = {},
|
||||
): 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;
|
||||
});
|
||||
|
||||
await requestWithDiag(() => api.closeForumTopic(chatId, messageThreadId), "closeForumTopic");
|
||||
|
||||
logVerbose(`[telegram] Closed forum topic ${messageThreadId} in chat ${chatId}`);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function reopenForumTopicTelegram(
|
||||
chatIdInput: string | number,
|
||||
messageThreadId: number,
|
||||
opts: TelegramForumTopicOpts = {},
|
||||
): 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;
|
||||
});
|
||||
|
||||
await requestWithDiag(() => api.reopenForumTopic(chatId, messageThreadId), "reopenForumTopic");
|
||||
|
||||
logVerbose(`[telegram] Reopened forum topic ${messageThreadId} in chat ${chatId}`);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function deleteForumTopicTelegram(
|
||||
chatIdInput: string | number,
|
||||
messageThreadId: number,
|
||||
opts: TelegramForumTopicOpts = {},
|
||||
): 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;
|
||||
});
|
||||
|
||||
await requestWithDiag(() => api.deleteForumTopic(chatId, messageThreadId), "deleteForumTopic");
|
||||
|
||||
logVerbose(`[telegram] Deleted forum topic ${messageThreadId} in chat ${chatId}`);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user