diff --git a/src/services/webhookConfigService.js b/src/services/webhookConfigService.js index 18a460f6..59955cd1 100644 --- a/src/services/webhookConfigService.js +++ b/src/services/webhookConfigService.js @@ -56,15 +56,26 @@ class WebhookConfigService { // 验证平台配置 if (config.platforms) { - const validPlatforms = ['wechat_work', 'dingtalk', 'feishu', 'slack', 'discord', 'custom'] + const validPlatforms = [ + 'wechat_work', + 'dingtalk', + 'feishu', + 'slack', + 'discord', + 'custom', + 'bark' + ] for (const platform of config.platforms) { if (!validPlatforms.includes(platform.type)) { throw new Error(`不支持的平台类型: ${platform.type}`) } - if (!platform.url || !this.isValidUrl(platform.url)) { - throw new Error(`无效的webhook URL: ${platform.url}`) + // Bark平台使用deviceKey而不是url + if (platform.type !== 'bark') { + if (!platform.url || !this.isValidUrl(platform.url)) { + throw new Error(`无效的webhook URL: ${platform.url}`) + } } // 验证平台特定的配置 @@ -108,6 +119,88 @@ class WebhookConfigService { case 'custom': // 自定义webhook,用户自行负责格式 break + case 'bark': + // 验证设备密钥 + if (!platform.deviceKey) { + throw new Error('Bark平台必须提供设备密钥') + } + + // 验证设备密钥格式(通常是22-24位字符) + if (platform.deviceKey.length < 20 || platform.deviceKey.length > 30) { + logger.warn('⚠️ Bark设备密钥长度可能不正确,请检查是否完整复制') + } + + // 验证服务器URL(如果提供) + if (platform.serverUrl) { + if (!this.isValidUrl(platform.serverUrl)) { + throw new Error('Bark服务器URL格式无效') + } + if (!platform.serverUrl.includes('/push')) { + logger.warn('⚠️ Bark服务器URL应该以/push结尾') + } + } + + // 验证声音参数(如果提供) + if (platform.sound) { + const validSounds = [ + 'default', + 'alarm', + 'anticipate', + 'bell', + 'birdsong', + 'bloom', + 'calypso', + 'chime', + 'choo', + 'descent', + 'electronic', + 'fanfare', + 'glass', + 'gotosleep', + 'healthnotification', + 'horn', + 'ladder', + 'mailsent', + 'minuet', + 'multiwayinvitation', + 'newmail', + 'newsflash', + 'noir', + 'paymentsuccess', + 'shake', + 'sherwoodforest', + 'silence', + 'spell', + 'suspense', + 'telegraph', + 'tiptoes', + 'typewriters', + 'update', + 'alert' + ] + if (!validSounds.includes(platform.sound)) { + logger.warn(`⚠️ 未知的Bark声音: ${platform.sound}`) + } + } + + // 验证级别参数 + if (platform.level) { + const validLevels = ['active', 'timeSensitive', 'passive', 'critical'] + if (!validLevels.includes(platform.level)) { + throw new Error(`无效的Bark通知级别: ${platform.level}`) + } + } + + // 验证图标URL(如果提供) + if (platform.icon && !this.isValidUrl(platform.icon)) { + logger.warn('⚠️ Bark图标URL格式可能不正确') + } + + // 验证点击跳转URL(如果提供) + if (platform.clickUrl && !this.isValidUrl(platform.clickUrl)) { + logger.warn('⚠️ Bark点击跳转URL格式可能不正确') + } + break } } diff --git a/src/services/webhookService.js b/src/services/webhookService.js index ad2778ff..cffa3b03 100644 --- a/src/services/webhookService.js +++ b/src/services/webhookService.js @@ -11,7 +11,8 @@ class WebhookService { feishu: this.sendToFeishu.bind(this), slack: this.sendToSlack.bind(this), discord: this.sendToDiscord.bind(this), - custom: this.sendToCustom.bind(this) + custom: this.sendToCustom.bind(this), + bark: this.sendToBark.bind(this) } } @@ -212,6 +213,33 @@ class WebhookService { await this.sendHttpRequest(platform.url, payload, platform.timeout || 10000) } + /** + * Bark webhook + */ + async sendToBark(platform, type, data) { + const payload = { + device_key: platform.deviceKey, + title: this.getNotificationTitle(type), + body: this.formatMessageForBark(type, data), + level: platform.level || this.getBarkLevel(type), + sound: platform.sound || this.getBarkSound(type), + group: platform.group || 'claude-relay', + badge: 1 + } + + // 添加可选参数 + if (platform.icon) { + payload.icon = platform.icon + } + + if (platform.clickUrl) { + payload.url = platform.clickUrl + } + + const url = platform.serverUrl || 'https://api.day.app/push' + await this.sendHttpRequest(url, payload, platform.timeout || 10000) + } + /** * 发送HTTP请求 */ @@ -351,6 +379,81 @@ class WebhookService { return titles[type] || '📢 系统通知' } + /** + * 获取Bark通知级别 + */ + getBarkLevel(type) { + const levels = { + accountAnomaly: 'timeSensitive', + quotaWarning: 'active', + systemError: 'critical', + securityAlert: 'critical', + test: 'passive' + } + + return levels[type] || 'active' + } + + /** + * 获取Bark声音 + */ + getBarkSound(type) { + const sounds = { + accountAnomaly: 'alarm', + quotaWarning: 'bell', + systemError: 'alert', + securityAlert: 'alarm', + test: 'default' + } + + return sounds[type] || 'default' + } + + /** + * 格式化Bark消息 + */ + formatMessageForBark(type, data) { + const lines = [] + + if (data.accountName) { + lines.push(`账号: ${data.accountName}`) + } + + if (data.platform) { + lines.push(`平台: ${data.platform}`) + } + + if (data.status) { + lines.push(`状态: ${data.status}`) + } + + if (data.errorCode) { + lines.push(`错误: ${data.errorCode}`) + } + + if (data.reason) { + lines.push(`原因: ${data.reason}`) + } + + if (data.message) { + lines.push(`消息: ${data.message}`) + } + + if (data.quota) { + lines.push(`剩余配额: ${data.quota.remaining}/${data.quota.total}`) + } + + if (data.usage) { + lines.push(`使用率: ${data.usage}%`) + } + + // 添加服务标识和时间戳 + lines.push(`\n服务: Claude Relay Service`) + lines.push(`时间: ${new Date().toLocaleString('zh-CN')}`) + + return lines.join('\n') + } + /** * 格式化通知详情 */