fix(telegram): split long messages at 4096 chars to avoid truncation
- Add TELEGRAM_MAX_TEXT and _chunkText(); send multiple messages when over limit - Prefer newline boundaries to avoid breaking Markdown Made-with: Cursor
This commit is contained in:
committed by
satoshipanic
parent
2a0b73e5a6
commit
28121298cf
@@ -4,6 +4,8 @@
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
const TELEGRAM_API = 'https://api.telegram.org';
|
||||
/** Telegram Bot API limit for sendMessage text (bytes/characters). */
|
||||
const TELEGRAM_MAX_TEXT = 4096;
|
||||
|
||||
// ─── Alert Tiers ────────────────────────────────────────────────────────────
|
||||
// FLASH: Immediate action required — market-moving, time-critical (e.g. war escalation, flash crash)
|
||||
@@ -48,42 +50,70 @@ export class TelegramAlerter {
|
||||
// ─── Core Messaging ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Send a message via Telegram Bot API.
|
||||
* Send a message via Telegram Bot API. Splits at TELEGRAM_MAX_TEXT so long messages
|
||||
* (e.g. /brief) are sent in multiple messages instead of being truncated or failing.
|
||||
* @param {string} message - markdown-formatted message
|
||||
* @param {object} opts - optional: { parseMode, disablePreview, replyToMessageId }
|
||||
* @param {object} opts - optional: { parseMode, disablePreview, replyToMessageId, chatId }
|
||||
* @returns {Promise<{ok: boolean, messageId?: number}>}
|
||||
*/
|
||||
async sendMessage(message, opts = {}) {
|
||||
if (!this.isConfigured) return { ok: false };
|
||||
const chatId = opts.chatId ?? this.chatId;
|
||||
const parseMode = opts.parseMode || 'Markdown';
|
||||
const chunks = this._chunkText(message, TELEGRAM_MAX_TEXT);
|
||||
|
||||
try {
|
||||
const res = await fetch(`${TELEGRAM_API}/bot${this.botToken}/sendMessage`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chat_id: opts.chatId ?? this.chatId,
|
||||
text: message,
|
||||
parse_mode: opts.parseMode || 'Markdown',
|
||||
disable_web_page_preview: opts.disablePreview !== false,
|
||||
...(opts.replyToMessageId ? { reply_to_message_id: opts.replyToMessageId } : {}),
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
let lastResult = { ok: false, messageId: undefined };
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
const res = await fetch(`${TELEGRAM_API}/bot${this.botToken}/sendMessage`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chat_id: chatId,
|
||||
text: chunks[i],
|
||||
parse_mode: parseMode,
|
||||
disable_web_page_preview: opts.disablePreview !== false,
|
||||
...(opts.replyToMessageId && i === 0 ? { reply_to_message_id: opts.replyToMessageId } : {}),
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.text().catch(() => '');
|
||||
console.error(`[Telegram] Send failed (${res.status}): ${err.substring(0, 200)}`);
|
||||
return { ok: false };
|
||||
if (!res.ok) {
|
||||
const err = await res.text().catch(() => '');
|
||||
console.error(`[Telegram] Send failed (${res.status}): ${err.substring(0, 200)}`);
|
||||
return lastResult;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
lastResult = { ok: true, messageId: data.result?.message_id };
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
return { ok: true, messageId: data.result?.message_id };
|
||||
return lastResult;
|
||||
} catch (err) {
|
||||
console.error('[Telegram] Send error:', err.message);
|
||||
return { ok: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split text into chunks of at most maxLen. Prefer breaking at newlines to avoid
|
||||
* splitting mid-Markdown.
|
||||
*/
|
||||
_chunkText(text, maxLen = TELEGRAM_MAX_TEXT) {
|
||||
if (!text || text.length <= maxLen) return text ? [text] : [];
|
||||
const chunks = [];
|
||||
let start = 0;
|
||||
while (start < text.length) {
|
||||
let end = Math.min(start + maxLen, text.length);
|
||||
if (end < text.length) {
|
||||
const lastNewline = text.lastIndexOf('\n', end - 1);
|
||||
if (lastNewline > start) end = lastNewline + 1;
|
||||
}
|
||||
chunks.push(text.slice(start, end));
|
||||
start = end;
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
// Backward-compatible alias
|
||||
async sendAlert(message) {
|
||||
const result = await this.sendMessage(message);
|
||||
|
||||
Reference in New Issue
Block a user