// BEGIN:VCALENDAR // VERSION:2.0 // PRODID:-//fabrice404//olympics-calendar//archery/AUS//EN // X-WR-CALNAME:šŸ‡¦šŸ‡ŗ Australia Archery | Paris 2024 // NAME:šŸ‡¦šŸ‡ŗ Australia Archery | Paris 2024 // BEGIN:VEVENT // UID:20240725T073000Z-archery-WOMENS-INDIVIDUAL-RANKING-ROUND // DTSTAMP:20240725T073000Z // DTSTART:20240725T073000Z // DTEND:20240725T103000Z // DESCRIPTION:Archery - Women's Individual Ranking Round\nšŸ‡ØšŸ‡³ AN // Qixuan\nšŸ‡²šŸ‡½ Alejandra VALENCIA\nšŸ‡²šŸ‡© Alexandra MIRCA\nšŸ‡µšŸ‡· Alondra // RIVERA\nšŸ‡«šŸ‡· Amelie CORDEAU\nšŸ‡§šŸ‡· Ana Luiza SLIACHTICAS CAETANO\nšŸ‡ØšŸ‡“ Ana // RENDON MARTINEZ\nšŸ‡²šŸ‡½ Ana VAZQUEZ\nšŸ‡²šŸ‡½ Angela RUIZ\nšŸ‡®šŸ‡³ Ankita // BHAKAT\nšŸ‡²šŸ‡¾ Ariana Nur Dania MOHAMAD ZAIRI\nšŸ‡®šŸ‡³ Bhajan KAUR\nšŸ‡¬šŸ‡§ // Bryony PITMAN\nšŸ‡¹šŸ‡¼ CHIU Yi-Ching\nšŸ‡«šŸ‡· Caroline LOPEZ\nšŸ‡ŗšŸ‡ø Casey // KAUFHOLD\nšŸ‡ŗšŸ‡ø Catalina GNORIEGA\nšŸ‡©šŸ‡Ŗ Charline SCHWARZ\nšŸ‡®šŸ‡¹ Chiara // REBAGLIATI\nšŸ‡®šŸ‡³ Deepika KUMARI\nšŸ‡øšŸ‡° Denisa BARANKOVA\nšŸ‡®šŸ‡© Diananda // CHOIRUNISA\nšŸ‡ŖšŸ‡ø Elia CANALES\nšŸ‡¹šŸ‡· Elif Berra GOKKIR\nšŸ‡¦šŸ‡¹ Elisabeth // STRAKA\nšŸ‡¬šŸ‡³ Fatoumata SYLLA\nšŸ‡³šŸ‡± Gaby SCHLOESSER\nšŸ‡øšŸ‡² Giorgia // CESARINI\nšŸ‡°šŸ‡· JEON Hunyoung\nšŸ‡ŖšŸ‡¬ Jana ALI\nšŸ‡ŗšŸ‡ø Jennifer MUCINO\nšŸ‡©šŸ‡Ŗ // Katharina BAUER\nšŸ‡©šŸ‡° Kirstine DANSTRUP ANDERSEN\nšŸ‡¹šŸ‡¼ LEI // Chien-Ying\nšŸ‡ØšŸ‡³ LI Jiaman\nšŸ‡¹šŸ‡¼ LI Tsai-Chi\nšŸ‡°šŸ‡· LIM Sihyeon\nšŸ‡¦šŸ‡ŗ // Laura PAEGLIS\nšŸ‡³šŸ‡± Laura van der WINKEL\nšŸ‡«šŸ‡· Lisa BARBELIN\nšŸ‡·šŸ‡“ // Madalina AMAISTROAIE\nšŸ‡ØšŸ‡æ Marie HORACKOVA\nšŸ‡¬šŸ‡§ Megs HAVERS\nšŸ‡©šŸ‡Ŗ // Michelle KROPPEN\nšŸ‡®šŸ‡± Mikaella MOSHE\nšŸ‡®šŸ‡· Mobina FALLAH\nšŸ‡°šŸ‡· NAM // Suhyeon\nšŸ‡ÆšŸ‡µ NODA Satsuki\nšŸ‡²šŸ‡¾ Nurul Azreena MOHAMAD FAZIL\nšŸ‡¬šŸ‡§ Penny // HEALEY\nšŸ‡³šŸ‡± Quinty ROEFFEN\nšŸ‡ŖšŸ‡Ŗ Reena PARNAT\nšŸ‡®šŸ‡© Rezza OCTAVIA\nšŸ‡¹šŸ‡³ // Rihab ELWALID\nšŸ‡²šŸ‡¾ Syaqiera MASHAYIKH\nšŸ‡®šŸ‡© Syifa Nurafifah KAMAL\nšŸ‡»šŸ‡³ // Thi Anh Nguyet DO\nšŸ‡ŗšŸ‡¦ Veronika MARCHENKO\nšŸ‡ØšŸ‡¦ Virginie CHENIER\nšŸ‡µšŸ‡± // Wioleta MYSZOR\nšŸ‡ØšŸ‡³ YANG Xiaolei\nšŸ‡¦šŸ‡æ Yaylagul RAMAZANOVA\nšŸ‡øšŸ‡® Zana // PINTARIC\nšŸ‡ŗšŸ‡æ Ziyodakhon ABDUSATTOROVA // SUMMARY:šŸ¹ Women's Individual Ranking Round // LOCATION:Invalides // END:VEVENT import { mkdirSync, writeFileSync } from "fs"; import { Calendar } from "./types"; import { getFlag } from "./nocs"; // BEGIN:VCALENDAR // VERSION:2.0 // PRODID:-//fabrice404//olympics-calendar//3x3-basketball/AUS//EN // X-WR-CALNAME:šŸ‡¦šŸ‡ŗ Australia 3x3 Basketball | Paris 2024 // NAME:šŸ‡¦šŸ‡ŗ Australia 3x3 Basketball | Paris 2024 // BEGIN:VEVENT // UID:20240730T160000Z-3x3-basketball-WOMENS-POOL-ROUND-AUS-CAN // DTSTAMP:20240730T160000Z // DTSTART:20240730T160000Z // DTEND:20240730T162500Z // DESCRIPTION:3x3 Basketball - Women's Pool Round // SUMMARY:šŸ€ AUS šŸ‡¦šŸ‡ŗ - šŸ‡ØšŸ‡¦ CAN // LOCATION:La Concorde 1 // END:VEVENT // END:VCALENDAR export const generateICSFiles = (calendar: Calendar): void => { generateICSFile(calendar, null, null); calendar.sports.forEach((sport) => { generateICSFile(calendar, sport.key, null); calendar.nocs.forEach((noc) => { generateICSFile(calendar, sport.key, noc.key); }); }); calendar.nocs.forEach((noc) => { generateICSFile(calendar, null, noc.key); }); }; export const generateICSFile = (calendar: Calendar, sportKey: string | null, nocKey: string | null): void => { calendar.languages.forEach((lang) => { const pathSportKey = sportKey ? sportKey : "all-sports"; const pathNocKey = nocKey ? nocKey : "calendar" const filepath = `../ui/public/data/${lang.code.toLowerCase()}/${pathSportKey.toLowerCase()}/${pathNocKey.toLowerCase()}.ics`; mkdirSync(filepath.split('/').slice(0, -1).join('/'), { recursive: true }); const titleComponents = []; if (nocKey) { titleComponents.push(`${calendar.nocs.find(n => n.key === nocKey)!.name[lang.code]}`); } if (sportKey) { titleComponents.push(calendar.sports.find(s => s.key === sportKey)!.name[lang.code]); } titleComponents.push("Milano Cortina 2026"); const title = titleComponents.join(' - '); const lines = []; lines.push("BEGIN:VCALENDAR"); lines.push("VERSION:2.0"); lines.push(`PRODID:-//fabrice404//olympics-calendar//${lang.code}/${pathSportKey}/${pathNocKey}`); lines.push(`X-WR-CALNAME:${title}`); lines.push(`NAME:${title}`); calendar.events .filter((event) => { if (sportKey && event.sport !== sportKey) return false; if (nocKey) { if (event.match) { const team1Key = event.match.team1.key; const team2Key = event.match.team2.key; if (team1Key !== nocKey && team2Key !== nocKey) { return false; } } else { return false; } } return true; }) .forEach((event) => { lines.push("BEGIN:VEVENT"); lines.push(`UID:${event.key.replace(/--/g, '-')}`); lines.push(`DTSTAMP:${event.start.replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z')}`); lines.push(`DTSTART:${event.start.replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z')}`); lines.push(`DTEND:${event.end.replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z')}`); lines.push(`LOCATION:${event.location[lang.code] || ''}`); const sport = calendar.sports.find(s => s.key === event.sport)!; lines.push(`DESCRIPTION:${sport.name[lang.code]} - ${event.name[lang.code] || ''}`); let summary = `SUMMARY:${event.name[lang.code] || ''}` if (event.match) { const team1Name = event.match.team1.name[lang.code] || event.match.team1.key; const team1Flag = getFlag(event.match.team1.key); const team2Name = event.match.team2.name[lang.code] || event.match.team2.key; const team2Flag = getFlag(event.match.team2.key); if (team1Name && team2Name) { lines.push(`SUMMARY:${team1Flag} ${team1Name} - ${team2Name} ${team2Flag}`); } } lines.push(summary); lines.push(`END:VEVENT`); }) lines.push("END:VCALENDAR"); writeFileSync(filepath, lines.join('\n')); }); };