mirror of
https://github.com/clarencechaan/ufc-cal.git
synced 2026-10-19 08:38:30 -07:00
Convert to TypeScript
This commit is contained in:
1 parent
a1163ca642
commit
44c2594121
13 files changed
+868
-317
No files matched your search
@@ -22,7 +22,7 @@ The three biggest points that this aims to address, which I found lacking in oth
|
||||
|
||||
## Example event
|
||||
|
||||

|
||||

|
||||
|
||||
## Info for nerds
|
||||
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import { getAllDetailedEvents } from "./scrape.js";
|
||||
import { writeFileSync } from "fs";
|
||||
import { createEvents } from "ics";
|
||||
|
||||
/**
|
||||
* Extracts the details of recent and near-future UFC events, then creates an
|
||||
* ICS file named "UFC.ics" in the current directory containing these events
|
||||
*
|
||||
* @returns {Promise<void>} - A Promise that resolves when the ICS file is created
|
||||
*/
|
||||
async function createICS() {
|
||||
try {
|
||||
let events = await getAllDetailedEvents();
|
||||
if (!events.length) throw new Error("No events retrieved");
|
||||
|
||||
// Get current date and time to communicate to the user how up-to-date
|
||||
// the event details are
|
||||
let currentDateTime = new Date();
|
||||
let dateTimestr = currentDateTime.toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
timeZone: "America/Toronto",
|
||||
timeZoneName: "short",
|
||||
});
|
||||
|
||||
// Convert event details in the format in accordance with the ICS generator
|
||||
events = events.map((event) => {
|
||||
let date = new Date(parseInt(event.date) * 1000);
|
||||
let start = [
|
||||
date.getFullYear(),
|
||||
date.getMonth() + 1,
|
||||
date.getDate(),
|
||||
date.getHours(),
|
||||
date.getMinutes(),
|
||||
];
|
||||
let duration = { hours: 3 };
|
||||
let title = event.name;
|
||||
let description = "";
|
||||
|
||||
// Distinguish between main card and prelims if this information has been
|
||||
// announced by the UFC, otherwise list all fights without categorizing
|
||||
if (event.fightCard.length)
|
||||
description = `${event.fightCard.join("\n")}\n`;
|
||||
if (event.mainCard.length)
|
||||
description += `Main Card\n--------------------\n${event.mainCard.join(
|
||||
"\n"
|
||||
)}\n`;
|
||||
if (event.prelims.length) {
|
||||
description += "\nPrelims";
|
||||
if (event.prelimsTime) {
|
||||
let prelimsTime = new Date(parseInt(event.prelimsTime) * 1000);
|
||||
let hoursAgo = (date - prelimsTime) / 1000 / 60 / 60;
|
||||
if (hoursAgo > 0) description += ` (${hoursAgo} hrs before Main)`;
|
||||
}
|
||||
description += `\n--------------------\n${event.prelims.join("\n")}\n`;
|
||||
}
|
||||
if (event.earlyPrelims.length) {
|
||||
description += "\nEarly Prelims";
|
||||
if (event.earlyPrelimsTime) {
|
||||
let earlyPrelimsTime = new Date(
|
||||
parseInt(event.earlyPrelimsTime) * 1000
|
||||
);
|
||||
let hoursAgo = (date - earlyPrelimsTime) / 1000 / 60 / 60;
|
||||
if (hoursAgo > 0) description += ` (${hoursAgo} hrs before Main)`;
|
||||
}
|
||||
description += `\n--------------------\n${event.earlyPrelims.join(
|
||||
"\n"
|
||||
)}\n`;
|
||||
}
|
||||
description += `\n${event.url}`;
|
||||
description += `\n\nAccurate as of ${dateTimestr}`;
|
||||
|
||||
let location = event.location;
|
||||
let uid = event.url;
|
||||
let calName = "UFC";
|
||||
return {
|
||||
start,
|
||||
duration,
|
||||
title,
|
||||
description,
|
||||
location,
|
||||
uid,
|
||||
calName,
|
||||
};
|
||||
});
|
||||
|
||||
console.log("\nDetailed events:");
|
||||
console.log(events);
|
||||
|
||||
// Create UFC.ics
|
||||
writeFileSync("UFC.ics", createEvents(events).value);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
createICS();
|
||||
Generated
+526
-1
@@ -9,9 +9,429 @@
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.12.0",
|
||||
"html-entities": "^2.4.0",
|
||||
"ics": "^3.4.0",
|
||||
"node-html-parser": "^6.1.9"
|
||||
"node-html-parser": "^6.1.9",
|
||||
"tsx": "^4.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
|
||||
"integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
|
||||
"integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
|
||||
"integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
|
||||
"integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
|
||||
"integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
|
||||
"integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
|
||||
"integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
|
||||
"integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
||||
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz",
|
||||
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
@@ -115,6 +535,74 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
|
||||
"integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.23.1",
|
||||
"@esbuild/android-arm": "0.23.1",
|
||||
"@esbuild/android-arm64": "0.23.1",
|
||||
"@esbuild/android-x64": "0.23.1",
|
||||
"@esbuild/darwin-arm64": "0.23.1",
|
||||
"@esbuild/darwin-x64": "0.23.1",
|
||||
"@esbuild/freebsd-arm64": "0.23.1",
|
||||
"@esbuild/freebsd-x64": "0.23.1",
|
||||
"@esbuild/linux-arm": "0.23.1",
|
||||
"@esbuild/linux-arm64": "0.23.1",
|
||||
"@esbuild/linux-ia32": "0.23.1",
|
||||
"@esbuild/linux-loong64": "0.23.1",
|
||||
"@esbuild/linux-mips64el": "0.23.1",
|
||||
"@esbuild/linux-ppc64": "0.23.1",
|
||||
"@esbuild/linux-riscv64": "0.23.1",
|
||||
"@esbuild/linux-s390x": "0.23.1",
|
||||
"@esbuild/linux-x64": "0.23.1",
|
||||
"@esbuild/netbsd-x64": "0.23.1",
|
||||
"@esbuild/openbsd-arm64": "0.23.1",
|
||||
"@esbuild/openbsd-x64": "0.23.1",
|
||||
"@esbuild/sunos-x64": "0.23.1",
|
||||
"@esbuild/win32-arm64": "0.23.1",
|
||||
"@esbuild/win32-ia32": "0.23.1",
|
||||
"@esbuild/win32-x64": "0.23.1"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
|
||||
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
@@ -196,6 +684,16 @@
|
||||
"integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-case": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
|
||||
@@ -208,6 +706,26 @@
|
||||
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
|
||||
"integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.23.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/type-fest": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
|
||||
@@ -220,6 +738,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/yup": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz",
|
||||
|
||||
+4
-2
@@ -5,14 +5,16 @@
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node index.js"
|
||||
"start": "npx tsx src/index.ts"
|
||||
},
|
||||
"author": "Clarence Chan",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.12.0",
|
||||
"html-entities": "^2.4.0",
|
||||
"ics": "^3.4.0",
|
||||
"node-html-parser": "^6.1.9"
|
||||
"node-html-parser": "^6.1.9",
|
||||
"tsx": "^4.19.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
import { parse } from "node-html-parser";
|
||||
import { decode } from "html-entities";
|
||||
|
||||
/**
|
||||
* Returns an array of URL strings of recent and near-future UFC events
|
||||
*
|
||||
* @returns {Promise<Array<string>>} - A Promise that resolves with the array
|
||||
* of URL strings of UFC events
|
||||
*/
|
||||
async function getEventLinks() {
|
||||
// Get first two pages of event links
|
||||
let urls = [
|
||||
"https://www.ufc.com/events?page=0",
|
||||
"https://www.ufc.com/events?page=1",
|
||||
];
|
||||
|
||||
async function getEventLinksFromUrl(url) {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const root = parse(text);
|
||||
|
||||
// Extract the URLs of the relevant events from the DOM object
|
||||
let links = root.querySelectorAll(".c-card-event--result__headline");
|
||||
links = links.map(
|
||||
(html) => `https://www.ufc.com${html.firstChild.getAttribute("href")}`
|
||||
);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
try {
|
||||
urls = urls.map(getEventLinksFromUrl);
|
||||
|
||||
let links = await Promise.all(urls);
|
||||
links = links.flat();
|
||||
|
||||
console.log("\nEvent links found:");
|
||||
console.log(links);
|
||||
return links;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fight card details of a UFC event given its URL
|
||||
*
|
||||
* @param {string} url - A URL string of a UFC event
|
||||
* @returns {Promise<object>} - A Promise that resolves with the fight card details of
|
||||
* the UFC event
|
||||
*/
|
||||
async function getDetailsFromEventLink(url) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const root = parse(text);
|
||||
|
||||
// Extract basic details of the event from the DOM object
|
||||
let headlinePrefix = root
|
||||
.querySelector(".c-hero__headline-prefix")
|
||||
.innerText.trim();
|
||||
let headline = root
|
||||
.querySelector(".c-hero__headline")
|
||||
.innerText.replace(/\s\s+/g, " ")
|
||||
.trim();
|
||||
let name = `${headlinePrefix}: ${headline}`;
|
||||
let date = root
|
||||
.querySelector(".c-hero__headline-suffix")
|
||||
.getAttribute("data-timestamp");
|
||||
let prelimsTime = root
|
||||
.querySelector("#prelims-card .c-event-fight-card-broadcaster__time")
|
||||
?.getAttribute("data-timestamp");
|
||||
let earlyPrelimsTime = root
|
||||
.querySelector("#early-prelims .c-event-fight-card-broadcaster__time")
|
||||
?.getAttribute("data-timestamp");
|
||||
|
||||
let location =
|
||||
root
|
||||
.querySelector(".field--name-venue")
|
||||
?.innerText.replaceAll("\n", "")
|
||||
.replaceAll(" ", " ") || "";
|
||||
let fightCard = [];
|
||||
let mainCard = root.querySelectorAll("#main-card .l-listing__item");
|
||||
let prelims = [];
|
||||
let earlyPrelims = [];
|
||||
|
||||
/**
|
||||
* Helper function to return the string of a fight given its DOM list item
|
||||
*
|
||||
* @param {object} li - The DOM list item of a fight
|
||||
* @returns {string} - The string of a fight
|
||||
*/
|
||||
function convertLiToStr(li) {
|
||||
let bout = li.querySelector(".c-listing-fight__class-text")?.innerText;
|
||||
|
||||
// Return only fight names without bout weightclass if formatting for
|
||||
// an event is broken on the UFC event page
|
||||
if (!bout)
|
||||
return (
|
||||
"• " +
|
||||
li
|
||||
.querySelector(".field--name-node-title")
|
||||
.innerText.trim()
|
||||
.replace(" vs ", " vs. ")
|
||||
);
|
||||
|
||||
// Convert weightclass to lbs
|
||||
if (bout.includes("Strawweight")) bout = "115";
|
||||
else if (bout.includes("Flyweight")) bout = "125";
|
||||
else if (bout.includes("Bantamweight")) bout = "135";
|
||||
else if (bout.includes("Featherweight")) bout = "145";
|
||||
else if (bout.includes("Lightweight")) bout = "155";
|
||||
else if (bout.includes("Welterweight")) bout = "170";
|
||||
else if (bout.includes("Middleweight")) bout = "185";
|
||||
else if (bout.includes("Light Heavyweight")) bout = "205";
|
||||
else if (bout.includes("Heavyweight")) bout = "265";
|
||||
else if (bout.includes("Catchweight")) bout = "CW";
|
||||
|
||||
// Extract and format fighter names with weightclass
|
||||
let red = li
|
||||
.querySelector(".c-listing-fight__corner-name--red")
|
||||
.innerText.replaceAll("\n", "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
let blue = li
|
||||
.querySelector(".c-listing-fight__corner-name--blue")
|
||||
.innerText.replaceAll("\n", "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
let ranks = li.querySelectorAll(
|
||||
".c-listing-fight__ranks-row .js-listing-fight__corner-rank span"
|
||||
);
|
||||
ranks = ranks.map((rank) => rank?.innerText);
|
||||
let redRankStr = ranks[0] ? ` (${ranks[0]})` : "";
|
||||
let blueRankStr = ranks[1] ? ` (${ranks[1]})` : "";
|
||||
let fightStr = `• ${red}${redRankStr} vs. ${blue}${blueRankStr} @${bout}`;
|
||||
|
||||
// Deentitize HTML entities
|
||||
fightStr = decode(fightStr);
|
||||
|
||||
return fightStr;
|
||||
}
|
||||
|
||||
console.log("\nGetting details from url:", url);
|
||||
|
||||
// Check if main card & prelims have been announced
|
||||
if (mainCard.length) {
|
||||
// Main card has been announced, extract prelims
|
||||
prelims = root.querySelectorAll("#prelims-card .l-listing__item");
|
||||
earlyPrelims = root.querySelectorAll("#early-prelims .l-listing__item");
|
||||
|
||||
console.log("Main card length:", mainCard.length);
|
||||
console.log("Prelims length:", prelims.length);
|
||||
|
||||
mainCard = mainCard.map(convertLiToStr);
|
||||
prelims = prelims.map(convertLiToStr);
|
||||
earlyPrelims = earlyPrelims.map(convertLiToStr);
|
||||
} else {
|
||||
// Main card has not been announced, extract entire fight card
|
||||
fightCard = root.querySelectorAll(
|
||||
".l-listing__group--bordered .l-listing__item"
|
||||
);
|
||||
|
||||
console.log("Fight card length:", fightCard.length);
|
||||
|
||||
fightCard = fightCard.map(convertLiToStr);
|
||||
}
|
||||
|
||||
// Deentitize HTML entities
|
||||
[name, location] = [decode(name), decode(location)];
|
||||
|
||||
let details = {
|
||||
name,
|
||||
url,
|
||||
date,
|
||||
location,
|
||||
fightCard,
|
||||
mainCard,
|
||||
prelims,
|
||||
earlyPrelims,
|
||||
prelimsTime,
|
||||
earlyPrelimsTime,
|
||||
};
|
||||
return details;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to retrieve event: ${url}\n${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of details of recent and near-future UFC events
|
||||
*
|
||||
* @returns {Promise<Array<object>>} - A Promise that resolves with the array
|
||||
* of details of recent and near-future UFC events
|
||||
*/
|
||||
async function getAllDetailedEvents() {
|
||||
let events = [];
|
||||
try {
|
||||
// Get the URLs of recent and near-future UFC events
|
||||
events = await getEventLinks();
|
||||
|
||||
// Convert each URL to the details of the corresponding UFC event
|
||||
events = events.map(getDetailsFromEventLink);
|
||||
events = await Promise.all(events);
|
||||
return events;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
export { getAllDetailedEvents };
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>UFC Calendar</title>
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
<link rel="icon" type="image/x-icon" href="./assets/favicon.ico" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
@@ -69,13 +69,13 @@
|
||||
<a
|
||||
class="sub-link"
|
||||
href="webcal://raw.githubusercontent.com/clarencechaan/ufc-cal/ics/UFC.ics"
|
||||
><img src="apple.svg" alt="apple" />
|
||||
><img src="./assets/apple.svg" alt="apple" />
|
||||
<div>Apple</div>
|
||||
</a>
|
||||
<a
|
||||
class="sub-link"
|
||||
href="https://calendar.google.com/calendar/u/0/r?cid=webcal://raw.githubusercontent.com/clarencechaan/ufc-cal/ics/UFC.ics"
|
||||
><img src="google.svg" alt="google" />
|
||||
><img src="./assets/google.svg" alt="google" />
|
||||
<div>Google</div>
|
||||
</a>
|
||||
</div>
|
||||
@@ -0,0 +1,99 @@
|
||||
import { getAllDetailedEvents } from "./scrape.js";
|
||||
import * as fs from "fs";
|
||||
import { createEvents, DateArray, EventAttributes } from "ics";
|
||||
|
||||
/**
|
||||
* Extracts the details of recent and near-future UFC events, then creates an
|
||||
* ICS file named "UFC.ics" in the current directory containing these events
|
||||
*/
|
||||
async function createICS() {
|
||||
try {
|
||||
const events = await getAllDetailedEvents();
|
||||
if (!events?.length) throw new Error("No events retrieved");
|
||||
|
||||
// Convert event details in the format in accordance with the ICS generator
|
||||
const formattedEvents = events.map(formatEventForCalendar);
|
||||
|
||||
console.log("\nDetailed events:");
|
||||
console.log(formattedEvents);
|
||||
|
||||
// Create UFC.ics
|
||||
const eventsData = createEvents(formattedEvents).value;
|
||||
if (eventsData) fs.writeFileSync("UFC.ics", eventsData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
function formatEventForCalendar(event: UFCEvent) {
|
||||
const date = new Date(parseInt(event.date) * 1000);
|
||||
const start: DateArray = [
|
||||
date.getFullYear(),
|
||||
date.getMonth() + 1,
|
||||
date.getDate(),
|
||||
date.getHours(),
|
||||
date.getMinutes(),
|
||||
];
|
||||
const duration: { hours: number } = { hours: 3 };
|
||||
const title = event.name;
|
||||
let description = "";
|
||||
|
||||
// Distinguish between main card and prelims if this information has been
|
||||
// announced by the UFC, otherwise list all fights without categorizing
|
||||
if (event.fightCard.length) description = `${event.fightCard.join("\n")}\n`;
|
||||
if (event.mainCard.length)
|
||||
description += `Main Card\n--------------------\n${event.mainCard.join(
|
||||
"\n"
|
||||
)}\n`;
|
||||
if (event.prelims.length) {
|
||||
description += "\nPrelims";
|
||||
if (event.prelimsTime) {
|
||||
const prelimsTime = new Date(parseInt(event.prelimsTime) * 1000);
|
||||
const hoursAgo = (+date - +prelimsTime) / 1000 / 60 / 60;
|
||||
if (hoursAgo > 0) description += ` (${hoursAgo} hrs before Main)`;
|
||||
}
|
||||
description += `\n--------------------\n${event.prelims.join("\n")}\n`;
|
||||
}
|
||||
if (event.earlyPrelims.length) {
|
||||
description += "\nEarly Prelims";
|
||||
if (event.earlyPrelimsTime) {
|
||||
const earlyPrelimsTime = new Date(
|
||||
parseInt(event.earlyPrelimsTime) * 1000
|
||||
);
|
||||
const hoursAgo = (+date - +earlyPrelimsTime) / 1000 / 60 / 60;
|
||||
if (hoursAgo > 0) description += ` (${hoursAgo} hrs before Main)`;
|
||||
}
|
||||
description += `\n--------------------\n${event.earlyPrelims.join("\n")}\n`;
|
||||
}
|
||||
description += `\n${event.url}`;
|
||||
|
||||
// Get current date and time to communicate to the user how up-to-date
|
||||
// the event details are
|
||||
const dateTimestr = new Date().toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
timeZone: "America/Toronto",
|
||||
timeZoneName: "short",
|
||||
});
|
||||
description += `\n\nAccurate as of ${dateTimestr}`;
|
||||
|
||||
const location = event.location;
|
||||
const uid = event.url.href;
|
||||
const calName = "UFC";
|
||||
|
||||
const calendarEvent: EventAttributes = {
|
||||
start,
|
||||
duration,
|
||||
title,
|
||||
description,
|
||||
location,
|
||||
uid,
|
||||
calName,
|
||||
};
|
||||
|
||||
return calendarEvent;
|
||||
}
|
||||
|
||||
createICS();
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
import { parse, HTMLElement } from "node-html-parser";
|
||||
import { decode } from "html-entities";
|
||||
|
||||
/**
|
||||
* Returns an array of URLs of recent and upcoming UFC events
|
||||
*/
|
||||
async function getEventURLs() {
|
||||
// Get first two pages of event urls
|
||||
const pageURLs = [
|
||||
new URL("https://www.ufc.com/events?page=0"),
|
||||
new URL("https://www.ufc.com/events?page=1"),
|
||||
];
|
||||
|
||||
async function getEventLinksFromUrl(url: URL) {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const root = parse(text);
|
||||
|
||||
// Extract the URLs of the relevant events from the HTML element
|
||||
const eventURLElements = root.querySelectorAll(
|
||||
".c-card-event--result__headline"
|
||||
);
|
||||
const eventURLs = eventURLElements.map(
|
||||
(html) =>
|
||||
new URL(
|
||||
`https://www.ufc.com${(html.firstChild as HTMLElement).getAttribute(
|
||||
"href"
|
||||
)}`
|
||||
)
|
||||
);
|
||||
|
||||
return eventURLs;
|
||||
}
|
||||
|
||||
try {
|
||||
const eventURLs = (
|
||||
await Promise.all(pageURLs.map(getEventLinksFromUrl))
|
||||
).flat();
|
||||
|
||||
console.log("\nEvent URLs found:");
|
||||
console.log(eventURLs);
|
||||
return eventURLs;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return the output string of a fight given its HTML
|
||||
* element list item
|
||||
*/
|
||||
function convertLiToStr(li: HTMLElement) {
|
||||
const bout = li.querySelector(".c-listing-fight__class-text")?.textContent;
|
||||
|
||||
let fightStr = "";
|
||||
|
||||
// Return only fight names without bout weightclass if formatting for
|
||||
// an event is broken on the UFC event page
|
||||
if (!bout) {
|
||||
const textContent = li
|
||||
.querySelector(".field--name-node-title")
|
||||
?.textContent?.trim()
|
||||
.replace(" vs ", " vs. ");
|
||||
if (!textContent) return "";
|
||||
fightStr += `• ${textContent}`;
|
||||
return fightStr;
|
||||
}
|
||||
|
||||
let weightClass = "";
|
||||
|
||||
// Get weightclass in short form
|
||||
if (bout.includes("Strawweight")) weightClass = "115";
|
||||
else if (bout.includes("Flyweight")) weightClass = "125";
|
||||
else if (bout.includes("Bantamweight")) weightClass = "135";
|
||||
else if (bout.includes("Featherweight")) weightClass = "145";
|
||||
else if (bout.includes("Lightweight")) weightClass = "155";
|
||||
else if (bout.includes("Welterweight")) weightClass = "170";
|
||||
else if (bout.includes("Middleweight")) weightClass = "185";
|
||||
else if (bout.includes("Light Heavyweight")) weightClass = "205";
|
||||
else if (bout.includes("Heavyweight")) weightClass = "265";
|
||||
else if (bout.includes("Catchweight")) weightClass = "CW";
|
||||
|
||||
// Extract and format fighter names with weightclass
|
||||
const red = li
|
||||
.querySelector(".c-listing-fight__corner-name--red")
|
||||
?.textContent?.replaceAll("\n", "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
const blue = li
|
||||
.querySelector(".c-listing-fight__corner-name--blue")
|
||||
?.textContent?.replaceAll("\n", "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
const ranks = [
|
||||
...li.querySelectorAll(
|
||||
".c-listing-fight__ranks-row .js-listing-fight__corner-rank span"
|
||||
),
|
||||
].map((rank) => rank?.textContent);
|
||||
|
||||
const redRankStr = ranks[0] ? ` (${ranks[0]})` : "";
|
||||
const blueRankStr = ranks[1] ? ` (${ranks[1]})` : "";
|
||||
fightStr += `• ${red}${redRankStr} vs. ${blue}${blueRankStr} @${bout}`;
|
||||
|
||||
// Deentitize HTML entities
|
||||
fightStr = decode(fightStr);
|
||||
|
||||
return fightStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fight card details of a UFC event given its URL
|
||||
*/
|
||||
async function getDetailsFromEventLink(url: URL) {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const root = parse(text);
|
||||
|
||||
// Extract basic details of the event from the HTML
|
||||
const headlinePrefix = root
|
||||
.querySelector(".c-hero__headline-prefix")
|
||||
?.innerText.trim();
|
||||
const headline = root
|
||||
.querySelector(".c-hero__headline")
|
||||
?.innerText.replace(/\s\s+/g, " ")
|
||||
.trim();
|
||||
const date = root
|
||||
.querySelector(".c-hero__headline-suffix")
|
||||
?.getAttribute("data-timestamp");
|
||||
|
||||
const prelimsTime = root
|
||||
.querySelector("#prelims-card .c-event-fight-card-broadcaster__time")
|
||||
?.getAttribute("data-timestamp");
|
||||
const earlyPrelimsTime = root
|
||||
.querySelector("#early-prelims .c-event-fight-card-broadcaster__time")
|
||||
?.getAttribute("data-timestamp");
|
||||
|
||||
let name = `${headlinePrefix}: ${headline}`;
|
||||
let location =
|
||||
root
|
||||
.querySelector(".field--name-venue")
|
||||
?.innerText.replaceAll("\n", "")
|
||||
.replaceAll(" ", " ") || "";
|
||||
let fightCard: string[] = [];
|
||||
let mainCard: string[] = [];
|
||||
let prelims: string[] = [];
|
||||
let earlyPrelims: string[] = [];
|
||||
|
||||
console.log(`\nGetting details from url: ${url.href}`);
|
||||
|
||||
const mainCardElements = root.querySelectorAll(
|
||||
"#main-card .l-listing__item"
|
||||
);
|
||||
// Check if main card & prelims have been announced
|
||||
if (mainCardElements.length) {
|
||||
// Main card has been announced, extract prelims
|
||||
const prelimsElements = root.querySelectorAll(
|
||||
"#prelims-card .l-listing__item"
|
||||
);
|
||||
const earlyPrelimsElements = root.querySelectorAll(
|
||||
"#early-prelims .l-listing__item"
|
||||
);
|
||||
|
||||
mainCard = mainCardElements.map(convertLiToStr);
|
||||
prelims = prelimsElements.map(convertLiToStr);
|
||||
earlyPrelims = earlyPrelimsElements.map(convertLiToStr);
|
||||
|
||||
console.log(`Main card length: ${mainCard.length}`);
|
||||
console.log(`Prelims length: ${prelims.length}`);
|
||||
} else {
|
||||
// Main card has not been announced, extract entire fight card
|
||||
const fightCardElements = root.querySelectorAll(
|
||||
".l-listing__group--bordered .l-listing__item"
|
||||
);
|
||||
|
||||
fightCard = fightCardElements.map(convertLiToStr);
|
||||
|
||||
console.log(`Fight card length: ${fightCard.length}`);
|
||||
}
|
||||
|
||||
// Deentitize HTML entities
|
||||
[name, location] = [decode(name), decode(location)];
|
||||
|
||||
if (!name || !date) {
|
||||
throw new Error("Failed to retrieve event details");
|
||||
}
|
||||
|
||||
const details: UFCEvent = {
|
||||
name,
|
||||
url,
|
||||
date,
|
||||
location,
|
||||
fightCard,
|
||||
mainCard,
|
||||
prelims,
|
||||
earlyPrelims,
|
||||
prelimsTime,
|
||||
earlyPrelimsTime,
|
||||
};
|
||||
return details;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to retrieve event: ${url.href}\n${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of details of recent and upcoming UFC events
|
||||
*/
|
||||
async function getAllDetailedEvents() {
|
||||
try {
|
||||
const eventURLs = await getEventURLs();
|
||||
|
||||
// Convert each URL to the details of the corresponding UFC event
|
||||
const detailedEvents = await Promise.all(
|
||||
eventURLs?.map(getDetailsFromEventLink) ?? []
|
||||
);
|
||||
return detailedEvents;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
export { getAllDetailedEvents };
|
||||
@@ -0,0 +1,12 @@
|
||||
type UFCEvent = {
|
||||
name: string;
|
||||
url: URL;
|
||||
date: string;
|
||||
location: string;
|
||||
fightCard: string[];
|
||||
mainCard: string[];
|
||||
prelims: string[];
|
||||
earlyPrelims: string[];
|
||||
prelimsTime: string | undefined;
|
||||
earlyPrelimsTime: string | undefined;
|
||||
};
|
||||
Reference in new issue
Block a user