93 lines
3.5 KiB
JavaScript
93 lines
3.5 KiB
JavaScript
// 1. 【引入依赖】
|
||
const fastify = require('fastify')({ logger: true });
|
||
// undici 是 Node.js 内置的高性能 HTTP 客户端,我们用它来发请求
|
||
const { request: undiciRequest } = require('undici');
|
||
|
||
// 2. 【配置】- 从环境变量读取 Gotify 的信息
|
||
// 这是最佳实践,避免将敏感信息硬编码在代码里
|
||
// const GOTIFY_URL = process.env.GOTIFY_URL; // 例如: 'http://your-gotify-server.com/message'
|
||
// const GOTIFY_TOKEN = process.env.GOTIFY_TOKEN; // 你的 Gotify 应用 Token
|
||
const GOTIFY_URL = 'https://gotify.zotv.ru/message'; // 例如: 'http://your-gotify-server.com/message'
|
||
const GOTIFY_TOKEN = 'A1wFaeaj-VskqyF'; // 你的 Gotify 应用 Token
|
||
|
||
// 启动前检查配置是否齐全
|
||
if (!GOTIFY_URL || !GOTIFY_TOKEN) {
|
||
console.error('错误:请设置 GOTIFY_URL 和 GOTIFY_TOKEN 环境变量!');
|
||
process.exit(1);
|
||
}
|
||
|
||
// 3. 【核心路由】 - 接收和转发 Webhook
|
||
fastify.post('/webhook/feishu', async (request, reply) => {
|
||
fastify.log.info('收到来自飞书的 Webhook 请求...');
|
||
|
||
const feishuPayload = request.body;
|
||
let title = '来自飞书的新消息'; // 默认标题
|
||
let message = '';
|
||
|
||
// --- 解析飞书消息 ---
|
||
// A. 处理简单的 "text" 类型消息
|
||
if (feishuPayload.msg_type === 'text') {
|
||
message = feishuPayload.content?.text || '无法解析的文本消息';
|
||
}
|
||
// B. 处理 "interactive" (卡片消息) 类型
|
||
else if (feishuPayload.msg_type === 'interactive' && feishuPayload.card) {
|
||
// 尝试从卡片标题中获取标题
|
||
title = feishuPayload.card.header?.title?.content || title;
|
||
// 尝试从卡片的第一个元素中获取内容
|
||
// (这是一个简化的逻辑,真实卡片可能很复杂)
|
||
const firstElement = feishuPayload.card.elements?.[0];
|
||
if (firstElement?.tag === 'div' && firstElement.text) {
|
||
message = firstElement.text.content || '无法解析的卡片内容';
|
||
} else {
|
||
message = JSON.stringify(feishuPayload.card.elements); // 如果结构不认识,就发原始数据
|
||
}
|
||
}
|
||
// C. 其他未知类型
|
||
else {
|
||
fastify.log.warn('收到了未知的飞书消息类型,将内容作为原始JSON转发');
|
||
message = JSON.stringify(feishuPayload, null, 2);
|
||
}
|
||
|
||
// --- 转发到 Gotify ---
|
||
const gotifyPayload = {
|
||
title: title,
|
||
message: message,
|
||
priority: 5, // Gotify 的消息优先级,可以按需调整
|
||
};
|
||
|
||
try {
|
||
fastify.log.info(`准备转发到 Gotify: ${GOTIFY_URL}`);
|
||
const { statusCode } = await undiciRequest(`${GOTIFY_URL}?token=${GOTIFY_TOKEN}`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(gotifyPayload),
|
||
});
|
||
|
||
if (statusCode >= 200 && statusCode < 300) {
|
||
fastify.log.info('成功转发消息到 Gotify!');
|
||
} else {
|
||
fastify.log.error(`转发到 Gotify 失败,状态码: ${statusCode}`);
|
||
}
|
||
|
||
} catch (error) {
|
||
fastify.log.error(`转发到 Gotify 时发生网络错误: ${error.message}`);
|
||
}
|
||
|
||
// --- 响应飞书 ---
|
||
// **非常重要**:无论转发是否成功,都应该立即回复飞书一个成功的响应。
|
||
// 否则飞书会认为你的 Webhook 地址有问题,并可能不停重试。
|
||
return { success: true, message: '消息已接收' };
|
||
});
|
||
|
||
// 4. 【启动服务器】
|
||
const start = async () => {
|
||
try {
|
||
// 监听 0.0.0.0 以便从局域网或 Docker 容器外访问
|
||
await fastify.listen({ port: 3000, host: '0.0.0.0' });
|
||
} catch (err) {
|
||
fastify.log.error(err);
|
||
process.exit(1);
|
||
}
|
||
};
|
||
|
||
start(); |