根本原因: 原本的「今日羽球報名」bot 寫在 Node-RED 的 function 節點裡,不易測試與擴充; 羽球團又需要在 Telegram 群組內直接記分、看發球者,並把戰績寫進 web 版共用的 DB。 影響: 同一個 bot(@JianMiauBadmintonBot)改由本專案服務:報名指令、按鈕、暱稱、 週一 10:00 排程、白名單行為與訊息格式皆與 Node-RED 版相同,舊訊息按鈕仍可用; 另新增 /記分(/score、/new)記分板,並在結束/再一局時寫入共用的 history 表。 修法: - signup.js:逐句移植 Node-RED bd_sm 狀態機(attendance / tg_poll / tg_members) - scoreboard.js + match.js + render.js:羽球規則(發球區、換位、Deuce、30 分封頂)、 上排 1 2/下排 4 3 版面、從今日出席選人開局、⏱ 結算、兩段式結束確認、 開局者/管理員權限、scoreList(web 版格式) - history.js:寫入 history 表,欄位與 web 版 ensureHistoryTable 相同 - scripts/nodered-switch.mjs:透過 admin API 暫停/恢復 Node-RED 舊流程 - 單元測試 23 項(規則、報名、選人、結算、權限、歷史寫入),Dockerfile / compose Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
53 lines
2.0 KiB
JavaScript
53 lines
2.0 KiB
JavaScript
// 切換 Node-RED 舊版「羽球報名」流程的開關(同一個 bot 只能有一個 polling 端)
|
||
//
|
||
// node scripts/nodered-switch.mjs pause # 停用 bd_tab、bot 設定改 send-only → 讓本專案接手
|
||
// node scripts/nodered-switch.mjs resume # 還原 Node-RED polling(本專案要先停)
|
||
// node scripts/nodered-switch.mjs status
|
||
//
|
||
// 用 Node-RED admin API(http://<host>:1880/flows),deploy type = nodes(只重啟有變動的節點)。
|
||
import 'dotenv/config'
|
||
|
||
const BASE = (process.env.NODERED_URL || 'http://192.168.5.45:1880').replace(/\/$/, '')
|
||
const TAB_ID = process.env.NODERED_BD_TAB || 'bd_tab'
|
||
const BOT_CONFIG_ID = process.env.NODERED_BD_BOT || 'tgbot_bd_config'
|
||
const mode = process.argv[2]
|
||
|
||
if (!['pause', 'resume', 'status'].includes(mode)) {
|
||
console.error('用法:node scripts/nodered-switch.mjs pause|resume|status')
|
||
process.exit(1)
|
||
}
|
||
|
||
const headers = { 'Node-RED-API-Version': 'v2', 'Content-Type': 'application/json' }
|
||
const res = await fetch(`${BASE}/flows`, { headers })
|
||
if (!res.ok) throw new Error(`GET /flows 失敗:${res.status}`)
|
||
const { rev, flows } = await res.json()
|
||
|
||
const tab = flows.find((n) => n.id === TAB_ID)
|
||
const bot = flows.find((n) => n.id === BOT_CONFIG_ID)
|
||
if (!tab || !bot) throw new Error(`找不到 ${TAB_ID} 或 ${BOT_CONFIG_ID}`)
|
||
|
||
const show = () =>
|
||
console.log(`Node-RED ${BASE}\n ${TAB_ID}: ${tab.disabled ? '停用' : '啟用'}\n ${BOT_CONFIG_ID}.updatemode: ${bot.updatemode}`)
|
||
|
||
if (mode === 'status') {
|
||
show()
|
||
process.exit(0)
|
||
}
|
||
|
||
if (mode === 'pause') {
|
||
tab.disabled = true
|
||
bot.updatemode = 'none'
|
||
} else {
|
||
tab.disabled = false
|
||
bot.updatemode = 'polling'
|
||
}
|
||
|
||
const post = await fetch(`${BASE}/flows`, {
|
||
method: 'POST',
|
||
headers: { ...headers, 'Node-RED-Deployment-Type': 'nodes' },
|
||
body: JSON.stringify({ rev, flows }),
|
||
})
|
||
if (!post.ok) throw new Error(`POST /flows 失敗:${post.status} ${await post.text()}`)
|
||
console.log(`✅ 已${mode === 'pause' ? '暫停' : '恢復'} Node-RED 羽球報名流程(rev ${(await post.json()).rev.slice(0, 8)})`)
|
||
show()
|