diff --git a/src/notifications/telegram.ts b/src/notifications/telegram.ts index 88a00e2..c71b902 100644 --- a/src/notifications/telegram.ts +++ b/src/notifications/telegram.ts @@ -21,6 +21,30 @@ const TYPE_EMOJI: Record = { custom: "📢", }; +const LOG_PREFIX = "[Telegram]"; +const PREVIEW_LIMIT = 240; + +function maskSecret(value: string, revealStart = 4, revealEnd = 4): string { + if (!value) return "missing"; + if (value.length <= revealStart + revealEnd + 2) { + return `${value.slice(0, 1)}...${value.slice(-1)} (len=${value.length})`; + } + return `${value.slice(0, revealStart)}...${value.slice(-revealEnd)} (len=${value.length})`; +} + +function maskChatId(value: string): string { + if (!value) return "missing"; + if (value.length <= 4) return `***${value}`; + return `***${value.slice(-4)}`; +} + +function previewText(text: string): string { + if (text.length <= PREVIEW_LIMIT) { + return text; + } + return `${text.slice(0, PREVIEW_LIMIT)}...`; +} + function formatNotificationMessage(notification: TradeNotification, accountLabel?: string): string { const levelEmoji = LEVEL_EMOJI[notification.level] ?? ""; const typeEmoji = TYPE_EMOJI[notification.type] ?? ""; @@ -55,6 +79,7 @@ export class TelegramNotifier implements NotificationSender { private readonly config: TelegramConfig; private readonly baseUrl: string; private sendQueue: Promise = Promise.resolve(); + private disabledLogged = false; constructor(config: Partial = {}) { this.config = { @@ -64,6 +89,12 @@ export class TelegramNotifier implements NotificationSender { accountLabel: config.accountLabel, }; this.baseUrl = `https://api.telegram.org/bot${this.config.botToken}`; + console.info(`${LOG_PREFIX} Notifier config`, { + enabled: this.config.enabled, + botToken: maskSecret(this.config.botToken), + chatId: maskChatId(this.config.chatId), + accountLabel: this.config.accountLabel ?? "none", + }); } isEnabled(): boolean { @@ -72,9 +103,22 @@ export class TelegramNotifier implements NotificationSender { async send(notification: TradeNotification): Promise { if (!this.isEnabled()) { + if (!this.disabledLogged) { + console.warn(`${LOG_PREFIX} Skipping send (missing bot token or chat id).`, { + botToken: maskSecret(this.config.botToken), + chatId: maskChatId(this.config.chatId), + }); + this.disabledLogged = true; + } return; } + console.info(`${LOG_PREFIX} Queueing notification`, { + type: notification.type, + level: notification.level, + title: notification.title, + chatId: maskChatId(this.config.chatId), + }); this.sendQueue = this.sendQueue .then(() => this.doSend(notification)) .catch(() => {}); @@ -85,6 +129,11 @@ export class TelegramNotifier implements NotificationSender { const url = `${this.baseUrl}/sendMessage`; try { + console.info(`${LOG_PREFIX} Sending notification`, { + chatId: maskChatId(this.config.chatId), + textLength: text.length, + textPreview: previewText(text), + }); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -94,12 +143,17 @@ export class TelegramNotifier implements NotificationSender { }), }); + const responseText = await response.text().catch(() => "unknown response"); + console.info(`${LOG_PREFIX} Response`, { + ok: response.ok, + status: response.status, + body: responseText, + }); if (!response.ok) { - const errorText = await response.text().catch(() => "unknown error"); - console.error(`[Telegram] Failed to send notification: ${response.status} ${errorText}`); + console.error(`${LOG_PREFIX} Failed to send notification: ${response.status} ${responseText}`); } } catch (error) { - console.error(`[Telegram] Failed to send notification: ${error instanceof Error ? error.message : String(error)}`); + console.error(`${LOG_PREFIX} Failed to send notification: ${error instanceof Error ? error.message : String(error)}`); } } }