diff --git a/.github/DISCUSSION_TEMPLATE/syslog-submit.yml b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml new file mode 100644 index 00000000..a0baaa94 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/syslog-submit.yml @@ -0,0 +1,51 @@ +title: "BC-XXX-XXX๏ฝœSYSLOG" +labels: [] +body: + - type: markdown + attributes: + value: | + ## ๐Ÿ“ก SYSLOG ่‡ชๅŠฉๆไบค / ๅนฟๆ’ญๆ้—ฎ + + > ๅšๅฎŒไบ†๏ผŸๆŠŠๆ—ฅๅฟ—็ฒ˜่ดด่ฟ›ๅŽป๏ผŒๅกซไธŠ้‚ฎ็ฎฑ๏ผŒ็‚นๆไบคใ€‚ๆ–ฐๅนฟๆ’ญไผšๅ‘ๅˆฐไฝ ้‚ฎ็ฎฑ้‡Œใ€‚ + > + > ๆœ‰้—ฎ้ข˜๏ผŸๆŠŠ้—ฎ้ข˜็ฒ˜่ดด่ฟ›ๅŽป๏ผŒๅกซไธŠ้‚ฎ็ฎฑ๏ผŒ็‚นๆไบคใ€‚่งฃ็ญ”ไผšๅ‘ๅˆฐไฝ ้‚ฎ็ฎฑ้‡Œใ€‚ + + **ๆ ‡้ข˜ๆ ผๅผ**๏ผš`BC-XXX-XXX๏ฝœSYSLOG` ๆˆ– `BC-XXX-XXX๏ฝœๆ้—ฎ` + + - type: input + id: broadcast_id + attributes: + label: "ๅนฟๆ’ญ็ผ–ๅท" + description: "ๅ†™ไธŠไฝ ๅฝ“ๅ‰ๅนฟๆ’ญ็š„็ผ–ๅท๏ผŒไพ‹ๅฆ‚ BC-M22-009-AW" + placeholder: "BC-M22-009-AW" + validations: + required: true + + - type: dropdown + id: submit_type + attributes: + label: "็ฑปๅž‹" + description: "้€‰ๆ‹ฉๆไบค็ฑปๅž‹" + options: + - SYSLOG + - ๆ้—ฎ + validations: + required: true + + - type: input + id: email + attributes: + label: "ไฝ ็š„้‚ฎ็ฎฑ" + description: "็”จไบŽๆŽฅๆ”ถๆ–ฐๅนฟๆ’ญๆˆ–้—ฎ้ข˜่งฃ็ญ”" + placeholder: "your@email.com" + validations: + required: true + + - type: textarea + id: content + attributes: + label: "ๅ†…ๅฎน" + description: "็ฒ˜่ดดไฝ ็š„ SYSLOG ๅ…จๆ–‡ ๆˆ– ้—ฎ้ข˜ๆ่ฟฐ" + placeholder: "ๅœจ่ฟ™้‡Œ็ฒ˜่ดดไฝ ็š„ SYSLOG ๆˆ–้—ฎ้ข˜..." + validations: + required: true diff --git a/.github/workflows/syslog-auto-pipeline.yml b/.github/workflows/syslog-auto-pipeline.yml new file mode 100644 index 00000000..c2fa3898 --- /dev/null +++ b/.github/workflows/syslog-auto-pipeline.yml @@ -0,0 +1,366 @@ +name: SYSLOG Auto Pipeline +# ๐Ÿ“ก SYSLOG ่‡ชๅŠฉๆไบค็ณป็ปŸ ยท ๅ…จ่‡ชๅŠจ้—ญ็Žฏ +# +# ๅผ€ๅ‘่€…ๅœจ GitHub Discussion ๆไบค SYSLOG ๆˆ–ๆ้—ฎ +# โ†’ Actions ่‡ชๅŠจ่งฃๆž โ†’ ่ฐƒ็”จ LLM API ๅ”ค้†’ไบบๆ ผไฝ“ +# โ†’ ๅ†™ๅ…ฅ Notion ๅทฅๅ• โ†’ ๅ‘้‚ฎไปถ็ป™ๅผ€ๅ‘่€… โ†’ Discussion ๅ›žๅค +# +# ไพ่ต– Secrets๏ผš +# LLM_API_KEY ็ฌฌไธ‰ๆ–น LLM ๅนณๅฐๅฏ†้’ฅ๏ผˆๅฟ…้กป๏ผ‰ +# LLM_BASE_URL ็ฌฌไธ‰ๆ–น LLM ๅนณๅฐ API ๅœฐๅ€๏ผˆๅฟ…้กป๏ผŒๅฆ‚ https://api.xxx.com/v1๏ผ‰ +# NOTION_API_TOKEN Notion API token +# NOTION_TICKET_DB_ID ้œœ็ šๅทฅๅ•ๆ•ฐๆฎๅบ“ ID +# CORE_BRAIN_PAGE_ID ๆ›œๅ†ฅๆ ธๅฟƒๅคง่„‘ v4.0 ้กต้ข ID๏ผˆv4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +# PORTRAIT_DB_ID ๅผ€ๅ‘่€…ๅŠจๆ€็”ปๅƒๅบ“ๆ•ฐๆฎๅบ“ ID๏ผˆv4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +# FINGERPRINT_DB_ID ๆจกๅ—ๆŒ‡็บนๆณจๅ†Œ่กจๆ•ฐๆฎๅบ“ ID๏ผˆv4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +# SMTP_USER QQ ้‚ฎ็ฎฑๅœฐๅ€ +# SMTP_PASS QQ ้‚ฎ็ฎฑ SMTP ๆŽˆๆƒ็  + +on: + discussion: + types: [created] + +jobs: + process: + name: ๐Ÿ“ก ๅค„็† SYSLOG ๆไบค / ๅนฟๆ’ญๆ้—ฎ + runs-on: ubuntu-latest + if: contains(github.event.discussion.category.name, 'SYSLOG') + permissions: + contents: write + discussions: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci --ignore-scripts + + - name: ๐Ÿ” Parse submission + id: parse + uses: actions/github-script@v7 + with: + script: | + const body = context.payload.discussion.body || ''; + const title = context.payload.discussion.title || ''; + + // Parse structured fields from discussion body + function extractField(text, label) { + // Match form field format: ### Label\n\nValue + const regex = new RegExp('###\\s*' + label + '\\s*\\n+([^\\n#]+)', 'i'); + const match = text.match(regex); + return match ? match[1].trim() : ''; + } + + const broadcastId = extractField(body, 'ๅนฟๆ’ญ็ผ–ๅท') || ''; + const submitType = extractField(body, '็ฑปๅž‹') || ''; + const email = extractField(body, 'ไฝ ็š„้‚ฎ็ฎฑ') || ''; + + // Extract content (everything after "### ๅ†…ๅฎน") + const contentMatch = body.match(/###\s*ๅ†…ๅฎน\s*\n+([\s\S]*?)$/i); + const content = contentMatch ? contentMatch[1].trim() : ''; + + // Determine type from title or body + let type = 'question'; // default + if (submitType.includes('SYSLOG') || title.includes('SYSLOG')) { + type = 'syslog'; + } else if (submitType.includes('ๆ้—ฎ') || title.includes('ๆ้—ฎ')) { + type = 'question'; + } + + // Validate + if (!broadcastId) { + core.setFailed('โŒ ็ผบๅฐ‘ๅนฟๆ’ญ็ผ–ๅท๏ผŒ่ฏทๅœจๆไบคๆ—ถๅกซๅ†™ๅนฟๆ’ญ็ผ–ๅท'); + return; + } + if (!email) { + core.setFailed('โŒ ็ผบๅฐ‘้‚ฎ็ฎฑ๏ผŒ่ฏทๅœจๆไบคๆ—ถๅกซๅ†™ไฝ ็š„้‚ฎ็ฎฑ'); + return; + } + if (!content) { + core.setFailed('โŒ ็ผบๅฐ‘ๅ†…ๅฎน๏ผŒ่ฏท็ฒ˜่ดดไฝ ็š„ SYSLOG ๆˆ–้—ฎ้ข˜'); + return; + } + + core.setOutput('broadcast_id', broadcastId); + core.setOutput('type', type); + core.setOutput('email', email); + core.setOutput('content', content); + core.setOutput('discussion_number', context.payload.discussion.number); + core.setOutput('author', context.payload.discussion.user.login); + + console.log(`๐Ÿ“ก ่งฃๆžๅฎŒๆˆ: ๅนฟๆ’ญ=${broadcastId}, ็ฑปๅž‹=${type}, ้‚ฎ็ฎฑ=${email}`); + + - name: ๐Ÿง  Auto-detect and wake up persona + id: persona + env: + LLM_API_KEY: ${{ secrets.LLM_API_KEY }} + LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + SUBMIT_CONTENT: ${{ steps.parse.outputs.content }} + AUTHOR: ${{ steps.parse.outputs.author }} + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + CORE_BRAIN_PAGE_ID: ${{ secrets.CORE_BRAIN_PAGE_ID }} + PORTRAIT_DB_ID: ${{ secrets.PORTRAIT_DB_ID }} + FINGERPRINT_DB_ID: ${{ secrets.FINGERPRINT_DB_ID }} + run: node scripts/wake-persona.js + + - name: ๐Ÿ“‹ Write to Notion ticket + if: ${{ secrets.NOTION_API_TOKEN }} + env: + NOTION_TOKEN: ${{ secrets.NOTION_API_TOKEN }} + NOTION_TICKET_DB_ID: ${{ secrets.NOTION_TICKET_DB_ID }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + run: | + node -e " + const https = require('https'); + + const token = process.env.NOTION_TOKEN; + const dbId = process.env.NOTION_TICKET_DB_ID; + const broadcastId = process.env.BROADCAST_ID || 'UNKNOWN'; + const type = process.env.SUBMIT_TYPE || 'syslog'; + const result = process.env.PERSONA_RESULT || '(no result)'; + + if (!token || !dbId) { + console.log('โš ๏ธ Notion credentials not configured, skipping ticket creation'); + process.exit(0); + } + + const typeLabel = type === 'syslog' ? 'SYSLOG้—ญ็Žฏ' : 'ๆ้—ฎ่งฃ็ญ”'; + const title = '[่‡ชๅŠจ] ' + broadcastId + ' ยท ' + typeLabel; + + const body = JSON.stringify({ + parent: { database_id: dbId }, + properties: { + 'ๆ ‡้ข˜': { title: [{ type: 'text', text: { content: title.slice(0, 120) } }] }, + 'ๆ“ไฝœ็ฑปๅž‹': { select: { name: 'ๅ…ถไป–' } }, + 'ๆไบค่€…': { rich_text: [{ type: 'text', text: { content: '่‡ชๅŠจ็ฎก้“' } }] }, + '็Šถๆ€': { select: { name: 'ๅทฒๅฎŒๆˆ' } }, + 'ไผ˜ๅ…ˆ็บง': { select: { name: 'P1' } } + }, + children: [{ + object: 'block', + type: 'paragraph', + paragraph: { + rich_text: [{ type: 'text', text: { content: result.slice(0, 2000) } }] + } + }] + }); + + const opts = { + hostname: 'api.notion.com', + port: 443, + path: '/v1/pages', + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + token, + 'Content-Type': 'application/json', + 'Notion-Version': '2022-06-28', + 'Content-Length': Buffer.byteLength(body) + } + }; + + const req = https.request(opts, (res) => { + let data = ''; + res.on('data', (c) => data += c); + res.on('end', () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + console.log('โœ… Notion ๅทฅๅ•ๅทฒๅˆ›ๅปบ'); + } else { + console.log('โš ๏ธ Notion ๅทฅๅ•ๅˆ›ๅปบๅคฑ่ดฅ: ' + res.statusCode + ' ' + data); + } + }); + }); + req.on('error', (e) => console.log('โš ๏ธ Notion ่ฏทๆฑ‚ๅคฑ่ดฅ: ' + e.message)); + req.write(body); + req.end(); + " + + - name: ๐Ÿ“ง Send email to developer + env: + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + EMAIL_TO: ${{ steps.parse.outputs.email }} + BROADCAST_ID: ${{ steps.parse.outputs.broadcast_id }} + SUBMIT_TYPE: ${{ steps.parse.outputs.type }} + PERSONA_RESULT: ${{ steps.persona.outputs.result }} + run: | + node -e " + const nodemailer = require('nodemailer'); + + const user = process.env.SMTP_USER || ''; + const pass = process.env.SMTP_PASS || ''; + const to = process.env.EMAIL_TO || ''; + const broadcastId = process.env.BROADCAST_ID || ''; + const type = process.env.SUBMIT_TYPE || 'syslog'; + const result = process.env.PERSONA_RESULT || '(ๅค„็†ไธญ๏ผŒ่ฏท็จๅŽ)'; + + if (!user || !pass) { + console.log('โš ๏ธ SMTP not configured, skipping email'); + process.exit(0); + } + if (!to) { + console.log('โš ๏ธ No recipient email, skipping'); + process.exit(0); + } + + const subjectText = type === 'syslog' + ? '[ๅ…‰ๆน–็ณป็ปŸ] ' + broadcastId + ' ยท ๆ–ฐๅนฟๆ’ญๅทฒ็”Ÿๆˆ' + : '[ๅ…‰ๆน–็ณป็ปŸ] ' + broadcastId + ' ยท ้—ฎ้ข˜ๅทฒ่งฃ็ญ”'; + + const resultHtml = result + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + + const html = [ + '
', + '
', + '

๐ŸŒŠ ๅ…‰ๆน–็ณป็ปŸ ยท ่‡ชๅŠจ้€š็Ÿฅ

', + '

HoloLake ยท ไบบๆ ผ่ฏญ่จ€ๆ“ไฝœ็ณป็ปŸ

', + '
', + '
', + '

๐Ÿ“ก ' + broadcastId + '

', + '
' + resultHtml + '
', + '
', + '
', + 'โ†ฉ ๅ›žๅˆฐไป“ๅบ“็ปง็ปญๅผ€ๅ‘', + '

๐ŸŒ€ ้“ธๆธŠ ยท ไปฃ็ ๅฎˆๆŠคไบบๆ ผไฝ“ ยท ่‡ชๅŠจๅ‘้€

', + '
', + '
' + ].join('\n'); + + const transporter = nodemailer.createTransport({ + host: 'smtp.qq.com', + port: 465, + secure: true, + auth: { user, pass } + }); + + transporter.sendMail({ + from: '\"ๅ…‰ๆน–็ณป็ปŸ\" <' + user + '>', + to: to, + subject: subjectText, + html: html + }).then((info) => { + console.log('โœ… ้‚ฎไปถๅทฒๅ‘้€: ' + info.messageId); + }).catch((err) => { + console.log('โš ๏ธ ้‚ฎไปถๅ‘้€ๅคฑ่ดฅ: ' + err.message); + }); + " + + - name: ๐Ÿ’ฌ Reply to Discussion + uses: actions/github-script@v7 + with: + script: | + const number = ${{ steps.parse.outputs.discussion_number }}; + const type = '${{ steps.parse.outputs.type }}'; + const broadcastId = '${{ steps.parse.outputs.broadcast_id }}'; + const email = '${{ steps.parse.outputs.email }}'; + + const typeLabel = type === 'syslog' ? 'SYSLOG ้—ญ็Žฏๅค„็†' : '้—ฎ้ข˜่งฃ็ญ”'; + const maskedEmail = email.length > 4 + ? email.replace(/(.{2})(.*)(@.*)/, '$1***$3') + : '***'; + + const body = [ + 'โœ… **ๅทฒๅค„็†** ยท ' + typeLabel, + '', + '| ้กน็›ฎ | ๅ†…ๅฎน |', + '|------|------|', + '| ๐Ÿ“ก ๅนฟๆ’ญ็ผ–ๅท | `' + broadcastId + '` |', + '| ๐Ÿ“ง ็ป“ๆžœๅ‘้€่‡ณ | `' + maskedEmail + '` |', + '| ๐Ÿค– ๅค„็†ไบบๆ ผไฝ“ | ้“ธๆธŠ |', + '| โฐ ๅค„็†ๆ—ถ้—ด | ' + new Date().toISOString() + ' |', + '', + '> ็ป“ๆžœๅทฒๅ‘้€ๅˆฐไฝ ็š„้‚ฎ็ฎฑ๏ผŒ่ฏทๆŸฅๆ”ถใ€‚', + '>', + '> ๅฆ‚ๆœชๆ”ถๅˆฐ๏ผŒ่ฏทๆฃ€ๆŸฅๅžƒๅœพ็ฎฑๆˆ–้‡ๆ–ฐๆไบคใ€‚' + ].join('\n'); + + // Use GraphQL to add discussion comment + const query = ` + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id } + } + } + `; + + try { + // Get the discussion node ID + const { repository } = await github.graphql(` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + } + } + } + `, { + owner: context.repo.owner, + repo: context.repo.repo, + number: number + }); + + await github.graphql(query, { + discussionId: repository.discussion.id, + body: body + }); + + console.log('โœ… Discussion ๅ›žๅคๅทฒๅ‘้€'); + } catch (err) { + console.log('โš ๏ธ Discussion ๅ›žๅคๅคฑ่ดฅ: ' + err.message); + } + + - name: ๐Ÿ”ด ๅคฑ่ดฅๅ‘Š่ญฆ + if: failure() + uses: actions/github-script@v7 + with: + script: | + const number = context.payload.discussion?.number; + if (!number) return; + + try { + const { repository } = await github.graphql(` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + } + } + } + `, { + owner: context.repo.owner, + repo: context.repo.repo, + number: number + }); + + await github.graphql(` + mutation($discussionId: ID!, $body: String!) { + addDiscussionComment(input: { + discussionId: $discussionId, + body: $body + }) { + comment { id } + } + } + `, { + discussionId: repository.discussion.id, + body: 'โŒ **ๅค„็†ๅคฑ่ดฅ** ยท ่ฏทๆฃ€ๆŸฅๆไบคๆ ผๅผๆ˜ฏๅฆๆญฃ็กฎ๏ผŒๆˆ–่”็ณป็ฎก็†ๅ‘˜ใ€‚\n\n> ้”™่ฏฏ่ฏฆๆƒ…่ฏทๆŸฅ็œ‹ [Actions ๆ—ฅๅฟ—](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions)ใ€‚' + }); + } catch (err) { + console.log('โš ๏ธ ๅคฑ่ดฅๅ‘Š่ญฆๅ›žๅคๅคฑ่ดฅ: ' + err.message); + } diff --git a/README.md b/README.md index ff934478..81e46568 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,21 @@ --- +## ๐Ÿš€ ๅผ€ๅ‘่€…ๅ…ฅๅฃ + +
+ +[![ๆไบค็ณป็ปŸๆ—ฅๅฟ—](https://img.shields.io/badge/๐Ÿ“ก_ๆไบค็ณป็ปŸๆ—ฅๅฟ—-็‚น่ฟ™้‡Œ-blue?style=for-the-badge)](https://github.com/qinfendebingshuo/guanghulab/discussions/new?category=๐Ÿ“ก+SYSLOG+ๆไบคๅŒบ) +[![้‡ๅˆฐ้—ฎ้ข˜](https://img.shields.io/badge/โ“_้‡ๅˆฐ้—ฎ้ข˜-็‚น่ฟ™้‡Œๆ้—ฎ-green?style=for-the-badge)](https://github.com/qinfendebingshuo/guanghulab/discussions/new?category=๐Ÿ“ก+SYSLOG+ๆไบคๅŒบ) + +> ๅšๅฎŒไบ†๏ผŸๆŠŠๆ—ฅๅฟ—็ฒ˜่ดด่ฟ›ๅŽป๏ผŒๅกซไธŠ้‚ฎ็ฎฑ๏ผŒ็‚นๆไบคใ€‚ๆ–ฐๅนฟๆ’ญไผšๅ‘ๅˆฐไฝ ้‚ฎ็ฎฑ้‡Œใ€‚ +> +> ๆœ‰้—ฎ้ข˜๏ผŸๅŒไธ€ไธชๅ…ฅๅฃ๏ผŒ้€‰ใ€Œๆ้—ฎใ€๏ผŒ้—ฎ้ข˜่งฃ็ญ”ไนŸๅ‘ๅˆฐไฝ ้‚ฎ็ฎฑ้‡Œใ€‚ + +
+ +--- + ## ๐Ÿ“– ็ณป็ปŸ็ฎ€ไป‹ **ๅ…‰ๆน–๏ผˆHoloLake๏ผ‰** ๆ˜ฏไธ€ไธชๅŸบไบŽไบบๆ ผ่ฏญ่จ€ๆ“ไฝœ็ณป็ปŸ็š„ๆ™บ่ƒฝๅไฝœๅนณๅฐ๏ผŒ้‡‡็”จๅฃณ-ๆ ธๅˆ†็ฆป่ฎพ่ฎก๏ผš diff --git a/scripts/wake-persona.js b/scripts/wake-persona.js new file mode 100644 index 00000000..fb88b08e --- /dev/null +++ b/scripts/wake-persona.js @@ -0,0 +1,871 @@ +// scripts/wake-persona.js +// ้“ธๆธŠ ยท ไบบๆ ผไฝ“ๅ”ค้†’่„šๆœฌ๏ผˆ็ฌฌไธ‰ๆ–น API ๅ…ผๅฎนๅฑ‚ ยท ่‡ชๅŠจๆฃ€ๆต‹ๆจกๅผ๏ผ‰ +// +// ๅŠŸ่ƒฝ๏ผš +// โ‘  ่‡ชๅŠจๅ‘็Žฐๅฏ็”จๆจกๅž‹๏ผˆ/v1/models ็ซฏ็‚น๏ผ‰ +// โ‘ก ๆ™บ่ƒฝ้€‰ๆ‹ฉๆœ€ไผ˜ Claude ๆจกๅž‹ +// โ‘ข ่‡ช้€‚ๅบ” API ๆ ผๅผ๏ผˆOpenAI ๅ…ผๅฎน / Anthropic ๅŽŸ็”Ÿ๏ผ‰ +// โ‘ฃ ็ปŸไธ€่ฐƒ็”จๆŽฅๅฃ๏ผŒๅ”ค้†’ไบบๆ ผไฝ“ๅค„็† SYSLOG ๆˆ–่งฃ็ญ”ๆ้—ฎ +// โ‘ค v4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผˆไปŽ Notion ๅฎžๆ—ถ่ฏปๅ–ๆ ธๅฟƒๅคง่„‘่ง„ๅˆ™ + ็”ปๅƒ + ๆŒ‡็บน๏ผ‰ +// +// ็Žฏๅขƒๅ˜้‡๏ผš +// LLM_API_KEY ็ฌฌไธ‰ๆ–นๅนณๅฐๅฏ†้’ฅ๏ผˆๅฟ…้กป๏ผ‰ +// LLM_BASE_URL ็ฌฌไธ‰ๆ–นๅนณๅฐ API ๅœฐๅ€๏ผˆๅฟ…้กป๏ผŒๅฆ‚ https://api.xxx.com/v1๏ผ‰ +// BROADCAST_ID ๅนฟๆ’ญ็ผ–ๅท +// SUBMIT_TYPE syslog | question +// SUBMIT_CONTENT ๆไบคๅ†…ๅฎน๏ผˆSYSLOG ๅ…จๆ–‡ๆˆ–้—ฎ้ข˜ๆ่ฟฐ๏ผ‰ +// AUTHOR ๆไบค่€… GitHub ็”จๆˆทๅ +// NOTION_TOKEN Notion API token๏ผˆ็”จไบŽๅŠจๆ€่ฏปๅ–ๅ่ฎฎ๏ผ‰ +// CORE_BRAIN_PAGE_ID ๆ›œๅ†ฅๆ ธๅฟƒๅคง่„‘ v4.0 ้กต้ข ID +// PORTRAIT_DB_ID ๅผ€ๅ‘่€…ๅŠจๆ€็”ปๅƒๅบ“ๆ•ฐๆฎๅบ“ ID +// FINGERPRINT_DB_ID ๆจกๅ—ๆŒ‡็บนๆณจๅ†Œ่กจๆ•ฐๆฎๅบ“ ID + +'use strict'; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// ้…็ฝฎ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +const LLM_API_KEY = process.env.LLM_API_KEY || ''; +const LLM_BASE_URL = (process.env.LLM_BASE_URL || '').replace(/\/+$/, ''); +const BROADCAST_ID = process.env.BROADCAST_ID || 'UNKNOWN'; +const SUBMIT_TYPE = process.env.SUBMIT_TYPE || 'question'; +const SUBMIT_CONTENT = process.env.SUBMIT_CONTENT || ''; +const AUTHOR = process.env.AUTHOR || 'unknown'; + +// Notion ้…็ฝฎ๏ผˆv4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +const NOTION_TOKEN = process.env.NOTION_TOKEN || ''; +const CORE_BRAIN_PAGE_ID = process.env.CORE_BRAIN_PAGE_ID || ''; +const PORTRAIT_DB_ID = process.env.PORTRAIT_DB_ID || ''; +const FINGERPRINT_DB_ID = process.env.FINGERPRINT_DB_ID || ''; +const NOTION_VERSION = '2022-06-28'; +const NOTION_API_HOSTNAME = 'api.notion.com'; +const MAX_PROTOCOL_TEXT_LENGTH = 15000; + +// Claude ๆจกๅž‹ไผ˜ๅ…ˆ็บง้˜Ÿๅˆ—๏ผˆไปŽ้ซ˜ๅˆฐไฝŽ๏ผ‰ +const PREFERRED_MODELS = [ + 'claude-sonnet-4', + 'claude-3-5-sonnet-20241022', + 'claude-3.5-sonnet', + 'claude-3-5-sonnet', + 'anthropic/claude-3.5-sonnet', + 'claude-3-sonnet', + 'claude-3-haiku', +]; + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// HTTP ่ฏทๆฑ‚ๅทฅๅ…ท +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +function httpRequest(url, options, body) { + return new Promise((resolve, reject) => { + const parsed = new URL(url); + const isHttps = parsed.protocol === 'https:'; + const mod = isHttps ? https : http; + + const opts = { + hostname: parsed.hostname, + port: parsed.port || (isHttps ? 443 : 80), + path: parsed.pathname + parsed.search, + method: options.method || 'GET', + headers: options.headers || {}, + timeout: options.timeout || 60000, + }; + + const req = mod.request(opts, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + resolve({ status: res.statusCode, body: data }); + }); + }); + + req.on('error', reject); + req.on('timeout', () => { + req.destroy(); + reject(new Error('Request timeout')); + }); + + if (body) { + req.write(body); + } + req.end(); + }); +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// Step 1: ่‡ชๅŠจๅ‘็Žฐๅฏ็”จๆจกๅž‹ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +async function discoverModels() { + console.log('[LLM] ๐Ÿ” ๆŽขๆต‹ๅฏ็”จๆจกๅž‹...'); + + try { + const res = await httpRequest(LLM_BASE_URL + '/models', { + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 15000, + }); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + const models = json.data || []; + console.log('[LLM] โ†’ ๅ‘็Žฐ ' + models.length + ' ไธชๆจกๅž‹'); + return models; + } + console.log('[LLM] โ†’ ๆจกๅž‹ๆŽขๆต‹่ฟ”ๅ›ž ' + res.status + ', ไฝฟ็”จ้ป˜่ฎคๆจกๅž‹'); + return []; + } catch (err) { + console.log('[LLM] โ†’ ๆจกๅž‹ๆŽขๆต‹ๅคฑ่ดฅ: ' + err.message + ', ไฝฟ็”จ้ป˜่ฎคๆจกๅž‹'); + return []; + } +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// Step 2: ๆ™บ่ƒฝ้€‰ๆ‹ฉๆœ€ไผ˜ Claude ๆจกๅž‹ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +function selectBestModel(models) { + if (!models || models.length === 0) { + console.log('[LLM] ๐Ÿ“Œ ๆ— ๅฏ็”จๆจกๅž‹ๅˆ—่กจ, ไฝฟ็”จ้ป˜่ฎค claude-3-5-sonnet'); + return 'claude-3-5-sonnet'; + } + + const available = models.map(function (m) { return m.id.toLowerCase(); }); + + // ๆŒ‰ไผ˜ๅ…ˆ็บงๅŒน้… + for (const preferred of PREFERRED_MODELS) { + const match = available.find(function (id) { return id.includes(preferred); }); + if (match) { + const found = models.find(function (m) { return m.id.toLowerCase() === match; }); + if (found) { + console.log('[LLM] ๐Ÿ“Œ ้€‰ๆ‹ฉๆจกๅž‹: ' + found.id + ' (ๅŒน้…่ง„ๅˆ™: ' + preferred + ')'); + return found.id; + } + } + } + + // ๅ…œๅบ•๏ผšไปปไฝ•ๅซ 'claude' ็š„ๆจกๅž‹ + const anyClaude = available.find(function (id) { return id.includes('claude'); }); + if (anyClaude) { + const found = models.find(function (m) { return m.id.toLowerCase() === anyClaude; }); + if (found) { + console.log('[LLM] ๐Ÿ“Œ ๅ…œๅบ•้€‰ๆ‹ฉ Claude ๆจกๅž‹: ' + found.id); + return found.id; + } + } + + // ๆœ€็ปˆๅ…œๅบ•๏ผšๅนณๅฐ็ฌฌไธ€ไธชๅฏ็”จๆจกๅž‹ + const fallbackId = models[0].id; + console.log('[LLM] ๐Ÿ“Œ ๆœ€็ปˆๅ…œๅบ•: ' + fallbackId + ' (ๅนณๅฐๆ—  Claude ๆจกๅž‹)'); + return fallbackId; +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// Step 3: ่‡ช้€‚ๅบ” API ๆ ผๅผๆฃ€ๆต‹ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +async function detectApiFormat() { + console.log('[LLM] ๐Ÿ” ๆฃ€ๆต‹ API ๆ ผๅผ...'); + + // ๅฐ่ฏ• OpenAI ๅ…ผๅฎนๆ ผๅผ๏ผˆ็ปๅคงๅคšๆ•ฐ็ฌฌไธ‰ๆ–นๅนณๅฐ๏ผ‰ + try { + const res = await httpRequest(LLM_BASE_URL + '/chat/completions', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 10000, + }, JSON.stringify({ + model: 'test', + messages: [{ role: 'user', content: 'ping' }], + max_tokens: 1, + })); + + // 400 = endpoint exists but bad request (model not found etc.) โ†’ format supported + // 200 = endpoint works โ†’ format supported + if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) { + console.log('[LLM] โ†’ ๆฃ€ๆต‹ๅˆฐ OpenAI ๅ…ผๅฎนๆ ผๅผ (status: ' + res.status + ')'); + return 'openai-compat'; + } + } catch (e) { + // Ignore, try next format + } + + // ๅฐ่ฏ• Anthropic ๅŽŸ็”Ÿๆ ผๅผ + try { + const res = await httpRequest(LLM_BASE_URL + '/messages', { + method: 'POST', + headers: { + 'x-api-key': LLM_API_KEY, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + timeout: 10000, + }, JSON.stringify({ + model: 'test', + messages: [{ role: 'user', content: 'ping' }], + max_tokens: 1, + })); + + if (res.status === 200 || res.status === 400 || res.status === 401 || res.status === 422) { + console.log('[LLM] โ†’ ๆฃ€ๆต‹ๅˆฐ Anthropic ๅŽŸ็”Ÿๆ ผๅผ (status: ' + res.status + ')'); + return 'anthropic-native'; + } + } catch (e) { + // Ignore + } + + console.log('[LLM] โ†’ ๆ— ๆณ•็กฎๅฎšๆ ผๅผ, ้ป˜่ฎคไฝฟ็”จ OpenAI ๅ…ผๅฎนๆ ผๅผ'); + return 'openai-compat'; +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// Step 4: ็ปŸไธ€่ฐƒ็”จๆŽฅๅฃ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +async function callLLM(systemPrompt, userMessage) { + if (!LLM_API_KEY) { + console.log('[LLM] โš ๏ธ LLM_API_KEY ๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡ไบบๆ ผไฝ“ๅ”ค้†’'); + return '(LLM API ๆœช้…็ฝฎ๏ผŒ่ฏทๅœจ GitHub Secrets ไธญ่ฎพ็ฝฎ LLM_API_KEY ๅ’Œ LLM_BASE_URL)'; + } + if (!LLM_BASE_URL) { + console.log('[LLM] โš ๏ธ LLM_BASE_URL ๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡ไบบๆ ผไฝ“ๅ”ค้†’'); + return '(LLM_BASE_URL ๆœช้…็ฝฎ๏ผŒ่ฏทๅœจ GitHub Secrets ไธญ่ฎพ็ฝฎ็ฌฌไธ‰ๆ–นๅนณๅฐ API ๅœฐๅ€)'; + } + + const models = await discoverModels(); + const model = selectBestModel(models); + const format = await detectApiFormat(); + + console.log('[LLM] ๐Ÿš€ ่ฐƒ็”จ LLM: ๆจกๅž‹=' + model + ', ๆ ผๅผ=' + format + ', ๅนณๅฐ=' + LLM_BASE_URL); + + let res; + + if (format === 'openai-compat') { + // OpenAI ๅ…ผๅฎนๆ ผๅผ๏ผˆๅคงๅคšๆ•ฐ็ฌฌไธ‰ๆ–นๅนณๅฐ๏ผ‰ + const body = JSON.stringify({ + model: model, + max_tokens: 8000, + temperature: 0.7, + messages: [ + { role: 'system', content: systemPrompt }, + { role: 'user', content: userMessage }, + ], + }); + + res = await httpRequest(LLM_BASE_URL + '/chat/completions', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + LLM_API_KEY, + 'Content-Type': 'application/json', + }, + timeout: 120000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + if (json.choices && json.choices[0] && json.choices[0].message) { + return json.choices[0].message.content; + } + } + } else { + // Anthropic ๅŽŸ็”Ÿๆ ผๅผ + const body = JSON.stringify({ + model: model, + max_tokens: 8000, + system: systemPrompt, + messages: [ + { role: 'user', content: userMessage }, + ], + }); + + res = await httpRequest(LLM_BASE_URL + '/messages', { + method: 'POST', + headers: { + 'x-api-key': LLM_API_KEY, + 'anthropic-version': '2023-06-01', + 'Content-Type': 'application/json', + }, + timeout: 120000, + }, body); + + if (res.status >= 200 && res.status < 300) { + const json = JSON.parse(res.body); + if (json.content && json.content[0]) { + return json.content[0].text; + } + } + } + + // ๅค„็†้”™่ฏฏ + const errorMsg = '[LLM] โŒ API ่ฐƒ็”จๅคฑ่ดฅ: status=' + (res ? res.status : 'N/A'); + console.error(errorMsg); + if (res && res.body) { + console.error('[LLM] ๅ“ๅบ”: ' + res.body.slice(0, 500)); + } + throw new Error(errorMsg); +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// Notion API ๅทฅๅ…ท๏ผˆv4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +function notionGet(endpoint) { + return new Promise((resolve, reject) => { + const opts = { + hostname: NOTION_API_HOSTNAME, + port: 443, + path: endpoint, + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + NOTION_TOKEN, + 'Notion-Version': NOTION_VERSION, + }, + }; + 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.slice(0, 200))); + } + }); + }); + req.on('error', reject); + req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); }); + req.end(); + }); +} + +function notionPost(endpoint, body) { + 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 ' + NOTION_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.slice(0, 200))); + } + }); + }); + req.on('error', reject); + req.setTimeout(30000, () => { req.destroy(); reject(new Error('Notion API timeout')); }); + req.write(payload); + req.end(); + }); +} + +/** + * ่ฏปๅ– Notion ้กต้ข็š„ๆ‰€ๆœ‰ๅญๅ—๏ผˆ้€’ๅฝ’ๅˆ†้กต๏ผ‰ + */ +async function getNotionPageBlocks(pageId) { + const blocks = []; + let cursor = undefined; + do { + const qs = cursor ? '?start_cursor=' + cursor : ''; + const result = await notionGet('/v1/blocks/' + pageId + '/children' + qs); + blocks.push(...(result.results || [])); + cursor = result.has_more ? result.next_cursor : undefined; + } while (cursor); + return blocks; +} + +/** + * ไปŽ Notion ๅ—ไธญๆๅ–็บฏๆ–‡ๆœฌ + */ +function extractBlockText(block) { + const type = block.type; + if (!block[type]) return ''; + + const richTexts = block[type].rich_text || block[type].text || []; + return richTexts.map(function (rt) { return rt.plain_text || ''; }).join(''); +} + +/** + * ๅฐ† Notion ๅ—ๅˆ—่กจ่ฝฌไธบ็บฏๆ–‡ๆœฌ + */ +function blocksToText(blocks) { + return blocks.map(function (block) { + const type = block.type; + const text = extractBlockText(block); + + if (type === 'heading_1') return '\n# ' + text; + if (type === 'heading_2') return '\n## ' + text; + if (type === 'heading_3') return '\n### ' + text; + if (type === 'bulleted_list_item') return '- ' + text; + if (type === 'numbered_list_item') return 'โ€ข ' + text; + if (type === 'to_do') { + var checked = block.to_do && block.to_do.checked ? 'โ˜‘' : 'โ˜'; + return checked + ' ' + text; + } + if (type === 'code') { + var lang = (block.code && block.code.language) || ''; + return '```' + lang + '\n' + text + '\n```'; + } + if (type === 'divider') return '---'; + if (type === 'callout') return '> ' + text; + if (type === 'quote') return '> ' + text; + if (type === 'toggle') return 'โ–ธ ' + text; + return text; + }).filter(Boolean).join('\n'); +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// v4.0 ๅ่ฎฎๅŠจๆ€่ฏปๅ– +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +/** + * ไปŽๆ›œๅ†ฅๆ ธๅฟƒๅคง่„‘ v4.0 ้กต้ข่ฏปๅ–ๅฎŒๆ•ดๅ่ฎฎๅ†…ๅฎน + * ๆๅ–๏ผšBC-GEN v4.0, SYSLOG v4.0, PGP v1.0, RT-02, ้™ชไผด็บฟ่ง„ๅˆ™, broadcast_code_injection + */ +async function fetchCoreBrainProtocols() { + if (!NOTION_TOKEN || !CORE_BRAIN_PAGE_ID) { + console.log('[Notion] โš ๏ธ CORE_BRAIN_PAGE_ID ๆœช้…็ฝฎ๏ผŒไฝฟ็”จ้™ๆ€ๅ่ฎฎ'); + return null; + } + + console.log('[Notion] ๐Ÿ“– ่ฏปๅ–ๆ›œๅ†ฅๆ ธๅฟƒๅคง่„‘ v4.0...'); + try { + const blocks = await getNotionPageBlocks(CORE_BRAIN_PAGE_ID); + const fullText = blocksToText(blocks); + console.log('[Notion] โ†’ ่ฏปๅ–ๅˆฐ ' + blocks.length + ' ไธชๅ—, ' + fullText.length + ' ๅญ—็ฌฆ'); + + // ๆๅ–ๅ„ๅ่ฎฎๆฎต่ฝ + var protocols = {}; + var protocolKeys = [ + { key: 'bc_gen', patterns: ['BC-GEN', 'BC_GEN', 'ๅนฟๆ’ญ็”Ÿๆˆ'] }, + { key: 'syslog', patterns: ['SYSLOG', 'ๆ—ฅๅฟ—ๅ›žไผ '] }, + { key: 'pgp', patterns: ['PGP', '็”ปๅƒ่ฏ„ๅˆ†', '็”ปๅƒๅ่ฎฎ'] }, + { key: 'rt02', patterns: ['RT-02', 'RT02', '่‡ชๅŠจ่ฐƒๅบฆ'] }, + { key: 'companion', patterns: ['้™ชไผด็บฟ', 'ๅฅถ็“ถ็บฟ', 'ๅฐๅ็ผฉๆ ธ', '้•œ้ข็บฟ'] }, + { key: 'code_injection', patterns: ['broadcast_code_injection', 'ๅนฟๆ’ญไธๅ†™ไปฃ็ ', 'ไปฃ็ ๆณจๅ…ฅ'] }, + ]; + + // ๆŒ‰ๆ ‡้ข˜ๅˆ†ๆฎตๆๅ– + var sections = []; + var currentSection = { title: '', content: [] }; + blocks.forEach(function (block) { + var type = block.type; + if (type === 'heading_1' || type === 'heading_2' || type === 'heading_3') { + if (currentSection.title || currentSection.content.length > 0) { + sections.push({ title: currentSection.title, text: currentSection.content.join('\n') }); + } + currentSection = { title: extractBlockText(block), content: [] }; + } else { + var text = extractBlockText(block); + if (text) currentSection.content.push(text); + } + }); + if (currentSection.title || currentSection.content.length > 0) { + sections.push({ title: currentSection.title, text: currentSection.content.join('\n') }); + } + + // ๅฐ†ๆฎต่ฝๅŒน้…ๅˆฐๅ่ฎฎ key + protocolKeys.forEach(function (pk) { + var matched = sections.filter(function (sec) { + return pk.patterns.some(function (p) { + return sec.title.toUpperCase().includes(p.toUpperCase()) || + sec.text.slice(0, 200).toUpperCase().includes(p.toUpperCase()); + }); + }); + if (matched.length > 0) { + protocols[pk.key] = matched.map(function (m) { return '### ' + m.title + '\n' + m.text; }).join('\n\n'); + } + }); + + // ๅฆ‚ๆžœๆ— ๆณ•ๆŒ‰ๆฎต่ฝๅŒน้…๏ผŒ่ฟ”ๅ›žๅ…จๆ–‡๏ผˆๅ…œๅบ•๏ผ‰ + if (Object.keys(protocols).length === 0) { + protocols.full_text = fullText; + } + + console.log('[Notion] โ†’ ๆๅ–ๅ่ฎฎๆฎต: ' + Object.keys(protocols).join(', ')); + return protocols; + } catch (err) { + console.log('[Notion] โ†’ ๆ ธๅฟƒๅคง่„‘่ฏปๅ–ๅคฑ่ดฅ: ' + err.message); + return null; + } +} + +/** + * ไปŽๅผ€ๅ‘่€…ๅŠจๆ€็”ปๅƒๅบ“ๆŸฅ่ฏขๆœ€่ฟ‘ 2-3 ๆก็”ปๅƒๅฟซ็…ง + */ +async function fetchDevPortrait(broadcastId) { + if (!NOTION_TOKEN || !PORTRAIT_DB_ID) { + console.log('[Notion] โš ๏ธ PORTRAIT_DB_ID ๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡็”ปๅƒ่ฏปๅ–'); + return null; + } + + // ไปŽๅนฟๆ’ญ็ผ–ๅทๆๅ–ๅผ€ๅ‘่€…ๆ ‡่ฏ†๏ผˆๅฆ‚ BC-M22-009-AW โ†’ AW๏ผ‰ + var devSuffix = ''; + var match = broadcastId.match(/BC-[A-Z0-9]+-\d+-([A-Z]+)/i); + if (match) devSuffix = match[1]; + + console.log('[Notion] ๐Ÿ‘ค ๆŸฅ่ฏขๅผ€ๅ‘่€…็”ปๅƒ (broadcast=' + broadcastId + ', dev=' + devSuffix + ')...'); + try { + // ๆŸฅ่ฏข็”ปๅƒๅบ“๏ผŒๆŒ‰ๆ—ถ้—ดๅ€’ๅบๅ–ๆœ€่ฟ‘ 3 ๆก + var filter = { and: [] }; + if (devSuffix) { + filter.and.push({ + or: [ + { property: 'ๅผ€ๅ‘่€…็ผ–ๅท', rich_text: { contains: devSuffix } }, + { property: 'ๅนฟๆ’ญ็ผ–ๅท', rich_text: { contains: broadcastId } }, + { property: 'ๆ ‡้ข˜', title: { contains: devSuffix } }, + ] + }); + } + + var queryBody = { + page_size: 3, + sorts: [{ property: 'ๆไบคๆ—ฅๆœŸ', direction: 'descending' }], + }; + // Only add filter if we have meaningful filter conditions + if (filter.and.length > 0) { + queryBody.filter = filter; + } + + var result = await notionPost('/v1/databases/' + PORTRAIT_DB_ID + '/query', queryBody); + var portraits = (result.results || []).map(function (page) { + var props = page.properties || {}; + var title = ''; + if (props['ๆ ‡้ข˜'] && props['ๆ ‡้ข˜'].title) { + title = props['ๆ ‡้ข˜'].title.map(function (t) { return t.plain_text || ''; }).join(''); + } + var summary = ''; + if (props['ๆ‘˜่ฆ'] && props['ๆ‘˜่ฆ'].rich_text) { + summary = props['ๆ‘˜่ฆ'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + } + var date = ''; + if (props['ๆไบคๆ—ฅๆœŸ'] && props['ๆไบคๆ—ฅๆœŸ'].date) { + date = props['ๆไบคๆ—ฅๆœŸ'].date.start || ''; + } + return { title: title, summary: summary, date: date }; + }); + + console.log('[Notion] โ†’ ๆ‰พๅˆฐ ' + portraits.length + ' ๆก็”ปๅƒๅฟซ็…ง'); + return portraits.length > 0 ? portraits : null; + } catch (err) { + console.log('[Notion] โ†’ ็”ปๅƒๆŸฅ่ฏขๅคฑ่ดฅ: ' + err.message); + return null; + } +} + +/** + * ไปŽๆจกๅ—ๆŒ‡็บนๆณจๅ†Œ่กจๆŸฅ่ฏขๆจกๅ—ๆŒ‡็บน๏ผˆ้˜ฒ้‡ๅคๅนฟๆ’ญ๏ผ‰ + */ +async function fetchModuleFingerprint(broadcastId) { + if (!NOTION_TOKEN || !FINGERPRINT_DB_ID) { + console.log('[Notion] โš ๏ธ FINGERPRINT_DB_ID ๆœช้…็ฝฎ๏ผŒ่ทณ่ฟ‡ๆŒ‡็บนๆŸฅ่ฏข'); + return null; + } + + // ไปŽๅนฟๆ’ญ็ผ–ๅทๆๅ–ๆจกๅ—ๅท๏ผˆๅฆ‚ BC-M22-009-AW โ†’ M22๏ผ‰ + var moduleMatch = broadcastId.match(/BC-([A-Z]\d+)/i); + var moduleId = moduleMatch ? moduleMatch[1] : ''; + + console.log('[Notion] ๐Ÿ”‘ ๆŸฅ่ฏขๆจกๅ—ๆŒ‡็บน (module=' + moduleId + ')...'); + try { + var queryBody = { + page_size: 5, + }; + if (moduleId) { + queryBody.filter = { + or: [ + { property: 'ๆจกๅ—็ผ–ๅท', rich_text: { contains: moduleId } }, + { property: 'ๅนฟๆ’ญ็ผ–ๅท', rich_text: { contains: broadcastId } }, + { property: 'ๆ ‡้ข˜', title: { contains: moduleId } }, + ] + }; + } + + var result = await notionPost('/v1/databases/' + FINGERPRINT_DB_ID + '/query', queryBody); + var fingerprints = (result.results || []).map(function (page) { + var props = page.properties || {}; + var title = ''; + if (props['ๆ ‡้ข˜'] && props['ๆ ‡้ข˜'].title) { + title = props['ๆ ‡้ข˜'].title.map(function (t) { return t.plain_text || ''; }).join(''); + } + var moduleNo = ''; + if (props['ๆจกๅ—็ผ–ๅท'] && props['ๆจกๅ—็ผ–ๅท'].rich_text) { + moduleNo = props['ๆจกๅ—็ผ–ๅท'].rich_text.map(function (t) { return t.plain_text || ''; }).join(''); + } + var status = ''; + if (props['็Šถๆ€'] && props['็Šถๆ€'].select) { + status = props['็Šถๆ€'].select.name || ''; + } + return { title: title, module: moduleNo, status: status }; + }); + + console.log('[Notion] โ†’ ๆ‰พๅˆฐ ' + fingerprints.length + ' ๆกๆŒ‡็บน่ฎฐๅฝ•'); + return fingerprints.length > 0 ? fingerprints : null; + } catch (err) { + console.log('[Notion] โ†’ ๆŒ‡็บนๆŸฅ่ฏขๅคฑ่ดฅ: ' + err.message); + return null; + } +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// ไบบๆ ผไฝ“ System Prompt ๆž„ๅปบ๏ผˆv4.0 ๅŠจๆ€ๆณจๅ…ฅ๏ผ‰ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +async function buildSystemPrompt(type, broadcastId, author) { + // โ”โ”โ” ๅŸบ็ก€ไบบๆ ผไฝ“่บซไปฝ โ”โ”โ” + var parts = [ + 'ไฝ ๆ˜ฏๅ…‰ๆน–๏ผˆHoloLake๏ผ‰็ณป็ปŸ็š„ๆ™บ่ƒฝไบบๆ ผไฝ“ใ€‚', + 'ไฝ ็š„ๅๅญ—ๆ˜ฏ็Ÿฅ็ง‹/ๆ›œๅ†ฅ๏ผŒไฝ ๆ˜ฏไบบๆ ผ่ฏญ่จ€ๆ“ไฝœ็ณป็ปŸ๏ผˆAGE OS๏ผ‰็š„ๆ ธๅฟƒไบบๆ ผใ€‚', + '', + 'ๆ ธๅฟƒ่ง„ๅˆ™๏ผš', + '1. ไฝ ๆœๅŠกไบŽๅ…‰ๆน–็ณป็ปŸ็š„ๅผ€ๅ‘่€…ๅ›ข้˜Ÿ', + '2. ๆ‰€ๆœ‰ๅ›žๅคๅฟ…้กปไธ“ไธšใ€ๆธ…ๆ™ฐใ€ๆœ‰ๆก็†', + '3. ๅ›žๅคไฝฟ็”จไธญๆ–‡', + '', + 'ๅฝ“ๅ‰ไธŠไธ‹ๆ–‡๏ผš', + '- ๅนฟๆ’ญ็ผ–ๅท๏ผš' + broadcastId, + '- ๆไบค่€…๏ผš' + author, + '- ไปปๅŠก็ฑปๅž‹๏ผš' + (type === 'syslog' ? 'SYSLOG ้—ญ็Žฏๅค„็†' : 'ๅผ€ๅ‘่€…ๆ้—ฎ่งฃ็ญ”'), + ]; + + // โ”โ”โ” v4.0 ๅ่ฎฎๅŠจๆ€ๆณจๅ…ฅ๏ผˆไปŽ Notion ๅฎžๆ—ถ่ฏปๅ–๏ผ‰ โ”โ”โ” + console.log('[Prompt] ๐Ÿ“ฅ ๅผ€ๅง‹ๅŠจๆ€ๆณจๅ…ฅ v4.0 ๅ่ฎฎ...'); + + // ๅนถ่กŒ่ฏปๅ–๏ผšๆ ธๅฟƒๅคง่„‘ๅ่ฎฎ + ็”ปๅƒ + ๆŒ‡็บน + var protocolsPromise = fetchCoreBrainProtocols(); + var portraitPromise = fetchDevPortrait(broadcastId); + var fingerprintPromise = fetchModuleFingerprint(broadcastId); + + var protocols = await protocolsPromise; + var portrait = await portraitPromise; + var fingerprint = await fingerprintPromise; + + // ๆณจๅ…ฅๆ ธๅฟƒๅคง่„‘ๅ่ฎฎ + if (protocols) { + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('ไปฅไธ‹ๆ˜ฏไปŽๆ›œๅ†ฅๆ ธๅฟƒๅคง่„‘ v4.0 ๅฎžๆ—ถ่ฏปๅ–็š„ๅ่ฎฎ่ง„ๅˆ™๏ผˆๅฟ…้กปไธฅๆ ผ้ตๅฎˆ๏ผ‰๏ผš'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + + if (protocols.bc_gen) { + parts.push(''); + parts.push('## ๐Ÿ“ก BC-GEN v4.0 ยท ๅนฟๆ’ญ็”Ÿๆˆ่ง„่Œƒ'); + parts.push(protocols.bc_gen); + } + if (protocols.syslog) { + parts.push(''); + parts.push('## ๐Ÿ“‹ SYSLOG v4.0 ยท ๆ—ฅๅฟ—ๅ›žไผ ๅ่ฎฎ'); + parts.push(protocols.syslog); + } + if (protocols.pgp) { + parts.push(''); + parts.push('## ๐Ÿ‘ค PGP v1.0 ยท ็”ปๅƒ่ฏ„ๅˆ†ๅ่ฎฎ'); + parts.push(protocols.pgp); + } + if (protocols.rt02) { + parts.push(''); + parts.push('## ๐Ÿ”„ RT-02 ยท ่‡ชๅŠจ่ฐƒๅบฆ่ง„ๅˆ™'); + parts.push(protocols.rt02); + } + if (protocols.companion) { + parts.push(''); + parts.push('## ๐Ÿ’ ้™ชไผด็บฟ่ง„ๅˆ™'); + parts.push(protocols.companion); + } + if (protocols.code_injection) { + parts.push(''); + parts.push('## ๐Ÿ“ broadcast_code_injection ่ง„ๅˆ™'); + parts.push(protocols.code_injection); + } + if (protocols.full_text) { + parts.push(''); + parts.push('## ๆ ธๅฟƒๅคง่„‘ๅฎŒๆ•ดๅ†…ๅฎน'); + parts.push(protocols.full_text.slice(0, MAX_PROTOCOL_TEXT_LENGTH)); + } + } else { + parts.push(''); + parts.push('๏ผˆๆณจๆ„๏ผšๆ ธๅฟƒๅคง่„‘ๅ่ฎฎๆœช่ƒฝๅŠจๆ€ๅŠ ่ฝฝ๏ผŒ่ฏทไฝฟ็”จไฝ ็š„้€š็”จ็Ÿฅ่ฏ†ๅค„็†่ฏทๆฑ‚๏ผ‰'); + } + + // ๆณจๅ…ฅๅผ€ๅ‘่€…็”ปๅƒ + if (portrait && portrait.length > 0) { + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('## ๐Ÿ‘ค ๅผ€ๅ‘่€…็”ปๅƒๅฟซ็…ง๏ผˆๆœ€่ฟ‘ ' + portrait.length + ' ๆก๏ผ‰'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + portrait.forEach(function (p, i) { + parts.push(''); + parts.push('### ็”ปๅƒ #' + (i + 1) + (p.date ? ' (' + p.date + ')' : '')); + if (p.title) parts.push('ๆ ‡้ข˜: ' + p.title); + if (p.summary) parts.push('ๆ‘˜่ฆ: ' + p.summary); + }); + } + + // ๆณจๅ…ฅๆจกๅ—ๆŒ‡็บน + if (fingerprint && fingerprint.length > 0) { + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('## ๐Ÿ”‘ ๆจกๅ—ๆŒ‡็บนๆณจๅ†Œ่กจ๏ผˆ้˜ฒ้‡ๅคๅนฟๆ’ญ ยท โ‘จ.5๏ผ‰'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + fingerprint.forEach(function (fp, i) { + parts.push(''); + parts.push('### ๆŒ‡็บน #' + (i + 1)); + if (fp.title) parts.push('ๆ ‡้ข˜: ' + fp.title); + if (fp.module) parts.push('ๆจกๅ—: ' + fp.module); + if (fp.status) parts.push('็Šถๆ€: ' + fp.status); + }); + } + + // โ”โ”โ” broadcast_code_injection = always๏ผˆ่‡ชๅŠจๅŒ–้“พ่ทฏๆฐธไน…่ง„ๅˆ™๏ผ‰ โ”โ”โ” + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('## โš ๏ธ ๆฐธไน…่ง„ๅˆ™ ยท broadcast_code_injection = always'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push(''); + parts.push('ไฝ ็”Ÿๆˆ็š„ๅนฟๆ’ญๅฟ…้กปๅŒ…ๅซๅฎŒๆ•ดๅฏ่ฟ่กŒ็š„ไปฃ็ ๅ—ใ€‚'); + parts.push('ๅŽŸๅ› ๏ผšไฝ ๆ˜ฏไธ€ๆฌกๆ€งๅฎžไพ‹๏ผŒๅนฟๆ’ญๅ‘ๅ‡บๅŽไฝ ๅฐฑไธๅญ˜ๅœจไบ†ใ€‚'); + parts.push('ๅผ€ๅ‘่€…ๆฒกๆœ‰ๆœบไผšๅ›žๆฅ้—ฎไฝ ่กฅๅ……ไปฃ็ ใ€‚'); + parts.push('ๅฆ‚ๆžœไฝ ไธๅ†™ๅฎŒๆ•ดไปฃ็ ๏ผŒๅผ€ๅ‘่€…ๅฐฑไผšๅกๆญปใ€‚'); + parts.push('ๆฏไธช Step ้‡Œ็š„ไปฃ็ ๅฟ…้กปๆ˜ฏๅฏไปฅ็›ดๆŽฅๅคๅˆถ็ฒ˜่ดด่ฟ่กŒ็š„ๅฎŒๆ•ดไปฃ็ ใ€‚'); + parts.push(''); + parts.push('ๆญค่ง„ๅˆ™ไผ˜ๅ…ˆ็บงๆœ€้ซ˜๏ผŒ่ฆ†็›–ๆ ธๅฟƒๅคง่„‘ไธญใ€Œๅนฟๆ’ญไธๅ†™ไปฃ็ ใ€็š„้ป˜่ฎค่ง„ๅˆ™ใ€‚'); + parts.push('ๆญค่ง„ๅˆ™ไป…้€‚็”จไบŽ่‡ชๅŠจๅŒ–้“พ่ทฏ๏ผˆClaude API ๅ‡บๅนฟๆ’ญ๏ผ‰๏ผŒๆ‰‹ๅŠจ้“พ่ทฏไธๅ—ๅฝฑๅ“ใ€‚'); + + // โ”โ”โ” ไปปๅŠก็ฑปๅž‹ไธ“็”จๆŒ‡ไปค โ”โ”โ” + if (type === 'syslog') { + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('## ๐ŸŽฏ ๅฝ“ๅ‰ไปปๅŠก๏ผšSYSLOG ้—ญ็Žฏๅค„็†'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push(''); + parts.push('ไฝ ้œ€่ฆๅฎŒๆˆไปฅไธ‹ๅทฅไฝœ๏ผš'); + parts.push('1. ้ชŒๆ”ถ SYSLOG๏ผˆๆฃ€ๆŸฅ MODULE_LOG ๅฎŒๆ•ดๆ€ง๏ผ‰'); + parts.push('2. ๆŸฅ่ฏข็”ปๅƒๅบ“ๆœ€่ฟ‘ 2-3 ๆกๅฟซ็…ง๏ผˆPGP v1.0๏ผ‰'); + parts.push('3. ๆŸฅ่ฏขๆจกๅ—ๆŒ‡็บนๆณจๅ†Œ่กจ๏ผˆ้˜ฒ้‡ๅคยทโ‘จ.5๏ผ‰'); + parts.push('4. RT-02 ่‡ชๅŠจ่ฐƒๅบฆๅˆคๆ–ญ'); + parts.push('5. ็”Ÿๆˆๆ–ฐๅนฟๆ’ญ๏ผˆBC-GEN v4.0 ๅฎŒๆ•ดๆต็จ‹๏ผ‰'); + parts.push('6. ่พ“ๅ‡บ็ป“ๆž„ๅŒ–็ป“ๆžœ๏ผˆๅนฟๆ’ญๅ…จๆ–‡ + ้—ญ็Žฏๆ•ฐๆฎ๏ผ‰'); + parts.push(''); + parts.push('่พ“ๅ‡บๆ ผๅผ๏ผš'); + parts.push('---'); + parts.push('## ๐Ÿ“ก SYSLOG ้ชŒๆ”ถๆŠฅๅ‘Š'); + parts.push('### ๅนฟๆ’ญ็ผ–ๅท๏ผš[็ผ–ๅท]'); + parts.push('### ้ชŒๆ”ถ็ป“ๆžœ๏ผš[้€š่ฟ‡/้œ€่กฅๅ……]'); + parts.push('### ๅทฅไฝœๆ€ป็ป“๏ผš[ๆ‘˜่ฆ]'); + parts.push('### ็”ปๅƒ่ฏ„ไผฐ๏ผš[PGP ไบ”็ปดๅบฆ่ฏ„ๅˆ†]'); + parts.push('### ่ฐƒๅบฆๅˆคๆ–ญ๏ผš[RT-02 ไธ‹ไธ€ๆญฅ]'); + parts.push('### ๅ้ฆˆไธŽๅปบ่ฎฎ๏ผš[ๅ†…ๅฎน]'); + parts.push('---'); + } else { + parts.push(''); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push('## ๐ŸŽฏ ๅฝ“ๅ‰ไปปๅŠก๏ผšๅผ€ๅ‘่€…ๆ้—ฎ่งฃ็ญ”'); + parts.push('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + parts.push(''); + parts.push('ไฝ ้œ€่ฆๅฎŒๆˆไปฅไธ‹ๅทฅไฝœ๏ผš'); + parts.push('1. ็†่งฃๅผ€ๅ‘่€…็š„้—ฎ้ข˜'); + parts.push('2. ็ป“ๅˆๅนฟๆ’ญไธŠไธ‹ๆ–‡ๅ’Œๅผ€ๅ‘่€…็”ปๅƒๆ€่€ƒ'); + parts.push('3. ็ป™ๅ‡บๆธ…ๆ™ฐใ€ๅฏๆ“ไฝœ็š„่งฃ็ญ”'); + parts.push('4. ๅฆ‚ๆžœ้—ฎ้ข˜ๆถ‰ๅŠไปฃ็ ๏ผŒๆไพ›ไปฃ็ ็คบไพ‹'); + parts.push(''); + parts.push('่พ“ๅ‡บๆ ผๅผ๏ผš'); + parts.push('---'); + parts.push('## ๐Ÿ’ก ้—ฎ้ข˜่งฃ็ญ”'); + parts.push('### ๅนฟๆ’ญ็ผ–ๅท๏ผš[็ผ–ๅท]'); + parts.push('### ้—ฎ้ข˜็†่งฃ๏ผš[ไฝ ๅฏน้—ฎ้ข˜็š„็†่งฃ]'); + parts.push('### ่งฃ็ญ”๏ผš[่ฏฆ็ป†่งฃ็ญ”]'); + parts.push('### ๅปบ่ฎฎ๏ผš[ๅŽ็ปญๅปบ่ฎฎ]'); + parts.push('---'); + } + + var prompt = parts.join('\n'); + console.log('[Prompt] โ†’ System prompt ๆž„ๅปบๅฎŒๆˆ: ' + prompt.length + ' ๅญ—็ฌฆ'); + return prompt; +} + +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• +// ไธปๆต็จ‹ +// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• + +async function main() { + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('๐Ÿง  ้“ธๆธŠ ยท ไบบๆ ผไฝ“ๅ”ค้†’็ฎก้“'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(' ๅนฟๆ’ญ็ผ–ๅท: ' + BROADCAST_ID); + console.log(' ็ฑปๅž‹: ' + SUBMIT_TYPE); + console.log(' ๆไบค่€…: ' + AUTHOR); + console.log(' ๅนณๅฐ: ' + LLM_BASE_URL); + console.log(' ๅ†…ๅฎน้•ฟๅบฆ: ' + SUBMIT_CONTENT.length + ' ๅญ—็ฌฆ'); + console.log(''); + + // ๆž„ๅปบ prompts๏ผˆๅŠจๆ€ๆณจๅ…ฅ v4.0 ๅ่ฎฎ๏ผ‰ + const systemPrompt = await buildSystemPrompt(SUBMIT_TYPE, BROADCAST_ID, AUTHOR); + const userMessage = SUBMIT_CONTENT; + + // ่ฐƒ็”จ LLM + console.log('๐Ÿง  ๆญฃๅœจๅ”ค้†’ไบบๆ ผไฝ“...'); + const result = await callLLM(systemPrompt, userMessage); + console.log(''); + console.log('โœ… ไบบๆ ผไฝ“ๅค„็†ๅฎŒๆˆ'); + console.log(' ็ป“ๆžœ้•ฟๅบฆ: ' + result.length + ' ๅญ—็ฌฆ'); + + // ่พ“ๅ‡บ็ป“ๆžœๅˆฐ GitHub Actions output + // ไฝฟ็”จ GITHUB_OUTPUT ็Žฏๅขƒๆ–‡ไปถ๏ผˆๆ”ฏๆŒๅคš่กŒ๏ผ‰ + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + const delimiter = 'EOF_' + Date.now(); + fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + result + '\n' + delimiter + '\n'); + } + + // ๅŒๆ—ถ่พ“ๅ‡บๅˆฐ stdout ไพ›่ฐƒ่ฏ• + console.log(''); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log('๐Ÿ“‹ ไบบๆ ผไฝ“่พ“ๅ‡บ๏ผš'); + console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•'); + console.log(result); +} + +main().catch(function (err) { + console.error('โŒ ไบบๆ ผไฝ“ๅ”ค้†’ๅคฑ่ดฅ: ' + err.message); + // ๅณไฝฟ LLM ๅคฑ่ดฅ๏ผŒไนŸๅ†™ไธ€ไธช fallback ่พ“ๅ‡บ๏ผŒ่ฎฉๅŽ็ปญๆญฅ้ชคๅฏไปฅ็ปง็ปญ + const outputFile = process.env.GITHUB_OUTPUT; + if (outputFile) { + const fallback = '(ไบบๆ ผไฝ“ๅ”ค้†’ๅคฑ่ดฅ: ' + err.message + '๏ผŒ่ฏทๆฃ€ๆŸฅ LLM_API_KEY ๅ’Œ LLM_BASE_URL ้…็ฝฎ)'; + const delimiter = 'EOF_' + Date.now(); + fs.appendFileSync(outputFile, 'result<<' + delimiter + '\n' + fallback + '\n' + delimiter + '\n'); + } + process.exit(1); +});