feat: 添加 Telegram 通知支持,更新配置和文档

This commit is contained in:
cita
2026-03-03 17:25:27 +08:00
parent cf6b2285fb
commit 9ff138f0b5
13 changed files with 295 additions and 4 deletions
+35
View File
@@ -32,6 +32,9 @@ describe('notifyService', () => {
config.barkUrl = '';
config.serverChanEnabled = false;
config.serverChanKey = '';
(config as any).telegramEnabled = false;
(config as any).telegramBotToken = '';
(config as any).telegramChatId = '';
config.smtpEnabled = true;
config.smtpHost = 'smtp.example.com';
config.smtpPort = 465;
@@ -116,4 +119,36 @@ describe('notifyService', () => {
expect(payload?.text || '').toContain('Local Time:');
expect(payload?.text || '').toContain('UTC Time:');
});
it('sends telegram message when telegram channel is enabled', async () => {
const { config } = await import('../config.js');
(config as any).telegramEnabled = true;
(config as any).telegramBotToken = '123456:telegram-token';
(config as any).telegramChatId = '-1001234567890';
config.smtpEnabled = false;
fetchMock.mockResolvedValue({
ok: true,
status: 200,
});
const { sendNotification } = await import('./notifyService.js');
await sendNotification('测试通知', 'message', 'warning', { bypassThrottle: true, throwOnFailure: true });
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith(
'https://api.telegram.org/bot123456:telegram-token/sendMessage',
expect.objectContaining({
method: 'POST',
headers: { 'Content-Type': 'application/json' },
}),
);
const rawBody = fetchMock.mock.calls[0]?.[1] as { body?: string };
const payload = JSON.parse(rawBody?.body || '{}') as { chat_id?: string; text?: string };
expect(payload.chat_id).toBe('-1001234567890');
expect(payload.text || '').toContain('Level: warning');
expect(payload.text || '').toContain('Local Time:');
expect(payload.text || '').toContain('UTC Time:');
});
});
+42 -1
View File
@@ -9,7 +9,7 @@ import {
} from './notificationThrottle.js';
import { formatLocalDateTime, getResolvedTimeZone } from './localTimeService.js';
type NotificationChannel = 'webhook' | 'bark' | 'serverchan' | 'smtp';
type NotificationChannel = 'webhook' | 'bark' | 'serverchan' | 'telegram' | 'smtp';
export type SendNotificationOptions = {
bypassThrottle?: boolean;
@@ -70,6 +70,18 @@ function buildTimeFootnote(now: Date): string {
].join('\n');
}
function buildTelegramText(
title: string,
message: string,
level: 'info' | 'warning' | 'error',
timeFootnote: string,
): string {
const maxTextLength = 3900;
const raw = `[metapi][${level.toUpperCase()}] ${title}\n\n${message}\n\nLevel: ${level}\n${timeFootnote}`;
if (raw.length <= maxTextLength) return raw;
return `${raw.slice(0, maxTextLength)}\n\n...(truncated)`;
}
export async function sendNotification(
title: string,
message: string,
@@ -163,6 +175,35 @@ export async function sendNotification(
);
}
if (config.telegramEnabled && config.telegramBotToken && config.telegramChatId) {
const telegramApiUrl = `https://api.telegram.org/bot${config.telegramBotToken}/sendMessage`;
const text = buildTelegramText(title, resolvedMessage, level, timeFootnote);
tasks.push({
channel: 'telegram',
run: async () => {
const response = await fetch(telegramApiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
chat_id: config.telegramChatId,
text,
disable_web_page_preview: true,
}),
});
if (!response.ok) {
throw new Error(`Telegram 响应状态 ${response.status}`);
}
let payload: { ok?: boolean; description?: string } | null = null;
try {
payload = await response.json() as { ok?: boolean; description?: string };
} catch {}
if (payload?.ok === false) {
throw new Error(payload.description || 'Telegram 返回失败');
}
},
});
}
if (
config.smtpEnabled &&
config.smtpHost &&