UNPKG

apptise-core

Version:

Core library for Apptise unified notification system

177 lines 6.78 kB
import { NotificationPlugin } from '../base/plugin.js'; import { HttpClient } from '../utils/http-client.js'; /** * Google Chat 通知插件 * 支持通过 Webhook 向 Google Chat 发送消息 */ export class GoogleChatPlugin extends NotificationPlugin { registration = { serviceId: 'google-chat', protocols: ['gchat'], name: 'Google Chat', description: 'Send notifications to Google Chat via webhooks', version: '1.0.0', }; /** * 解析 Google Chat URL * 支持格式: * 1. gchat://workspace/key/token * 2. gchat://workspace/path?key=xxx&token=xxx * @param url - Google Chat webhook URL * @returns 解析后的插件配置 */ parseUrl(url) { const parsedUrl = this.parseUrlBase(url); if (!this.registration.protocols.includes(parsedUrl.protocol)) { throw this.createError('INVALID_URL', `Unsupported protocol: ${parsedUrl.protocol}. Expected one of: ${this.registration.protocols.join(', ')}`); } let workspace; let key; let token; // 解析路径部分 const pathParts = parsedUrl.pathname?.split('/').filter(Boolean) || []; // 检查是否有查询参数中的 key 和 token const queryKey = parsedUrl.searchParams.get('key'); const queryToken = parsedUrl.searchParams.get('token'); if (queryKey && queryToken) { // 格式: gchat://workspace/path?key=xxx&token=xxx workspace = parsedUrl.hostname || ''; if (!workspace) { throw this.createError('INVALID_URL', 'Missing workspace in URL'); } key = queryKey; token = queryToken; } else if (pathParts.length >= 3) { // 格式: gchat://workspace/key/token [workspace, key, token] = pathParts; } else { throw this.createError('INVALID_URL', 'Invalid Google Chat URL format. Expected: gchat://workspace/key/token or gchat://workspace/path?key=xxx&token=xxx'); } if (!workspace || !key || !token) { throw this.createError('INVALID_URL', 'Missing required parameters: workspace, key, or token'); } // 构建 webhook URL (不包含 key 和 token 参数) const webhookUrl = `https://chat.googleapis.com/v1/spaces/${workspace}/messages`; // 解析其他查询参数 const thread = parsedUrl.searchParams.get('thread'); const format = parsedUrl.searchParams.get('format') || 'text'; const avatar = parsedUrl.searchParams.get('avatar'); const mode = parsedUrl.searchParams.get('mode') || 'sync'; return { serviceId: this.registration.serviceId, url, config: { workspace, key, token, webhookUrl, thread, format, avatar, mode, }, }; } /** * 发送通知到 Google Chat * @param config - 插件配置 * @param message - 通知消息 * @returns 发送结果 */ async send(config, message) { if (!this.validateConfig(config)) { return this.createErrorResult('Invalid plugin configuration'); } if (!this.validateMessage(message)) { return this.createErrorResult('Invalid notification message'); } try { const { result, duration } = await this.measureTime(async () => { return await this.sendMessage(config, message); }); return this.createSuccessResult(result, duration); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; return this.createErrorResult(errorMessage, undefined, undefined); } } /** * 发送消息到 Google Chat * @private */ async sendMessage(config, message) { const { webhookUrl, thread, format, avatar, key, token } = config.config; // 构建请求 URL,添加 key 和 token 参数(按照Python版本的顺序) const url = new URL(webhookUrl); url.searchParams.set('token', token); url.searchParams.set('key', key); if (thread) { url.searchParams.set('threadKey', thread); } const requestUrl = url.toString(); // 构建消息体 const payload = this.buildPayload(message, format, avatar); // 构建请求头 const headers = { 'content-type': 'application/json; charset=utf-8', 'User-Agent': 'Apptise/1.0.0 (+https://github.com/apptise/apptise)', }; const requestData = JSON.stringify(payload); // Log HTTP request details for equivalence testing console.log(`[APPTISE_HTTP_REQUEST] Method: POST`); console.log(`[APPTISE_HTTP_REQUEST] URL: ${requestUrl}`); console.log(`[APPTISE_HTTP_REQUEST] Headers: ${JSON.stringify(headers)}`); console.log(`[APPTISE_HTTP_REQUEST] Data: ${requestData}`); console.log(`[APPTISE_HTTP_REQUEST] Timeout: 30000`); // 发送请求 const response = await HttpClient.post(requestUrl, requestData, { headers, timeout: 30000, }); // Log HTTP response details for equivalence testing console.log(`[APPTISE_HTTP_RESPONSE] Status: ${response.status}`); console.log(`[APPTISE_HTTP_RESPONSE] Headers: ${JSON.stringify(response.headers)}`); console.log(`[APPTISE_HTTP_RESPONSE] Content: ${JSON.stringify(response.data)}`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.data; } /** * 构建消息载荷 * @private */ buildPayload(message, format, avatar) { const payload = {}; if (format === 'markdown') { // Markdown 格式 let text = `**${message.title}**`; if (message.body) { text += `\n${message.body}`; } payload.text = text; } else { // 纯文本格式 let text = message.title; if (message.body) { text += `\r\n${message.body}`; } payload.text = text; } // 添加头像 if (avatar) { payload.sender = { displayName: 'Apptise', avatarUrl: avatar, }; } return payload; } } // 导出插件实例 export const googleChatPlugin = new GoogleChatPlugin(); //# sourceMappingURL=google-chat.js.map