@studentsphere/ots-provider-wigor 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"schools.js","sourceRoot":"","sources":["../src/schools.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts DELETED
@@ -1,38 +0,0 @@
1
- export interface WigorEventJSON {
2
- Commentaire: string | null;
3
- NoCours: number;
4
- NoGroupe: number;
5
- LibelleGroupe: string | null;
6
- LibelleSemaine: string | null;
7
- Matiere: string | null;
8
- Salles: string | null;
9
- NomEcole: string | null;
10
- LogoEcole: string | null;
11
- CoursMixtePicto: string | null;
12
- CoursMixteInfoBulle: string | null;
13
- FondCour: string | null;
14
- NomProf: string | null;
15
- Duree: number | null;
16
- RecurrenceRule: string | null;
17
- RecurrenceID: string | null;
18
- RecurrenceException: string | null;
19
- IsAllDay: boolean;
20
- Title: string | null;
21
- Description: string | null;
22
- Start: string;
23
- StartTimezone: string | null;
24
- End: string;
25
- EndTimezone: string | null;
26
- TeamsURL: string | null;
27
- TeamsUrl: string | null;
28
- ColorRed: number;
29
- ColorGreen: number;
30
- ColorBlue: number;
31
- Origine: number;
32
- LienTrack: string | null;
33
- }
34
- export interface School {
35
- id: string;
36
- name: string;
37
- logo: string;
38
- }
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/dist/utils.d.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * Determines the schedule server URL based on the school ID.
3
- * @param schoolId The school ID.
4
- * @returns The schedule server URL.
5
- */
6
- export declare const getScheduleServer: (schoolId?: string) => string;
package/dist/utils.js DELETED
@@ -1,14 +0,0 @@
1
- import { CD_SCHOOLS, CD_SCHOOLS_TIMETABLE_ENDPOINT, IGENSIA_SCHOOLS_TIMETABLE_ENDPOINT, } from "./constants";
2
- /**
3
- * Determines the schedule server URL based on the school ID.
4
- * @param schoolId The school ID.
5
- * @returns The schedule server URL.
6
- */
7
- export const getScheduleServer = (schoolId) => {
8
- if (schoolId && CD_SCHOOLS.includes(schoolId)) {
9
- return CD_SCHOOLS_TIMETABLE_ENDPOINT;
10
- }
11
- // Fallback to igs which also seems to support /Home/Get
12
- return IGENSIA_SCHOOLS_TIMETABLE_ENDPOINT;
13
- };
14
- //# sourceMappingURL=utils.js.map
package/dist/utils.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,UAAU,EACV,6BAA6B,EAC7B,kCAAkC,GAClC,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAiB,EAAU,EAAE;IAC9D,IAAI,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,OAAO,6BAA6B,CAAC;IACtC,CAAC;IACD,wDAAwD;IACxD,OAAO,kCAAkC,CAAC;AAC3C,CAAC,CAAC"}
package/src/index.ts DELETED
@@ -1,390 +0,0 @@
1
- import {
2
- BaseTimetableProvider,
3
- type Course,
4
- type ProviderCredentials,
5
- type School,
6
- } from "@studentsphere/ots-core";
7
- import {
8
- CD_SCHOOLS,
9
- CD_SCHOOLS_TIMETABLE_ENDPOINT,
10
- IGENSIA_SCHOOLS,
11
- IGENSIA_SCHOOLS_TIMETABLE_ENDPOINT,
12
- LOGIN_SERVER_ENDPOINT,
13
- } from "./constants";
14
- import { SCHOOLS_DATA } from "./schools";
15
- import type { WigorEventJSON } from "./types";
16
-
17
- const getScheduleServer = (schoolId?: string) => {
18
- if (schoolId && CD_SCHOOLS.includes(schoolId)) {
19
- return CD_SCHOOLS_TIMETABLE_ENDPOINT;
20
- }
21
- if (schoolId && IGENSIA_SCHOOLS.includes(schoolId)) {
22
- return IGENSIA_SCHOOLS_TIMETABLE_ENDPOINT;
23
- }
24
- return CD_SCHOOLS_TIMETABLE_ENDPOINT;
25
- };
26
-
27
- export class WigorProvider extends BaseTimetableProvider {
28
- get id(): string {
29
- return "wigor";
30
- }
31
-
32
- get name(): string {
33
- return "Wigor";
34
- }
35
-
36
- get logo(): string {
37
- return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEABAMAAACuXLVVAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA9QTFRFj79V////4+/VxN6mqM176BQKnwAAA0BJREFUeNrtm92N2lAUhPfaLuAeTAH2pgGcCkznLiEFESkPkYDJ8WgtmYGNmO8lSqyNv/M38LIfxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGPMt+fn55UBn3S3Jx9Ie3vyY7fAFFfqPwJxZcYnJa70uwXOceWIT5q4cvriZ3YA1VCBIXUNnuwAXnNIdZLhjI8UaOPG1kYHDGcnXbBdG2E49Ef2wssZ2XBaeLALPlCsM75YG0UQoMDMl0MWBE3AcEQCPAhQYBBcIVkpqBOHoxcIshsRPb9CWRCMQgEeBEwgVFfIP1q6eIVAxdWA3pArlAVBQwWmRws0q7tWAqh8ZIJvBNCXVGtwgf13CHUSAbxCWRCMARx4cO1nXNu1YAI4MdkddhHITHZWI4CNxt6ck4DkA7khAqgrFCgRSBV8GJO9hkbnO8SuyIJgikB6yYcxL2pkAtguXRBEgsSA6A67SMx0Y/dzTgJtJE6SbwOsqiYSgyAGeLqWSFQSA5ogOEfiqIgBettTJHpNDJCyxkgcSLM0QRArkBiQBEEXK8yyGMjH3cYKJ1kM5LqaWGGQxUCebIkVao4BWRCcY4VjvhdZEEyxQi+LgVzYGCscZDGQRxurpGWRBUEXq8yyGMDzhkYfYDiqGMDKsNEwHFUM4CuxTnwl6oiCAAQqNh0HogoCqBOGI4sBLA0E7hdSFwM4XKgT3omrIgsCEIC/4LGo7vAIdcK3JdxIWRBQgWFSXSGGH9QJNzk+RSAmJtBjDMiCAOqEzYcYEN0hcsLepCuUC8wqAR4ECIZkQAxo7hA50N7Uhws0pE7Sm+HhAu23FKg4HEUM3KB1NmQ7hXeIdbb0CvV3OL9agA6nFwgUWueoigGkoXVOe66w2d4B+k+sXL3A5e8fS34i6MAaFdTUHbjU7QJlUXRggwCiF1huq4A0EoHU1kLVLhKBsl2gvoEANHqRCvDBVqq2CARyXVygfLyJwALDUQnwyRaqdhEJlK0C9R0EsNGLUICPttL/ZVEI5Moq79TTBcpLBJY0HLnAQl8zwK7IBMq2OusLBH49R+A3bXRBARmfd5zw11ru8G8GGWOMMcYYY4wxxhhjjDHGGGOMMcYYY4z5v/gDnuEEumjCiJsAAAAASUVORK5CYII=";
38
- }
39
-
40
- get schools(): School[] {
41
- return SCHOOLS_DATA;
42
- }
43
-
44
- async validateCredentials(
45
- credentials: ProviderCredentials,
46
- ): Promise<boolean> {
47
- try {
48
- const { cookies } = await this.login(LOGIN_SERVER_ENDPOINT, credentials);
49
- return cookies.size > 0;
50
- } catch (_err) {
51
- return false;
52
- }
53
- }
54
-
55
- async getSchedule(
56
- credentials: ProviderCredentials,
57
- from: Date,
58
- to: Date,
59
- ): Promise<Course[]> {
60
- const scheduleServer = getScheduleServer(
61
- credentials.schoolId as string | undefined,
62
- );
63
-
64
- const params = new URLSearchParams({
65
- sort: "",
66
- group: "",
67
- filter: "",
68
- dateDebut: from.toISOString(),
69
- dateFin: to.toISOString(),
70
- });
71
-
72
- const fullScheduleUrl = `${scheduleServer}?${params.toString()}`;
73
-
74
- const { cookies } = await this.login(
75
- LOGIN_SERVER_ENDPOINT,
76
- credentials,
77
- fullScheduleUrl,
78
- );
79
-
80
- const response = await fetch(fullScheduleUrl, {
81
- headers: {
82
- "User-Agent": "ots",
83
- Cookie: this.serializeCookies(cookies, scheduleServer),
84
- },
85
- });
86
-
87
- if (!response.ok || response.status === 302) {
88
- const text = await response.text();
89
- if (text.includes("cas/login") || response.status === 302) {
90
- throw new Error(
91
- "Session expired or redirected to login. Authentication was not fully established.",
92
- );
93
- }
94
- throw new Error(
95
- `Failed to fetch schedule: ${response.status} ${response.statusText}`,
96
- );
97
- }
98
-
99
- const contentType = response.headers.get("content-type");
100
- if (!contentType?.includes("application/json")) {
101
- const text = await response.text();
102
- console.error("[Wigor] Expected JSON but received:", text.slice(0, 200));
103
- throw new Error(
104
- `Expected JSON response but received ${contentType || "unknown content type"}. You might have been redirected to a login page.`,
105
- );
106
- }
107
-
108
- const json = (await response.json()) as { Data: WigorEventJSON[] };
109
- const events = json.Data || [];
110
-
111
- return events.map((event) => {
112
- const subject =
113
- event.Commentaire && event.Commentaire !== "COMMENTAIRE"
114
- ? event.Commentaire
115
- : event.Matiere || event.Title || "Sans titre";
116
-
117
- return {
118
- hash: event.NoCours.toString(),
119
- subject,
120
- start: new Date(event.Start),
121
- end: new Date(event.End),
122
- location:
123
- event.Salles && event.Salles !== "Aucune" ? event.Salles : undefined,
124
- teacher: event.NomProf || "Anonyme",
125
- color: `rgb(${event.ColorRed},${event.ColorGreen},${event.ColorBlue})`,
126
- description: (() => {
127
- const parts: string[] = [];
128
- parts.push(`\n`);
129
- const teamsUrlField = event.TeamsURL || event.TeamsUrl;
130
- if (teamsUrlField) {
131
- const linkRegex =
132
- /<a [^>]*href="([^"]+)"[^>]*>[\s\S]*?<img [^>]*src="([^"]+)"[^>]*>[\s\S]*?<\/a>/gi;
133
- let match: RegExpExecArray | null;
134
- // biome-ignore lint/suspicious/noAssignInExpressions: valid regex usage
135
- while ((match = linkRegex.exec(teamsUrlField)) !== null) {
136
- if (match[1] && match[2]) {
137
- const url = match[1];
138
- const imgSrc = match[2];
139
- const labelMatch = imgSrc.match(/MTeams_([^.]+)\.png/);
140
- const label = labelMatch
141
- ? `Lien Teams ${labelMatch[1].replace(/_/g, " ")}`
142
- : "Lien Teams";
143
- parts.push(`${label}:\n${url}\n`);
144
- }
145
- }
146
-
147
- if (parts.length <= 1) {
148
- // 1 because of the initial newline
149
- const simpleUrls = [
150
- ...teamsUrlField.matchAll(/href="([^"]+)"/g),
151
- ].map((m) => m[1]);
152
- for (const url of simpleUrls) {
153
- if (url) parts.push(`Lien Teams:\n${url}\n`);
154
- }
155
- }
156
- }
157
-
158
- if (event.LibelleGroupe) {
159
- parts.push(event.LibelleGroupe);
160
- }
161
- if (event.LibelleSemaine) {
162
- parts.push(event.LibelleSemaine);
163
- }
164
- if (event.Commentaire && event.Commentaire !== "COMMENTAIRE") {
165
- parts.push(event.Commentaire);
166
- }
167
- if (event.Description) {
168
- parts.push(event.Description);
169
- }
170
- return parts.length > 0 ? parts.join("\n") : undefined;
171
- })(),
172
- originalData: event,
173
- };
174
- });
175
- }
176
-
177
- private async login(
178
- loginServer: string,
179
- credentials: ProviderCredentials,
180
- service?: string,
181
- ): Promise<{
182
- cookies: Map<
183
- string,
184
- Map<string, { value: string; path: string; expires?: number }>
185
- >;
186
- }> {
187
- const jar: Map<
188
- string,
189
- Map<string, { value: string; path: string; expires?: number }>
190
- > = new Map();
191
- const serviceParam = service
192
- ? `?service=${encodeURIComponent(service)}`
193
- : "";
194
-
195
- let currentUrl = `${loginServer}${serviceParam}`;
196
-
197
- // 1. GET login page to extract hidden fields
198
- const getRes = await fetch(currentUrl, {
199
- headers: { "User-Agent": "ots" },
200
- });
201
- this.updateCookies(jar, currentUrl, getRes.headers.getSetCookie());
202
- const html = await getRes.text();
203
- const hidden = this.extractHiddenFields(html);
204
-
205
- // 2. POST credentials
206
- const form = new URLSearchParams();
207
- form.append("username", credentials.identifier as string);
208
- form.append("password", credentials.password || "");
209
- for (const [k, v] of Object.entries(hidden)) {
210
- if (k !== "username" && k !== "password") form.append(k, v);
211
- }
212
- if (!form.has("_eventId")) form.append("_eventId", "submit");
213
- let response = await fetch(currentUrl, {
214
- method: "POST",
215
- headers: {
216
- "Content-Type": "application/x-www-form-urlencoded",
217
- "User-Agent": "ots",
218
- Cookie: this.serializeCookies(jar, currentUrl),
219
- },
220
- body: form.toString(),
221
- redirect: "manual",
222
- });
223
-
224
- // 3. Follow redirect chain manually to capture all intermediate cookies
225
- let redirectCount = 0;
226
- const maxRedirects = 15;
227
- const seenTickets = new Set<string>();
228
-
229
- while (true) {
230
- this.updateCookies(jar, currentUrl, response.headers.getSetCookie());
231
-
232
- if (response.status >= 300 && response.status < 400) {
233
- let location = response.headers.get("location");
234
- if (!location) break;
235
-
236
- // Ticket Reuse Prevention: detect and strip reused tickets
237
- const urlObj = new URL(location, currentUrl);
238
- const ticket = urlObj.searchParams.get("ticket");
239
- if (ticket) {
240
- if (seenTickets.has(ticket)) {
241
- urlObj.searchParams.delete("ticket");
242
- location = urlObj.toString();
243
- } else {
244
- seenTickets.add(ticket);
245
- }
246
- }
247
-
248
- currentUrl = new URL(location, currentUrl).toString();
249
- const cookieHeader = this.serializeCookies(jar, currentUrl);
250
- if (cookieHeader)
251
- response = await fetch(currentUrl, {
252
- headers: {
253
- "User-Agent": "ots",
254
- Cookie: cookieHeader,
255
- },
256
- redirect: "manual",
257
- });
258
- redirectCount++;
259
- if (redirectCount > maxRedirects) break;
260
- } else {
261
- // Final response or error
262
- if (response.status >= 400) {
263
- const body = await response.text();
264
- console.error(
265
- `[Wigor] [DEBUG] Error Status: ${response.status} at ${currentUrl}`,
266
- );
267
- console.error(`[Wigor] [DEBUG] Error Body: ${body.slice(0, 1000)}`);
268
- }
269
- break;
270
- }
271
- }
272
-
273
- if (response.status >= 400) {
274
- throw new Error(
275
- `Authentication failed with status ${response.status} at ${currentUrl}`,
276
- );
277
- }
278
-
279
- return { cookies: jar };
280
- }
281
-
282
- private extractHiddenFields(html: string): Record<string, string> {
283
- const fields: Record<string, string> = {};
284
- const regex =
285
- /<input[^>]+type="hidden"[^>]+name="([^"]+)"[^>]+value="([^"]*)"/gi;
286
- let match: RegExpExecArray | null;
287
- // biome-ignore lint/suspicious/noAssignInExpressions: valid regex usage
288
- while ((match = regex.exec(html)) !== null) {
289
- if (match[1]) fields[match[1]] = match[2] || "";
290
- }
291
- return fields;
292
- }
293
-
294
- private updateCookies(
295
- jar: Map<
296
- string,
297
- Map<string, { value: string; path: string; expires?: number }>
298
- >,
299
- url: string,
300
- setCookieHeaders: string[],
301
- ) {
302
- const parsedUrl = new URL(url);
303
- const hostname = parsedUrl.hostname;
304
-
305
- for (const header of setCookieHeaders) {
306
- const parts = header.split(";");
307
- const firstPart = parts[0]?.split("=");
308
-
309
- if (firstPart && firstPart.length === 2 && firstPart[0]) {
310
- const name = firstPart[0].trim();
311
- const value = firstPart[1].trim();
312
-
313
- let targetDomain = hostname;
314
- let path = "/";
315
- let isDeletion = value === "";
316
- let expiresAt: number | undefined;
317
-
318
- for (const part of parts.slice(1)) {
319
- const p = part.trim().toLowerCase();
320
- if (p.startsWith("domain=")) {
321
- const d = p.split("=")[1]?.trim();
322
- if (d) targetDomain = d.startsWith(".") ? d : `.${d}`;
323
- } else if (p.startsWith("path=")) {
324
- path = p.split("=")[1]?.trim() || "/";
325
- } else if (p.startsWith("expires=")) {
326
- const exp = p.split("=")[1]?.trim();
327
- if (exp) {
328
- const date = new Date(exp);
329
- if (date.getTime() < Date.now()) isDeletion = true;
330
- expiresAt = date.getTime();
331
- }
332
- } else if (p.startsWith("max-age=")) {
333
- const maxAge = parseInt(p.split("=")[1]?.trim() || "0", 10);
334
- if (maxAge <= 0) isDeletion = true;
335
- else expiresAt = Date.now() + maxAge * 1000;
336
- }
337
- }
338
-
339
- let domainCookies = jar.get(targetDomain);
340
- if (!domainCookies) {
341
- domainCookies = new Map();
342
- jar.set(targetDomain, domainCookies);
343
- }
344
-
345
- if (isDeletion) {
346
- domainCookies.delete(name);
347
- } else {
348
- domainCookies.set(name, { value, path, expires: expiresAt });
349
- }
350
- }
351
- }
352
- }
353
-
354
- private serializeCookies(
355
- jar: Map<
356
- string,
357
- Map<string, { value: string; path: string; expires?: number }>
358
- >,
359
- url: string,
360
- ): string {
361
- const parsedUrl = new URL(url);
362
- const hostname = parsedUrl.hostname;
363
- const path = parsedUrl.pathname;
364
- const cookiesToSet = new Map<string, string>();
365
-
366
- for (const [domain, domainCookies] of jar.entries()) {
367
- // Send if exact host match or if it's a parent domain wildcard
368
- if (
369
- hostname === domain ||
370
- (domain.startsWith(".") && hostname.endsWith(domain))
371
- ) {
372
- for (const [name, cookie] of domainCookies.entries()) {
373
- // Expiry check
374
- if (cookie.expires && cookie.expires < Date.now()) {
375
- domainCookies.delete(name);
376
- continue;
377
- }
378
- // Path match: cookie path must be a prefix of request path
379
- if (path.startsWith(cookie.path)) {
380
- cookiesToSet.set(name, cookie.value);
381
- }
382
- }
383
- }
384
- }
385
-
386
- return Array.from(cookiesToSet.entries())
387
- .map(([name, value]) => `${name}=${value}`)
388
- .join("; ");
389
- }
390
- }