Fix: voting flow bugs na code review
- acceptingMatch/finalizingMatches als const gedeclareerd (ipv implicit global) - 30-min voting timeout met cleanup interval - _created timestamp bij acceptingMatch init - finalizingMatches semaphore tegen race conditions - try/catch + logError in finalizeMatch() - Speler check in vote callbacks (m.players.includes) - finalizingMatches.delete bij cleanup na finalize
This commit is contained in:
@@ -155,6 +155,21 @@ function setUserState(chatId, data) {
|
||||
userState.set(chatId, data);
|
||||
}
|
||||
|
||||
// ─── Match voting state (per match pending ranked/friendly vote) ───
|
||||
const acceptingMatch = {};
|
||||
const finalizingMatches = new Set(); // semaphore tegen race condition
|
||||
|
||||
// Voting timeout: clean up matches older than 30 minutes
|
||||
setInterval(() => {
|
||||
const now = Date.now();
|
||||
for (const [matchId, m] of Object.entries(acceptingMatch)) {
|
||||
if (m._created && now - m._created > 30 * 60 * 1000) {
|
||||
delete acceptingMatch[matchId];
|
||||
log('Voting timeout: match ' + matchId + ' cleaned up');
|
||||
}
|
||||
}
|
||||
}, 60 * 1000); // check elke minuut
|
||||
|
||||
// ─── Safe sendMessage wrapper (met rate limiting) ───
|
||||
const sendMsg = throttledSend;
|
||||
|
||||
@@ -1549,7 +1564,8 @@ bot.on('callback_query', async (query) => {
|
||||
location: matchInfo.location,
|
||||
proposed_teams: matchInfo.proposed_teams,
|
||||
ranked: new Set(),
|
||||
friendly: new Set()
|
||||
friendly: new Set(),
|
||||
_created: Date.now()
|
||||
};
|
||||
|
||||
bot.answerCallbackQuery(query.id, { text: 'Aanwezig!' }).catch(() => {});
|
||||
@@ -1593,6 +1609,12 @@ bot.on('callback_query', async (query) => {
|
||||
const player = players.find(p => p.telegram_id == chatId);
|
||||
if (!player) return;
|
||||
|
||||
// Security: only match players can vote
|
||||
if (!m.players.includes(player.id)) {
|
||||
bot.answerCallbackQuery(query.id, { text: 'Je zit niet in deze match' }).catch(() => {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.ranked.has(player.id) || m.friendly.has(player.id)) {
|
||||
bot.answerCallbackQuery(query.id, { text: 'Je hebt al gestemd!' }).catch(() => {});
|
||||
return;
|
||||
@@ -1637,11 +1659,20 @@ bot.on('callback_query', async (query) => {
|
||||
|
||||
// ─── Finaliseer match ───
|
||||
async function finalizeMatch(matchId, finalType) {
|
||||
if (finalizingMatches.has(matchId)) return; // prevent race
|
||||
finalizingMatches.add(matchId);
|
||||
|
||||
const players = await api('/players');
|
||||
const m = acceptingMatch[matchId];
|
||||
if (!m) return;
|
||||
if (!m) { finalizingMatches.delete(matchId); return; }
|
||||
|
||||
try { await api('/matches/' + matchId + '/type', 'POST', { match_type: finalType }); } catch(e) {}
|
||||
try {
|
||||
await api('/matches/' + matchId + '/type', 'POST', { match_type: finalType });
|
||||
} catch(e) {
|
||||
logError('finalizeMatch: failed to set match type:', e.message);
|
||||
finalizingMatches.delete(matchId);
|
||||
return;
|
||||
}
|
||||
|
||||
const getName = id => { const p = players.find(x => x.id == id); return p ? p.name : '?'; };
|
||||
const t1names = (m.proposed_teams?.team1 || []).map(getName).join(' + ');
|
||||
@@ -1659,6 +1690,7 @@ bot.on('callback_query', async (query) => {
|
||||
}
|
||||
|
||||
delete acceptingMatch[matchId];
|
||||
finalizingMatches.delete(matchId);
|
||||
log('Match ' + matchId + ' finalized as ' + finalType);
|
||||
}
|
||||
} else if (data.startsWith('team_swap_')) {
|
||||
|
||||
Reference in New Issue
Block a user