From bc9ef8b3dc0c129fb4c95cd3e03c3d73f838f9d4 Mon Sep 17 00:00:00 2001 From: Nova Coder Date: Mon, 25 May 2026 10:47:11 +0000 Subject: [PATCH] Vote flow: ranked/friendly na confirmatie, majority decides --- bot.js | 160 +++++++++++++++++++++++++++++++++++++++++++++++- new_section.txt | 158 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 new_section.txt diff --git a/bot.js b/bot.js index 1bba62d..22cdabf 100644 --- a/bot.js +++ b/bot.js @@ -1503,7 +1503,165 @@ bot.on('callback_query', async (query) => { logError('match response error:', e.message); bot.answerCallbackQuery(query.id, { text: 'Fout' }).catch(() => {}); } - } else if (data.startsWith('team_swap_')) { + // ─── Match accept: ranked/friendly stem ─── + if (data.startsWith('match_accept_') || data.startsWith('match_reject_')) { + const parts = data.split('_'); + const action = parts[1]; + const matchId = parts[2]; + + try { + const players = await api('/players'); + if (!Array.isArray(players)) throw new Error('Invalid players response'); + const player = players.find(p => p.telegram_id == chatId); + if (!player) return; + + if (action === 'reject') { + const updated = await api('/matches/' + matchId + '/respond', 'POST', { + player_id: player.id, + response: 'rejected' + }); + bot.answerCallbackQuery(query.id, { text: 'Afgewezen' }).catch(() => {}); + + if (updated && updated.status === 'cancelled') { + for (const pid of (updated.players || [])) { + if (pid === player.id) continue; + const p = players.find(pl => pl.id === pid); + if (p && p.telegram_id) { + sendMsg(p.telegram_id, player.name + ' heeft afgezegd voor ' + updated.date + ' ' + updated.start + '.\\n\\nNiet genoeg spelers meer. Stuur /match voor een nieuwe poging.'); + } + } + } + bot.editMessageText('Je hebt afgezegd.', { chat_id: chatId, message_id: query.message.message_id }).catch(() => {}); + return; + } + + const matchInfo = await api('/matches/' + matchId); + if (!matchInfo) throw new Error('Match not found'); + + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (matchInfo.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (matchInfo.proposed_teams?.team2 || []).map(getName).join(' + '); + + acceptingMatch[matchId] = { + players: matchInfo.players || [], + date: matchInfo.date, + start: matchInfo.start, + location: matchInfo.location, + proposed_teams: matchInfo.proposed_teams, + ranked: new Set(), + friendly: new Set() + }; + + bot.answerCallbackQuery(query.id, { text: 'Aanwezig!' }).catch(() => {}); + + sendMsg(chatId, + '🎾 **Je bevestigt deelname!**\\n\\n' + matchInfo.date + ' ' + matchInfo.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n**Is dit een ranked of friendly wedstrijd?**', + { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: [ + [{ text: '🏆 Ranked', callback_data: 'vote_ranked_' + matchId }, + { text: '🟢 Friendly', callback_data: 'vote_friendly_' + matchId }] + ] + } + } + ); + + bot.editMessageText('Je hebt bevestigd — kies ranked of friendly in DM.', { + chat_id: chatId, + message_id: query.message.message_id + }).catch(() => {}); + + } catch (e) { + logError('match response error:', e.message); + bot.answerCallbackQuery(query.id, { text: 'Fout' }).catch(() => {}); + } + } + + // ─── Vote: ranked/friendly keuze ─── + if (data.startsWith('vote_ranked_') || data.startsWith('vote_friendly_')) { + const matchId = data.split('_')[2]; + const vote = data.startsWith('vote_ranked_') ? 'ranked' : 'friendly'; + + const m = acceptingMatch[matchId]; + if (!m) { + bot.answerCallbackQuery(query.id, { text: 'Match niet gevonden of verlopen' }).catch(() => {}); + return; + } + + const players = await api('/players'); + const player = players.find(p => p.telegram_id == chatId); + if (!player) return; + + if (m.ranked.has(player.id) || m.friendly.has(player.id)) { + bot.answerCallbackQuery(query.id, { text: 'Je hebt al gestemd!' }).catch(() => {}); + return; + } + + if (vote === 'ranked') m.ranked.add(player.id); + else m.friendly.add(player.id); + + const rankedCount = m.ranked.size; + const friendlyCount = m.friendly.size; + const totalResponses = rankedCount + friendlyCount; + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (m.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (m.proposed_teams?.team2 || []).map(getName).join(' + '); + + bot.answerCallbackQuery(query.id, { text: 'Je stem is geregistraerd!' }).catch(() => {}); + + if (rankedCount >= 3) { + await finalizeMatch(matchId, 'ranked'); + } else if (friendlyCount >= 3) { + await finalizeMatch(matchId, 'friendly'); + } else if (totalResponses === 4) { + await finalizeMatch(matchId, 'friendly'); + } else { + const stemmen = 'Stemmen: ' + rankedCount + ' ranked, ' + friendlyCount + ' friendly'; + bot.editMessageText( + '🎾 **Je bevestigt deelname!**\\n\\n' + m.date + ' ' + m.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n' + stemmen + '\\n\\n**Is dit een ranked of friendly wedstrijd?**', + { + chat_id: chatId, + message_id: query.message.message_id, + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: [ + [{ text: '🏆 Ranked', callback_data: 'vote_ranked_' + matchId }, + { text: '🟢 Friendly', callback_data: 'vote_friendly_' + matchId }] + ] + } + } + ).catch(() => {}); + } + } + + // ─── Finaliseer match ─── + async function finalizeMatch(matchId, finalType) { + const players = await api('/players'); + const m = acceptingMatch[matchId]; + if (!m) return; + + try { await api('/matches/' + matchId + '/type', 'POST', { match_type: finalType }); } catch(e) {} + + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (m.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (m.proposed_teams?.team2 || []).map(getName).join(' + '); + const typeLabel = finalType === 'ranked' ? '🏆 **Ranked** — score telt mee!' : '🟢 **Friendly**'; + + for (const pid of m.players) { + const p = players.find(x => x.id == pid); + if (p && p.telegram_id) { + sendMsg(p.telegram_id, + '🎾 **Match bevestigd!**\\n\\n' + m.date + ' ' + m.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n' + typeLabel + '\\n\\nBoek de baan en vergeet niet /score in te vullen na de wedstrijd!', + { parse_mode: 'Markdown' } + ); + } + } + + delete acceptingMatch[matchId]; + log('Match ' + matchId + ' finalized as ' + finalType); + } +} else if (data.startsWith('team_swap_')) { const matchId = data.split('_')[2]; try { const teams = await api('/matches/' + matchId + '/teams'); diff --git a/new_section.txt b/new_section.txt new file mode 100644 index 0000000..ca944e7 --- /dev/null +++ b/new_section.txt @@ -0,0 +1,158 @@ + // ─── Match accept: ranked/friendly stem ─── + if (data.startsWith('match_accept_') || data.startsWith('match_reject_')) { + const parts = data.split('_'); + const action = parts[1]; + const matchId = parts[2]; + + try { + const players = await api('/players'); + if (!Array.isArray(players)) throw new Error('Invalid players response'); + const player = players.find(p => p.telegram_id == chatId); + if (!player) return; + + if (action === 'reject') { + const updated = await api('/matches/' + matchId + '/respond', 'POST', { + player_id: player.id, + response: 'rejected' + }); + bot.answerCallbackQuery(query.id, { text: 'Afgewezen' }).catch(() => {}); + + if (updated && updated.status === 'cancelled') { + for (const pid of (updated.players || [])) { + if (pid === player.id) continue; + const p = players.find(pl => pl.id === pid); + if (p && p.telegram_id) { + sendMsg(p.telegram_id, player.name + ' heeft afgezegd voor ' + updated.date + ' ' + updated.start + '.\\n\\nNiet genoeg spelers meer. Stuur /match voor een nieuwe poging.'); + } + } + } + bot.editMessageText('Je hebt afgezegd.', { chat_id: chatId, message_id: query.message.message_id }).catch(() => {}); + return; + } + + const matchInfo = await api('/matches/' + matchId); + if (!matchInfo) throw new Error('Match not found'); + + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (matchInfo.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (matchInfo.proposed_teams?.team2 || []).map(getName).join(' + '); + + acceptingMatch[matchId] = { + players: matchInfo.players || [], + date: matchInfo.date, + start: matchInfo.start, + location: matchInfo.location, + proposed_teams: matchInfo.proposed_teams, + ranked: new Set(), + friendly: new Set() + }; + + bot.answerCallbackQuery(query.id, { text: 'Aanwezig!' }).catch(() => {}); + + sendMsg(chatId, + '🎾 **Je bevestigt deelname!**\\n\\n' + matchInfo.date + ' ' + matchInfo.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n**Is dit een ranked of friendly wedstrijd?**', + { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: [ + [{ text: '🏆 Ranked', callback_data: 'vote_ranked_' + matchId }, + { text: '🟢 Friendly', callback_data: 'vote_friendly_' + matchId }] + ] + } + } + ); + + bot.editMessageText('Je hebt bevestigd — kies ranked of friendly in DM.', { + chat_id: chatId, + message_id: query.message.message_id + }).catch(() => {}); + + } catch (e) { + logError('match response error:', e.message); + bot.answerCallbackQuery(query.id, { text: 'Fout' }).catch(() => {}); + } + } + + // ─── Vote: ranked/friendly keuze ─── + if (data.startsWith('vote_ranked_') || data.startsWith('vote_friendly_')) { + const matchId = data.split('_')[2]; + const vote = data.startsWith('vote_ranked_') ? 'ranked' : 'friendly'; + + const m = acceptingMatch[matchId]; + if (!m) { + bot.answerCallbackQuery(query.id, { text: 'Match niet gevonden of verlopen' }).catch(() => {}); + return; + } + + const players = await api('/players'); + const player = players.find(p => p.telegram_id == chatId); + if (!player) return; + + if (m.ranked.has(player.id) || m.friendly.has(player.id)) { + bot.answerCallbackQuery(query.id, { text: 'Je hebt al gestemd!' }).catch(() => {}); + return; + } + + if (vote === 'ranked') m.ranked.add(player.id); + else m.friendly.add(player.id); + + const rankedCount = m.ranked.size; + const friendlyCount = m.friendly.size; + const totalResponses = rankedCount + friendlyCount; + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (m.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (m.proposed_teams?.team2 || []).map(getName).join(' + '); + + bot.answerCallbackQuery(query.id, { text: 'Je stem is geregistraerd!' }).catch(() => {}); + + if (rankedCount >= 3) { + await finalizeMatch(matchId, 'ranked'); + } else if (friendlyCount >= 3) { + await finalizeMatch(matchId, 'friendly'); + } else if (totalResponses === 4) { + await finalizeMatch(matchId, 'friendly'); + } else { + const stemmen = 'Stemmen: ' + rankedCount + ' ranked, ' + friendlyCount + ' friendly'; + bot.editMessageText( + '🎾 **Je bevestigt deelname!**\\n\\n' + m.date + ' ' + m.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n' + stemmen + '\\n\\n**Is dit een ranked of friendly wedstrijd?**', + { + chat_id: chatId, + message_id: query.message.message_id, + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: [ + [{ text: '🏆 Ranked', callback_data: 'vote_ranked_' + matchId }, + { text: '🟢 Friendly', callback_data: 'vote_friendly_' + matchId }] + ] + } + } + ).catch(() => {}); + } + } + + // ─── Finaliseer match ─── + async function finalizeMatch(matchId, finalType) { + const players = await api('/players'); + const m = acceptingMatch[matchId]; + if (!m) return; + + try { await api('/matches/' + matchId + '/type', 'POST', { match_type: finalType }); } catch(e) {} + + const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; }; + const t1names = (m.proposed_teams?.team1 || []).map(getName).join(' + '); + const t2names = (m.proposed_teams?.team2 || []).map(getName).join(' + '); + const typeLabel = finalType === 'ranked' ? '🏆 **Ranked** — score telt mee!' : '🟢 **Friendly**'; + + for (const pid of m.players) { + const p = players.find(x => x.id == pid); + if (p && p.telegram_id) { + sendMsg(p.telegram_id, + '🎾 **Match bevestigd!**\\n\\n' + m.date + ' ' + m.start + '\\n🟡 ' + t1names + '\\n🔵 ' + t2names + '\\n\\n' + typeLabel + '\\n\\nBoek de baan en vergeet niet /score in te vullen na de wedstrijd!', + { parse_mode: 'Markdown' } + ); + } + } + + delete acceptingMatch[matchId]; + log('Match ' + matchId + ' finalized as ' + finalType); + }