forked from boos/BoosAPI
feat: 添加 Telegram 通知支持,更新配置和文档
This commit is contained in:
@@ -132,6 +132,38 @@ describe('settings and auth events', () => {
|
||||
expect(body.message).toContain('Webhook URL');
|
||||
});
|
||||
|
||||
it('rejects telegram config when bot token is missing but telegram is enabled', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'PUT',
|
||||
url: '/api/settings/runtime',
|
||||
payload: {
|
||||
telegramEnabled: true,
|
||||
telegramChatId: '-1001234567890',
|
||||
telegramBotToken: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
const body = response.json() as { message?: string };
|
||||
expect(body.message).toContain('Telegram Bot Token');
|
||||
});
|
||||
|
||||
it('rejects telegram config when chat id is missing but telegram is enabled', async () => {
|
||||
const response = await app.inject({
|
||||
method: 'PUT',
|
||||
url: '/api/settings/runtime',
|
||||
payload: {
|
||||
telegramEnabled: true,
|
||||
telegramBotToken: '123456:telegram-token',
|
||||
telegramChatId: '',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
const body = response.json() as { message?: string };
|
||||
expect(body.message).toContain('Telegram Chat ID');
|
||||
});
|
||||
|
||||
it('persists and returns routing fallback unit cost from runtime settings', async () => {
|
||||
const updateResponse = await app.inject({
|
||||
method: 'PUT',
|
||||
|
||||
@@ -21,6 +21,9 @@ interface RuntimeSettingsBody {
|
||||
barkEnabled?: boolean;
|
||||
serverChanEnabled?: boolean;
|
||||
serverChanKey?: string;
|
||||
telegramEnabled?: boolean;
|
||||
telegramBotToken?: string;
|
||||
telegramChatId?: string;
|
||||
smtpEnabled?: boolean;
|
||||
smtpHost?: string;
|
||||
smtpPort?: number;
|
||||
@@ -155,6 +158,20 @@ function applyImportedSettingToRuntime(key: string, value: unknown) {
|
||||
config.serverChanKey = value.trim();
|
||||
return;
|
||||
}
|
||||
case 'telegram_enabled': {
|
||||
config.telegramEnabled = !!value;
|
||||
return;
|
||||
}
|
||||
case 'telegram_bot_token': {
|
||||
if (typeof value !== 'string') return;
|
||||
config.telegramBotToken = value.trim();
|
||||
return;
|
||||
}
|
||||
case 'telegram_chat_id': {
|
||||
if (typeof value !== 'string') return;
|
||||
config.telegramChatId = value.trim();
|
||||
return;
|
||||
}
|
||||
case 'smtp_enabled': {
|
||||
config.smtpEnabled = !!value;
|
||||
return;
|
||||
@@ -239,6 +256,9 @@ function getRuntimeSettingsResponse(currentAdminIp = '') {
|
||||
barkEnabled: config.barkEnabled,
|
||||
serverChanEnabled: config.serverChanEnabled,
|
||||
serverChanKeyMasked: maskSecret(config.serverChanKey),
|
||||
telegramEnabled: config.telegramEnabled,
|
||||
telegramBotTokenMasked: maskSecret(config.telegramBotToken),
|
||||
telegramChatId: config.telegramChatId,
|
||||
smtpEnabled: config.smtpEnabled,
|
||||
smtpHost: config.smtpHost,
|
||||
smtpPort: config.smtpPort,
|
||||
@@ -297,6 +317,30 @@ export async function settingsRoutes(app: FastifyInstance) {
|
||||
}
|
||||
}
|
||||
|
||||
const telegramTouched = body.telegramEnabled !== undefined
|
||||
|| body.telegramBotToken !== undefined
|
||||
|| body.telegramChatId !== undefined;
|
||||
const nextTelegramEnabled = body.telegramEnabled !== undefined
|
||||
? !!body.telegramEnabled
|
||||
: config.telegramEnabled;
|
||||
const nextTelegramBotToken = body.telegramBotToken !== undefined
|
||||
? String(body.telegramBotToken || '').trim()
|
||||
: config.telegramBotToken;
|
||||
const nextTelegramChatId = body.telegramChatId !== undefined
|
||||
? String(body.telegramChatId || '').trim()
|
||||
: config.telegramChatId;
|
||||
if (telegramTouched && nextTelegramEnabled) {
|
||||
if (!nextTelegramBotToken) {
|
||||
return reply.code(400).send({ success: false, message: 'Telegram Bot Token 不能为空(启用 Telegram 时)' });
|
||||
}
|
||||
if (!nextTelegramBotToken.includes(':')) {
|
||||
return reply.code(400).send({ success: false, message: 'Telegram Bot Token 格式无效(示例:123456:abcDEF)' });
|
||||
}
|
||||
if (!nextTelegramChatId) {
|
||||
return reply.code(400).send({ success: false, message: 'Telegram Chat ID 不能为空(启用 Telegram 时)' });
|
||||
}
|
||||
}
|
||||
|
||||
if (body.checkinCron !== undefined) {
|
||||
if (!cron.validate(body.checkinCron)) {
|
||||
return reply.code(400).send({ success: false, message: '签到 Cron 表达式无效' });
|
||||
@@ -382,6 +426,30 @@ export async function settingsRoutes(app: FastifyInstance) {
|
||||
upsertSetting('serverchan_key', config.serverChanKey);
|
||||
}
|
||||
|
||||
if (body.telegramEnabled !== undefined) {
|
||||
if (!!body.telegramEnabled !== config.telegramEnabled) {
|
||||
changedLabels.push('Telegram 开关');
|
||||
}
|
||||
config.telegramEnabled = !!body.telegramEnabled;
|
||||
upsertSetting('telegram_enabled', config.telegramEnabled);
|
||||
}
|
||||
|
||||
if (body.telegramBotToken !== undefined) {
|
||||
if (String(body.telegramBotToken || '').trim() !== config.telegramBotToken) {
|
||||
changedLabels.push('Telegram Bot Token');
|
||||
}
|
||||
config.telegramBotToken = String(body.telegramBotToken || '').trim();
|
||||
upsertSetting('telegram_bot_token', config.telegramBotToken);
|
||||
}
|
||||
|
||||
if (body.telegramChatId !== undefined) {
|
||||
if (String(body.telegramChatId || '').trim() !== config.telegramChatId) {
|
||||
changedLabels.push('Telegram Chat ID');
|
||||
}
|
||||
config.telegramChatId = String(body.telegramChatId || '').trim();
|
||||
upsertSetting('telegram_chat_id', config.telegramChatId);
|
||||
}
|
||||
|
||||
if (body.smtpEnabled !== undefined) {
|
||||
if (!!body.smtpEnabled !== config.smtpEnabled) {
|
||||
changedLabels.push('SMTP 开关');
|
||||
|
||||
@@ -187,4 +187,16 @@ describe('resolveUpstreamEndpointCandidates', () => {
|
||||
expect(isEndpointDowngradeError(405, '{"error":{"message":"Method Not Allowed"}}')).toBe(true);
|
||||
expect(isEndpointDowngradeError(400, '{"error":{"message":"unsupported endpoint","type":"invalid_request_error"}}')).toBe(true);
|
||||
});
|
||||
|
||||
it('treats Claude Code CLI-only restriction on responses as downgrade candidate', () => {
|
||||
const upstreamError = JSON.stringify({
|
||||
error: {
|
||||
code: 'invalid_request',
|
||||
type: 'new_api_error',
|
||||
message: '请勿在 Claude Code CLI 之外使用接口 (request id: abc123)',
|
||||
},
|
||||
});
|
||||
|
||||
expect(isEndpointDowngradeError(400, upstreamError)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user