Files
padel-api/padel-peakz.js
T
root da64562eb4 Initial commit: Padel Planner API
- Express API server met SQLite
- Database migratie (migrate.cjs)
- REST endpoints voor players, matches, scores
- CHANGELOG.md en .gitignore
2026-05-24 20:25:54 +00:00

98 lines
3.0 KiB
JavaScript

#!/usr/bin/env node
// Peakz Padel beschikbaarheid scraper
// Aanroep: node padel-peakz.mjs [datum] [locatie]
// Voorbeeld: node padel-peakz.mjs 2026-05-24 Atoomweg
const { chromium } = require('playwright');
const LOCATIONS = {
'Atoomweg': '6637dd8e-cd4b-4fee-af49-196f6828b7dc',
'Groningen': '6637dd8e-cd4b-4fee-af49-196f6828b7dc',
};
const RESERVATION_TYPE = '6';
const PLAYING_TIME = '90';
async function getAvailability(dateStr, locationName) {
const locationId = LOCATIONS[locationName] || LOCATIONS['Atoomweg'];
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
});
const page = await context.newPage();
try {
const url = `https://www.peakzpadel.nl/reserveren/court-booking/reservation?daypart=---&date=${dateStr}&location=${encodeURIComponent(locationName)}&playingTimes=${PLAYING_TIME}&courtTypeIds=13`;
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: locationName,
slots,
available: slots.filter(s => s.available),
unavailable: slots.filter(s => !s.available)
};
} catch (err) {
return { error: err.message };
} finally {
await browser.close();
}
}
// CLI mode
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);
}
// Output als JSON voor machine-readable
if (process.argv.includes('--json')) {
console.log(JSON.stringify(result));
process.exit(0);
}
// Human readable
const dayNames = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'];
const d = new Date(result.date);
const dayLabel = dayNames[d.getDay()];
const dateLabel = `${dayLabel} ${d.getDate()}/${d.getMonth()+1}`;
console.log(`\n🏓 Peakz Padel — ${result.location}`);
console.log(`📅 ${dateLabel}`);
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) {
const price = s.price ? ` — €${s.price}` : '';
console.log(` 🟢 ${s.time}${price}`);
}
}
console.log('');
process.exit(0);
});