// scripts/bridge/check-queue.js // ๐ŸŒ‰ ๆกฅๆŽฅยท่ฐƒๅบฆ้˜Ÿๅˆ—ๆฃ€ๆŸฅ // // ๆŸฅ่ฏข Notion ่ฐƒๅบฆ้˜Ÿๅˆ—ๆ•ฐๆฎๅบ“ไธญๆŒ‡ๅฎš็ฑปๅž‹+็Šถๆ€็š„ไปปๅŠก // ็”จๆณ•: node scripts/bridge/check-queue.js [BROADCAST_READY|SYSLOG_RECEIVED|HEARTBEAT] // // ็Žฏๅขƒๅ˜้‡๏ผš // NOTION_TOKEN Notion API token // BRIDGE_QUEUE_DB_ID ๆกฅๆŽฅ่ฐƒๅบฆ้˜Ÿๅˆ—ๆ•ฐๆฎๅบ“ 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'; // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // Notion API ๅŸบ็ก€่ฐƒ็”จ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• 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(); }); } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ไธป้€ป่พ‘ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• async function checkQueue(taskType) { const token = process.env.NOTION_TOKEN; const dbId = process.env.BRIDGE_QUEUE_DB_ID; if (!token) { console.log('โš ๏ธ ็ผบๅฐ‘ NOTION_TOKEN๏ผŒ่ทณ่ฟ‡่ฐƒๅบฆ้˜Ÿๅˆ—ๆฃ€ๆŸฅ'); process.exit(0); } if (!dbId) { console.log('โš ๏ธ ็ผบๅฐ‘ BRIDGE_QUEUE_DB_ID๏ผŒ่ทณ่ฟ‡่ฐƒๅบฆ้˜Ÿๅˆ—ๆฃ€ๆŸฅ'); process.exit(0); } console.log(`๐ŸŒ‰ ๆฃ€ๆŸฅ่ฐƒๅบฆ้˜Ÿๅˆ— ยท ็ฑปๅž‹=${taskType} ยท ็Šถๆ€=ๅพ…ๅค„็†`); const filter = { and: [ { property: '็ฑปๅž‹', select: { equals: taskType } }, { property: 'ๅค„็†็Šถๆ€', status: { equals: 'ๅพ…ๅค„็†' } }, ], }; const body = { filter, sorts: [{ property: 'ๅˆ›ๅปบๆ—ถ้—ด', direction: 'ascending' }], page_size: 50, }; try { const result = await notionPost(`/v1/databases/${dbId}/query`, body, token); const tasks = result.results || []; console.log(`๐Ÿ“‹ ๅ‘็Žฐ ${tasks.length} ๆกๅพ…ๅค„็†ไปปๅŠก`); // ๅฐ†ไปปๅŠกๅˆ—่กจๅ†™ๅ…ฅไธดๆ—ถๆ–‡ไปถไพ›ๅŽ็ปญๆญฅ้ชคไฝฟ็”จ const outputDir = path.join('data', 'bridge-logs'); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const outputFile = path.join(outputDir, `queue-${taskType.toLowerCase()}-${Date.now()}.json`); const taskSummaries = tasks.map(t => ({ id: t.id, task_name: extractTitle(t), task_type: taskType, dev_id: extractSelect(t, 'DEV็ผ–ๅท'), broadcast_id: extractRichText(t, 'ๅนฟๆ’ญ็ผ–ๅท'), source: extractRichText(t, 'ๆฅๆบๆธ ้“'), payload: extractRichText(t, 'Payload'), created_time: t.created_time, })); fs.writeFileSync(outputFile, JSON.stringify(taskSummaries, null, 2)); console.log(`๐Ÿ“ ไปปๅŠกๅˆ—่กจๅทฒๅ†™ๅ…ฅ: ${outputFile}`); // ่พ“ๅ‡บๅˆฐ GITHUB_OUTPUT๏ผˆไพ› workflow ไฝฟ็”จ๏ผ‰ if (process.env.GITHUB_OUTPUT) { fs.appendFileSync(process.env.GITHUB_OUTPUT, `task_count=${tasks.length}\n` + `queue_file=${outputFile}\n` ); } return taskSummaries; } catch (e) { console.error(`โŒ ๆŸฅ่ฏข่ฐƒๅบฆ้˜Ÿๅˆ—ๅคฑ่ดฅ: ${e.message}`); process.exit(1); } } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // Notion ๅฑžๆ€งๆๅ–่พ…ๅŠฉ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• function extractTitle(page) { const prop = page.properties && page.properties['ไปปๅŠกๅ็งฐ']; if (prop && prop.title && prop.title.length > 0) { return prop.title.map(t => t.plain_text).join(''); } return ''; } function extractSelect(page, name) { const prop = page.properties && page.properties[name]; if (prop && prop.select) { return prop.select.name || ''; } return ''; } function extractRichText(page, name) { const prop = page.properties && page.properties[name]; if (prop && prop.rich_text && prop.rich_text.length > 0) { return prop.rich_text.map(t => t.plain_text).join(''); } return ''; } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๅ…ฅๅฃ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• const taskType = process.argv[2] || 'BROADCAST_READY'; checkQueue(taskType).catch(e => { console.error(e); process.exit(1); });