diff --git a/src/channels/telegram.ts b/src/channels/telegram.ts index 4176f03..c7d19e5 100644 --- a/src/channels/telegram.ts +++ b/src/channels/telegram.ts @@ -1,4 +1,4 @@ -import { Bot } from 'grammy'; +import { Api, Bot } from 'grammy'; import { ASSISTANT_NAME, TRIGGER_PATTERN } from '../config.js'; import { readEnvFile } from '../env.js'; @@ -17,6 +17,29 @@ export interface TelegramChannelOpts { registeredGroups: () => Record; } +/** + * Send a message with Telegram Markdown parse mode, falling back to plain text. + * Claude's output naturally matches Telegram's Markdown v1 format: + * *bold*, _italic_, `code`, ```code blocks```, [links](url) + */ +async function sendTelegramMessage( + api: { sendMessage: Api['sendMessage'] }, + chatId: string | number, + text: string, + options: { message_thread_id?: number } = {}, +): Promise { + try { + await api.sendMessage(chatId, text, { + ...options, + parse_mode: 'Markdown', + }); + } catch (err) { + // Fallback: send as plain text if Markdown parsing fails + logger.debug({ err }, 'Markdown send failed, falling back to plain text'); + await api.sendMessage(chatId, text, options); + } +} + export class TelegramChannel implements Channel { name = 'telegram'; @@ -203,10 +226,11 @@ export class TelegramChannel implements Channel { // Telegram has a 4096 character limit per message — split if needed const MAX_LENGTH = 4096; if (text.length <= MAX_LENGTH) { - await this.bot.api.sendMessage(numericId, text); + await sendTelegramMessage(this.bot.api, numericId, text); } else { for (let i = 0; i < text.length; i += MAX_LENGTH) { - await this.bot.api.sendMessage( + await sendTelegramMessage( + this.bot.api, numericId, text.slice(i, i + MAX_LENGTH), );