Enhance Telegram notification functionality

- Introduced functions to mask sensitive information and preview notification text for improved logging and security.
- Added detailed logging for notification sending process, including configuration details and response handling.
- Implemented checks to prevent sending notifications when bot token or chat ID is missing, with appropriate warnings logged.
This commit is contained in:
discountry
2026-01-12 12:12:14 +08:00
parent 598f2a0eb6
commit aad14395e0
+57 -3
View File
@@ -21,6 +21,30 @@ const TYPE_EMOJI: Record<string, string> = {
custom: "📢", 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 { function formatNotificationMessage(notification: TradeNotification, accountLabel?: string): string {
const levelEmoji = LEVEL_EMOJI[notification.level] ?? ""; const levelEmoji = LEVEL_EMOJI[notification.level] ?? "";
const typeEmoji = TYPE_EMOJI[notification.type] ?? ""; const typeEmoji = TYPE_EMOJI[notification.type] ?? "";
@@ -55,6 +79,7 @@ export class TelegramNotifier implements NotificationSender {
private readonly config: TelegramConfig; private readonly config: TelegramConfig;
private readonly baseUrl: string; private readonly baseUrl: string;
private sendQueue: Promise<void> = Promise.resolve(); private sendQueue: Promise<void> = Promise.resolve();
private disabledLogged = false;
constructor(config: Partial<TelegramConfig> = {}) { constructor(config: Partial<TelegramConfig> = {}) {
this.config = { this.config = {
@@ -64,6 +89,12 @@ export class TelegramNotifier implements NotificationSender {
accountLabel: config.accountLabel, accountLabel: config.accountLabel,
}; };
this.baseUrl = `https://api.telegram.org/bot${this.config.botToken}`; 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 { isEnabled(): boolean {
@@ -72,9 +103,22 @@ export class TelegramNotifier implements NotificationSender {
async send(notification: TradeNotification): Promise<void> { async send(notification: TradeNotification): Promise<void> {
if (!this.isEnabled()) { 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; 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 this.sendQueue = this.sendQueue
.then(() => this.doSend(notification)) .then(() => this.doSend(notification))
.catch(() => {}); .catch(() => {});
@@ -85,6 +129,11 @@ export class TelegramNotifier implements NotificationSender {
const url = `${this.baseUrl}/sendMessage`; const url = `${this.baseUrl}/sendMessage`;
try { try {
console.info(`${LOG_PREFIX} Sending notification`, {
chatId: maskChatId(this.config.chatId),
textLength: text.length,
textPreview: previewText(text),
});
const response = await fetch(url, { const response = await fetch(url, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, 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) { if (!response.ok) {
const errorText = await response.text().catch(() => "unknown error"); console.error(`${LOG_PREFIX} Failed to send notification: ${response.status} ${responseText}`);
console.error(`[Telegram] Failed to send notification: ${response.status} ${errorText}`);
} }
} catch (error) { } 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)}`);
} }
} }
} }