guanghulab/scripts/write-notion-syslog.js

228 lines
8.3 KiB
JavaScript
Raw Permalink Normal View History

// scripts/write-notion-syslog.js
// 铸渊 · 飞书 SYSLOG → Notion 收件箱写入
//
// 读取 syslog-inbox/ 下的 JSON 文件,写入 Notion SYSLOG 收件箱数据库。
// 用法: node scripts/write-notion-syslog.js <syslog-file-path>
//
// 环境变量:
// NOTION_TOKEN Notion API token必须
// NOTION_DB_SYSLOG SYSLOG 收件箱数据库 ID必须
'use strict';
const https = require('https');
const fs = require('fs');
const path = require('path');
// ══════════════════════════════════════════════════════════
// 常量
// ══════════════════════════════════════════════════════════
const NOTION_VERSION = '2022-06-28';
const NOTION_API_HOSTNAME = 'api.notion.com';
const NOTION_RICH_TEXT_MAX = 2000;
// ══════════════════════════════════════════════════════════
// HTTP 请求工具(复用 receive-syslog.js 模式)
// ══════════════════════════════════════════════════════════
function notionPost(endpoint, body, token) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify(body);
const opts = {
hostname: NOTION_API_HOSTNAME,
port: 443,
path: endpoint,
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json',
'Notion-Version': NOTION_VERSION,
'Content-Length': Buffer.byteLength(payload),
},
};
const req = https.request(opts, res => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const parsed = JSON.parse(data);
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(parsed);
} else {
reject(new Error('Notion API ' + res.statusCode + ': ' + (parsed.message || data)));
}
} catch (e) {
reject(new Error('Notion API parse error: ' + data));
}
});
});
req.on('error', reject);
req.write(payload);
req.end();
});
}
// ══════════════════════════════════════════════════════════
// Notion 属性构建辅助
// ══════════════════════════════════════════════════════════
function richText(content) {
const str = String(content || '');
const chunks = [];
for (let i = 0; i < str.length; i += NOTION_RICH_TEXT_MAX) {
chunks.push({ type: 'text', text: { content: str.slice(i, i + NOTION_RICH_TEXT_MAX) } });
}
return chunks.length ? chunks : [{ type: 'text', text: { content: '' } }];
}
function titleProp(text) {
return { title: [{ type: 'text', text: { content: String(text || '').slice(0, 120) } }] };
}
function richTextProp(text) {
return { rich_text: richText(text) };
}
function selectProp(name) {
return { select: { name: String(name) } };
}
function dateProp(dateStr) {
return { date: { start: dateStr } };
}
function statusProp(name) {
return { status: { name: String(name) } };
}
// ══════════════════════════════════════════════════════════
// 主流程
// ══════════════════════════════════════════════════════════
async function main() {
const filePath = process.argv[2];
const notionToken = process.env.NOTION_TOKEN;
const syslogDbId = process.env.NOTION_DB_SYSLOG;
if (!filePath) {
console.error('❌ 用法: node write-notion-syslog.js <syslog-file-path>');
process.exit(1);
}
if (!notionToken || !syslogDbId) {
console.log('⚠️ 缺少 NOTION_TOKEN 或 NOTION_DB_SYSLOG跳过 Notion 写入');
process.exit(0);
}
// 读取 SYSLOG 文件(保留原始文本用于 文件内容 字段)
let fileContent;
let entry;
try {
fileContent = fs.readFileSync(filePath, 'utf8');
entry = JSON.parse(fileContent);
} catch (e) {
console.error('❌ 读取/解析文件失败: ' + e.message);
process.exit(1);
}
const filename = path.basename(filePath);
const syslogText = entry.syslog_raw || entry.syslog || entry.content || '';
const source = entry.source || 'unknown';
const senderName = entry.sender_name || '';
const senderOpenId = entry.sender_open_id || entry.sender_id || '';
const chatId = entry.chat_id || '';
const timestamp = entry.timestamp || new Date().toISOString();
const dateStr = timestamp.split('T')[0] || new Date().toISOString().split('T')[0];
// 解析 SYSLOG 正文内容(兼容 v4.0 和 v3.0-legacy 格式)
let syslogData = null;
if (syslogText) {
try { syslogData = JSON.parse(syslogText); } catch (_) { /* 非 JSON 则忽略 */ }
}
// DEV编号优先从 SYSLOG 内容解析fallback 到 DT-USER
const devId = (syslogData && (syslogData.dev_id || (syslogData.header && syslogData.header.developer_id)))
|| entry.dev_id || entry.from || 'DT-USER';
// 其他可选字段:兼容 v4.0 header 和 v3.0 顶层字段
const broadcastId = (syslogData && (syslogData.broadcast_id || (syslogData.header && syslogData.header.broadcast_id))) || '';
const moduleName = (syslogData && (syslogData.module || (syslogData.header && syslogData.header.module))) || '';
const statusField = (syslogData && (syslogData.status || (syslogData.header && syslogData.header.status))) || '';
const protocolVersion = (syslogData && (syslogData.protocol_version || syslogData.syslog_version)) || '';
const commitSha = process.env.GITHUB_SHA || '';
console.log('📋 写入 Notion SYSLOG 收件箱...');
console.log(' 文件: ' + filename);
console.log(' 来源: ' + source);
console.log(' 发送者: ' + (senderName || senderOpenId || '未知'));
console.log(' DEV编号: ' + devId);
// 构建 Notion 页面属性
const properties = {
'标题': titleProp('飞书SYSLOG · ' + (senderName || source) + ' · ' + dateStr),
'接收时间': dateProp(dateStr),
'处理状态': statusProp('待处理'),
};
// 可选字段(如果 Notion 数据库有这些列)
if (source) properties['推送方'] = richTextProp(source);
if (devId) properties['DEV编号'] = selectProp(devId);
// 文件内容
properties['文件内容'] = richTextProp(fileContent);
// 来源路径
properties['来源路径'] = richTextProp(filePath);
// 其他可选字段
if (broadcastId) properties['广播编号'] = richTextProp(broadcastId);
if (moduleName) properties['模块'] = richTextProp(moduleName);
if (statusField) properties['环节状态'] = selectProp(statusField);
if (protocolVersion) properties['协议版本'] = richTextProp(protocolVersion);
if (commitSha) properties['commit_sha'] = richTextProp(commitSha);
// 构建页面内容
const contentBlocks = [];
// SYSLOG 正文
if (syslogText) {
contentBlocks.push({
object: 'block',
type: 'paragraph',
paragraph: {
rich_text: richText(syslogText),
},
});
}
// 原始 JSON调试用
contentBlocks.push({
object: 'block',
type: 'code',
code: {
rich_text: richText(JSON.stringify(entry, null, 2)),
language: 'json',
},
});
const body = {
parent: { database_id: syslogDbId },
properties,
children: contentBlocks,
};
try {
const result = await notionPost('/v1/pages', body, notionToken);
console.log(' ✅ Notion SYSLOG 收件箱条目已创建: ' + result.id);
} catch (err) {
console.error(' ❌ Notion 写入失败: ' + err.message);
// 不退出,允许后续步骤继续
}
}
main().catch(err => {
console.error('❌ write-notion-syslog 失败: ' + err.message);
process.exit(1);
});