Vote flow: ranked/friendly na confirmatie, majority decides

This commit is contained in:
Nova Coder
2026-05-25 10:47:11 +00:00
parent 22e77bcc80
commit bc9ef8b3dc
2 changed files with 317 additions and 1 deletions
+159 -1
View File
@@ -1503,7 +1503,165 @@ bot.on('callback_query', async (query) => {
logError('match response error:', e.message); logError('match response error:', e.message);
bot.answerCallbackQuery(query.id, { text: 'Fout' }).catch(() => {}); 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]; const matchId = data.split('_')[2];
try { try {
const teams = await api('/matches/' + matchId + '/teams'); const teams = await api('/matches/' + matchId + '/teams');
+158
View File
@@ -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);
}