152 lines
5.8 KiB
JavaScript
152 lines
5.8 KiB
JavaScript
// 把比賽狀態轉成 Telegram 訊息文字(HTML)與 inline 鍵盤
|
||||
|
|
import { getServing, getWinner, isDoubles } from './match.js'
|
|||
|
|
import { escapeHtml } from './util.js'
|
|||
|
|
|
|||
|
|
const TEAM_ICON = ['🅰️', '🅱️']
|
|||
|
|
const COURT_LABEL = { right: '右區', left: '左區' }
|
|||
|
|
|
|||
|
|
// 隊員名單,依「畫面位置」排列(同 web 版:上排 1 2、下排 4 3),發球者前面加 🏸
|
|||
|
|
// 上排 🅰️ 為鏡像:畫面左 = 自己的右發球區;下排 🅱️:畫面右 = 自己的右發球區
|
|||
|
|
function renderPlayers(match, teamIndex, serving) {
|
|||
|
|
const team = match.teams[teamIndex]
|
|||
|
|
let ordered = team.players
|
|||
|
|
if (team.players.length === 2) {
|
|||
|
|
const rightPlayer = team.players[team.right]
|
|||
|
|
const leftPlayer = team.players[1 - team.right]
|
|||
|
|
ordered = teamIndex === 0 ? [rightPlayer, leftPlayer] : [leftPlayer, rightPlayer]
|
|||
|
|
}
|
|||
|
|
return ordered
|
|||
|
|
.map((name) => {
|
|||
|
|
const isServer = serving && serving.team === teamIndex && serving.server === name
|
|||
|
|
return `${isServer ? '🏸' : ''}${escapeHtml(name)}`
|
|||
|
|
})
|
|||
|
|
.join(' / ')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function renderText(match, { ended = false } = {}) {
|
|||
|
|
const serving = getServing(match)
|
|||
|
|
const winner = getWinner(match)
|
|||
|
|
const lines = []
|
|||
|
|
|
|||
|
|
lines.push(`🏸 <b>羽球記分板</b> ${isDoubles(match) ? '雙打' : '單打'}・${match.target} 分制`)
|
|||
|
|
lines.push('')
|
|||
|
|
|
|||
|
|
for (const teamIndex of [0, 1]) {
|
|||
|
|
const marker = winner === teamIndex ? ' 🏆' : ''
|
|||
|
|
lines.push(
|
|||
|
|
`${TEAM_ICON[teamIndex]} ${renderPlayers(match, teamIndex, serving)} <b>${match.score[teamIndex]}</b>${marker}`,
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
lines.push('')
|
|||
|
|
|
|||
|
|
const hasPoints = (match.scoreList ?? []).length > 0
|
|||
|
|
if (ended) {
|
|||
|
|
if (match.historyId) lines.push(`⏹ 已結束 📝 已存入歷史戰績 #${match.historyId}`)
|
|||
|
|
else if (hasPoints && !match.finished) lines.push('⏹ 已結束(未結算,未存入戰績)')
|
|||
|
|
else lines.push('⏹ 已結束')
|
|||
|
|
} else if (match.confirmEnd) {
|
|||
|
|
if (match.finished) lines.push('❓ <b>確定要結束嗎?</b>會把這場寫入歷史戰績')
|
|||
|
|
else if (hasPoints) lines.push('❓ <b>確定要結束嗎?</b>比賽未結算,不會寫入戰績(要存請先按 ⏱ 結算)')
|
|||
|
|
else lines.push('❓ <b>確定要結束嗎?</b>')
|
|||
|
|
} else if (match.settled) {
|
|||
|
|
const result = winner !== null ? `${TEAM_ICON[winner]} 獲勝` : '平手'
|
|||
|
|
lines.push(`⏱ 時間到結算 ${match.score[0]} : ${match.score[1]} ${result}`)
|
|||
|
|
} else if (winner !== null) {
|
|||
|
|
lines.push(`🎉 ${TEAM_ICON[winner]} 獲勝! ${match.score[0]} : ${match.score[1]}`)
|
|||
|
|
} else if (!serving) {
|
|||
|
|
lines.push('👉 請先選擇由哪一隊先發球')
|
|||
|
|
} else {
|
|||
|
|
lines.push(
|
|||
|
|
`發球:${TEAM_ICON[serving.team]} <b>${escapeHtml(serving.server)}</b>(${COURT_LABEL[serving.court]})` +
|
|||
|
|
` 接發:${escapeHtml(serving.receiver)}`,
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return lines.join('\n')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// callback_data 格式:sc:<action>
|
|||
|
|
const btn = (text, action) => ({ text, callback_data: `sc:${action}` })
|
|||
|
|
|
|||
|
|
export function renderKeyboard(match, { ended = false } = {}) {
|
|||
|
|
if (ended) return { inline_keyboard: [] }
|
|||
|
|
const rows = buildKeyboardRows(match)
|
|||
|
|
if (match.confirmEnd) {
|
|||
|
|
// 兩段式確認:把「結束」換成「確定結束 / 取消」
|
|||
|
|
return {
|
|||
|
|
inline_keyboard: rows.map((row) =>
|
|||
|
|
row.some((b) => b.callback_data === 'sc:end')
|
|||
|
|
? [btn('✅ 確定結束', 'end'), btn('↩️ 取消', 'endcancel')]
|
|||
|
|
: row,
|
|||
|
|
),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return { inline_keyboard: rows }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function buildKeyboardRows(match) {
|
|||
|
|
|
|||
|
|
if (match.finished) {
|
|||
|
|
return [
|
|||
|
|
[btn('↩️ 上一步', 'undo'), btn('🔄 再一局', 'reset')],
|
|||
|
|
[btn('🏁 結束', 'end')],
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (match.server === null) {
|
|||
|
|
const rows = [[btn('🅰️ 先發球', 'srv0'), btn('🅱️ 先發球', 'srv1')]]
|
|||
|
|
if (isDoubles(match)) {
|
|||
|
|
rows.push([btn('🔀 🅰️ 換位', 'swap0'), btn('🔀 🅱️ 換位', 'swap1')])
|
|||
|
|
}
|
|||
|
|
rows.push([btn('🏁 結束', 'end')])
|
|||
|
|
return rows
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return [
|
|||
|
|
[btn('🅰️ +1', 'pt0'), btn('🅱️ +1', 'pt1')],
|
|||
|
|
[btn('↩️ 上一步', 'undo'), btn('🔄 重新開始', 'reset')],
|
|||
|
|
[btn('⏱ 結算', 'settle'), btn('🏁 結束', 'end')],
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ---------- 選人(/記分 無參數:從今日出席名單點選 1→4) ----------
|
|||
|
|
const PICK_NUM = ['1️⃣', '2️⃣', '3️⃣', '4️⃣']
|
|||
|
|
|
|||
|
|
export function renderPickerText(picker) {
|
|||
|
|
const slot = (i) => (picker.picked[i] != null ? escapeHtml(picker.names[picker.picked[i]]) : '—')
|
|||
|
|
const lines = [
|
|||
|
|
`🏸 <b>選擇上場人員</b> 依 1 → 4 順序點選(今日出席 ${picker.names.length} 人)`,
|
|||
|
|
'',
|
|||
|
|
`🅰️ 1️⃣ ${slot(0)} 2️⃣ ${slot(1)}`,
|
|||
|
|
`🅱️ 4️⃣ ${slot(3)} 3️⃣ ${slot(2)}`,
|
|||
|
|
'',
|
|||
|
|
]
|
|||
|
|
if (picker.picked.length === 0) lines.push('👉 點名字加入;1、2 一隊,3、4 一隊(位置同 web 版:上 1 2/下 4 3);點滿 4 人自動開局')
|
|||
|
|
else if (picker.picked.length === 2) lines.push('👉 繼續點第 3、4 位,或按「單打開始」')
|
|||
|
|
else if (picker.picked.length < 4) lines.push(`👉 繼續點第 ${picker.picked.length + 1} 位`)
|
|||
|
|
return lines.join('\n')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function renderPickerKeyboard(picker) {
|
|||
|
|
const rows = []
|
|||
|
|
const perRow = picker.names.length > 8 ? 3 : 2
|
|||
|
|
let row = []
|
|||
|
|
picker.names.forEach((name, i) => {
|
|||
|
|
const order = picker.picked.indexOf(i)
|
|||
|
|
const label = order >= 0 ? `${PICK_NUM[order]} ${name}` : name
|
|||
|
|
row.push({ text: label, callback_data: `sc:pick${i}` })
|
|||
|
|
if (row.length === perRow) {
|
|||
|
|
rows.push(row)
|
|||
|
|
row = []
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
if (row.length) rows.push(row)
|
|||
|
|
|
|||
|
|
const controls = []
|
|||
|
|
if (picker.picked.length > 0) controls.push({ text: '↩️ 取消上一位', callback_data: 'sc:unpick' })
|
|||
|
|
if (picker.picked.length === 2) controls.push({ text: '▶️ 單打開始', callback_data: 'sc:pickgo' })
|
|||
|
|
controls.push({ text: '✖️ 取消', callback_data: 'sc:end' })
|
|||
|
|
rows.push(controls)
|
|||
|
|
return { inline_keyboard: rows }
|
|||
|
|
}
|