const { chromium } = require('playwright'); const LOCATIONS = [ { name: 'Atoomweg', city: 'Groningen', indoor: true, courtTypeIds: '13' }, { name: 'Euroborg', city: 'Groningen', indoor: true, courtTypeIds: '5' }, { name: 'Suikerterrein', city: 'Groningen', indoor: false, courtTypeIds: '' }, ]; const PLAYING_TIME = '90'; function findLocation(nameOrAlias) { const lower = nameOrAlias.toLowerCase(); for (const loc of LOCATIONS) { if (loc.name.toLowerCase() === lower || loc.city.toLowerCase() === lower) return loc; } if (lower === 'ataomweg' || lower === 'atoom') return LOCATIONS[0]; if (lower === 'euro') return LOCATIONS[1]; if (lower === 'suiker') return LOCATIONS[2]; return null; } async function getAvailability(dateStr, locationName) { const loc = findLocation(locationName) || LOCATIONS[0]; const browser = await chromium.launch({ headless: true, args: ['--no-sandbox', '--disable-gpu'], executablePath: '/usr/bin/chromium-browser' }); const context = await browser.newContext({ userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36' }); const page = await context.newPage(); try { // Bouw de URL met de juiste parameters per locatie let url = `https://www.peakzpadel.nl/reserveren/court-booking/reservation?daypart=---&date=${encodeURIComponent(dateStr)}&location=${encodeURIComponent(loc.name)}&playingTimes=${PLAYING_TIME}`; if (loc.courtTypeIds) { url += '&courtTypeIds=' + loc.courtTypeIds; } await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 }); await page.waitForTimeout(3000); const slots = await page.evaluate(() => { const buttons = document.querySelectorAll('button'); const results = []; for (const btn of buttons) { const text = btn.textContent.trim(); if (text.match(/^\d{2}:\d{2}/)) { const price = text.match(/€\s*([\d,]+)/); results.push({ time: text.split(' ')[0], price: price ? price[1] : null, available: !btn.disabled }); } } return results; }); return { date: dateStr, location: loc.name, indoor: loc.indoor, slots, available: slots.filter(s => s.available), unavailable: slots.filter(s => !s.available) }; } catch (err) { return { error: err.message }; } finally { await browser.close(); } } const dateArg = process.argv[2] || new Date().toISOString().split('T')[0]; const locationArg = process.argv[3] || 'Atoomweg'; getAvailability(dateArg, locationArg).then(result => { if (result.error) { console.error('Fout:', result.error); process.exit(1); } if (process.argv.includes('--json')) { console.log(JSON.stringify(result)); process.exit(0); } const dayNames = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za']; const d = new Date(result.date); const dayLabel = dayNames[d.getDay()] + ' ' + d.getDate() + '/' + (d.getMonth()+1); const typeIcon = result.indoor ? '🏢' : '🌳'; const typeLabel = result.indoor ? 'Indoor' : 'Buiten'; console.log(`\n${typeIcon} Peakz Padel — ${result.location} (${typeLabel})`); console.log(`📅 ${dayLabel}`); console.log(`🟢 ${result.available.length} banen vrij`); console.log(`🔴 ${result.unavailable.length} banen bezet`); console.log(''); if (result.available.length > 0) { console.log('Vrije tijden:'); for (const s of result.available.slice(0, 15)) { console.log(` 🟢 ${s.time}${s.price ? ' — €' + s.price : ''}`); } if (result.available.length > 15) console.log(` ... en ${result.available.length - 15} meer`); } console.log(''); process.exit(0); });