pegasus-autosign/getCourse.ts

108 lines
3.6 KiB
TypeScript

type Course = {
id: number;
start: string; // e.g. "2025-09-24T10:45" (local wall time)
end: string; // e.g. "2025-09-24T12:45"
[k: string]: any;
};
import { token } from "./getToken";
const TZ = "Europe/Paris";
/** Format a Date (interpreted in the given time zone) to YYYYMMDD */
function formatYMD(date: Date, timeZone = TZ): string {
const fmt = new Intl.DateTimeFormat("en-CA", {
timeZone,
year: "numeric",
month: "2-digit",
day: "2-digit",
});
// en-CA gives "YYYY-MM-DD" -> strip dashes
return fmt.format(date).replaceAll("-", "");
}
/** Return Monday 00:00 and Sunday 23:59:59.999 of the week that contains `now` in TZ */
function getWeekBounds(now: Date, timeZone = TZ): { monday: Date; sunday: Date } {
// Get the "wall-clock" Y/M/D in TZ
const parts = new Intl.DateTimeFormat("en-GB", {
timeZone,
year: "numeric",
month: "2-digit",
day: "2-digit",
weekday: "short",
}).formatToParts(now);
const y = Number(parts.find((p) => p.type === "year")!.value);
const m = Number(parts.find((p) => p.type === "month")!.value);
const d = Number(parts.find((p) => p.type === "day")!.value);
const wk = parts.find((p) => p.type === "weekday")!.value; // "Mon".."Sun"
// Build a Date that represents midnight in TZ by using UTC as a container
// (Date.UTC so it's not shifted by the host machine's local tz)
const midnightInTZ = new Date(Date.UTC(y, m - 1, d, 0, 0, 0, 0));
// Map Mon..Sun -> 0..6 (offset from Monday)
const weekdayIdx = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].indexOf(wk);
const monday = new Date(midnightInTZ);
monday.setUTCDate(monday.getUTCDate() - weekdayIdx);
const sunday = new Date(monday);
sunday.setUTCDate(sunday.getUTCDate() + 6);
sunday.setUTCHours(23, 59, 59, 999);
return { monday, sunday };
}
/**
* Returns the course occurring at the moment this function runs.
* If none is in progress, returns null.
*/
export async function getCurrentCourse(now: Date = new Date()): Promise<Course | null> {
// Compute Mon..Sun bounds in Europe/Paris, then format as YYYYMMDD for the query.
const { monday, sunday } = getWeekBounds(now, TZ);
const startYMD = formatYMD(monday, TZ); // e.g. "20250922"
const endYMD = formatYMD(sunday, TZ); // e.g. "20250928"
const url = `https://learning.estia.fr/pegasus/index.php?com=emergement&job=get-cours.json&start=${startYMD}&end=${endYMD}`;
const res = await fetch(url, {
credentials: "include",
headers: {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:142.0) Gecko/20100101 Firefox/142.0",
Accept: "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "en-US,en;q=0.5",
"X-Requested-With": "XMLHttpRequest",
"Sec-GPC": "1",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
Pragma: "no-cache",
"Cache-Control": "no-cache",
cookie: `PHPSESSID=${token}`,
Referer: "https://learning.estia.fr/pegasus/index.php?com=emergement&job=load-cours-programmes-apprenant",
},
method: "GET",
mode: "cors",
});
if (!res.ok) {
throw new Error(`Failed to fetch courses: ${res.status} ${res.statusText}`);
}
const courses: Course[] = (await res.json()) as Course[];
// Sort by start time just in case (ascending)
courses.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
// Find the one currently in progress: start <= now < end
const current = courses.find((c) => {
const start = new Date(c.start).getTime();
const end = new Date(c.end).getTime();
const t = now.getTime();
return t >= start && t < end;
});
console.log("Current course:", current);
return current ?? null;
}