@mo7yw4ng/openape 1.0.0 → 1.0.2

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.
Files changed (133) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +15 -5
  3. package/esm/_dnt.polyfills.d.ts +83 -6
  4. package/esm/_dnt.polyfills.d.ts.map +1 -0
  5. package/esm/_dnt.polyfills.js +127 -1
  6. package/esm/_dnt.shims.d.ts +1 -0
  7. package/esm/_dnt.shims.d.ts.map +1 -0
  8. package/esm/deno.d.ts +2 -0
  9. package/esm/deno.d.ts.map +1 -0
  10. package/esm/deno.js +2 -1
  11. package/esm/src/commands/announcements.d.ts +3 -0
  12. package/esm/src/commands/announcements.d.ts.map +1 -0
  13. package/esm/src/commands/announcements.js +135 -0
  14. package/esm/src/commands/auth.d.ts +3 -0
  15. package/esm/src/commands/auth.d.ts.map +1 -0
  16. package/esm/src/commands/auth.js +264 -0
  17. package/esm/src/commands/calendar.d.ts +3 -0
  18. package/esm/src/commands/calendar.d.ts.map +1 -0
  19. package/esm/src/commands/calendar.js +180 -0
  20. package/esm/src/commands/courses.d.ts +3 -0
  21. package/esm/src/commands/courses.d.ts.map +1 -0
  22. package/esm/src/commands/courses.js +348 -0
  23. package/esm/src/commands/forums.d.ts +3 -0
  24. package/esm/src/commands/forums.d.ts.map +1 -0
  25. package/esm/src/commands/forums.js +231 -0
  26. package/esm/src/commands/grades.d.ts +3 -0
  27. package/esm/src/commands/grades.d.ts.map +1 -0
  28. package/esm/src/commands/grades.js +121 -0
  29. package/esm/src/commands/materials.d.ts +3 -0
  30. package/esm/src/commands/materials.d.ts.map +1 -0
  31. package/esm/src/commands/materials.js +362 -0
  32. package/esm/src/commands/quizzes.d.ts +3 -0
  33. package/esm/src/commands/quizzes.d.ts.map +1 -0
  34. package/esm/src/commands/quizzes.js +160 -0
  35. package/esm/src/commands/skills.d.ts +3 -0
  36. package/esm/src/commands/skills.d.ts.map +1 -0
  37. package/esm/src/commands/skills.js +110 -0
  38. package/esm/src/commands/videos.d.ts +3 -0
  39. package/esm/src/commands/videos.d.ts.map +1 -0
  40. package/esm/src/commands/videos.js +302 -0
  41. package/esm/src/index.d.ts +27 -0
  42. package/esm/src/index.d.ts.map +1 -0
  43. package/esm/src/index.js +149 -0
  44. package/esm/src/lib/auth.d.ts +25 -0
  45. package/esm/src/lib/auth.d.ts.map +1 -0
  46. package/esm/src/lib/auth.js +194 -0
  47. package/esm/src/lib/config.d.ts +6 -0
  48. package/esm/src/lib/config.d.ts.map +1 -0
  49. package/esm/src/lib/config.js +36 -0
  50. package/esm/src/lib/logger.d.ts +3 -0
  51. package/esm/src/lib/logger.d.ts.map +1 -0
  52. package/esm/src/lib/logger.js +24 -0
  53. package/esm/src/lib/moodle.d.ts +205 -0
  54. package/esm/src/lib/moodle.d.ts.map +1 -0
  55. package/esm/src/lib/moodle.js +690 -0
  56. package/esm/src/lib/session.d.ts +8 -0
  57. package/esm/src/lib/session.d.ts.map +1 -0
  58. package/esm/src/lib/session.js +42 -0
  59. package/esm/src/lib/token.d.ts +38 -0
  60. package/esm/src/lib/token.d.ts.map +1 -0
  61. package/esm/src/lib/token.js +178 -0
  62. package/esm/src/lib/types.d.ts +271 -0
  63. package/esm/src/lib/types.d.ts.map +1 -0
  64. package/esm/src/lib/types.js +1 -0
  65. package/esm/src/lib/utils.d.ts +17 -0
  66. package/esm/src/lib/utils.d.ts.map +1 -0
  67. package/esm/src/lib/utils.js +51 -0
  68. package/package.json +7 -3
  69. package/script/_dnt.polyfills.d.ts +83 -6
  70. package/script/_dnt.polyfills.d.ts.map +1 -0
  71. package/script/_dnt.polyfills.js +128 -0
  72. package/script/_dnt.shims.d.ts +1 -0
  73. package/script/_dnt.shims.d.ts.map +1 -0
  74. package/script/deno.d.ts +2 -0
  75. package/script/deno.d.ts.map +1 -0
  76. package/script/deno.js +2 -1
  77. package/script/src/commands/announcements.d.ts +1 -0
  78. package/script/src/commands/announcements.d.ts.map +1 -0
  79. package/script/src/commands/announcements.js +75 -222
  80. package/script/src/commands/auth.d.ts +1 -0
  81. package/script/src/commands/auth.d.ts.map +1 -0
  82. package/script/src/commands/auth.js +49 -17
  83. package/script/src/commands/calendar.d.ts +1 -0
  84. package/script/src/commands/calendar.d.ts.map +1 -0
  85. package/script/src/commands/calendar.js +112 -301
  86. package/script/src/commands/courses.d.ts +1 -0
  87. package/script/src/commands/courses.d.ts.map +1 -0
  88. package/script/src/commands/courses.js +43 -173
  89. package/script/src/commands/forums.d.ts +1 -0
  90. package/script/src/commands/forums.d.ts.map +1 -0
  91. package/script/src/commands/forums.js +145 -311
  92. package/script/src/commands/grades.d.ts +1 -0
  93. package/script/src/commands/grades.d.ts.map +1 -0
  94. package/script/src/commands/grades.js +62 -194
  95. package/script/src/commands/materials.d.ts +1 -0
  96. package/script/src/commands/materials.d.ts.map +1 -0
  97. package/script/src/commands/materials.js +111 -166
  98. package/script/src/commands/quizzes.d.ts +1 -0
  99. package/script/src/commands/quizzes.d.ts.map +1 -0
  100. package/script/src/commands/quizzes.js +40 -102
  101. package/script/src/commands/skills.d.ts +1 -0
  102. package/script/src/commands/skills.d.ts.map +1 -0
  103. package/script/src/commands/skills.js +17 -18
  104. package/script/src/commands/videos.d.ts +1 -0
  105. package/script/src/commands/videos.d.ts.map +1 -0
  106. package/script/src/commands/videos.js +26 -52
  107. package/script/src/index.d.ts +1 -0
  108. package/script/src/index.d.ts.map +1 -0
  109. package/script/src/index.js +4 -4
  110. package/script/src/lib/auth.d.ts +1 -0
  111. package/script/src/lib/auth.d.ts.map +1 -0
  112. package/script/src/lib/auth.js +9 -10
  113. package/script/src/lib/config.d.ts +1 -0
  114. package/script/src/lib/config.d.ts.map +1 -0
  115. package/script/src/lib/config.js +6 -7
  116. package/script/src/lib/logger.d.ts +1 -0
  117. package/script/src/lib/logger.d.ts.map +1 -0
  118. package/script/src/lib/logger.js +1 -2
  119. package/script/src/lib/moodle.d.ts +25 -54
  120. package/script/src/lib/moodle.d.ts.map +1 -0
  121. package/script/src/lib/moodle.js +103 -324
  122. package/script/src/lib/session.d.ts +1 -0
  123. package/script/src/lib/session.d.ts.map +1 -0
  124. package/script/src/lib/session.js +3 -29
  125. package/script/src/lib/token.d.ts +16 -5
  126. package/script/src/lib/token.d.ts.map +1 -0
  127. package/script/src/lib/token.js +71 -36
  128. package/script/src/lib/types.d.ts +10 -0
  129. package/script/src/lib/types.d.ts.map +1 -0
  130. package/script/src/lib/utils.d.ts +12 -0
  131. package/script/src/lib/utils.d.ts.map +1 -0
  132. package/script/src/lib/utils.js +57 -11
  133. package/skills/openape/SKILL.md +331 -328
@@ -15,17 +15,47 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.getMessagesApi = exports.getResourcesByCoursesApi = exports.getQuizzesByCoursesApi = exports.getSupervideosInCourseApi = exports.getSiteInfoApi = exports.completeVideo = exports.downloadVideo = exports.getVideoMetadata = exports.getCalendarEventsApi = exports.getCalendarEvents = exports.getCourseGradesApi = exports.getCourseGrades = exports.getResourcesInCourse = exports.getDiscussionPosts = exports.getForumDiscussions = exports.getForumsByCourseIds = exports.getForumIdFromPage = exports.getForumsApi = exports.getForumsInCourse = exports.getQuizzesInCourse = exports.getSupervideosInCourse = exports.getCourseState = exports.getEnrolledCourses = exports.getEnrolledCoursesApi = exports.moodleAjax = exports.moodleApiCall = void 0;
36
+ exports.moodleApiCall = moodleApiCall;
37
+ exports.moodleAjax = moodleAjax;
38
+ exports.getEnrolledCoursesApi = getEnrolledCoursesApi;
39
+ exports.getEnrolledCourses = getEnrolledCourses;
40
+ exports.getCourseState = getCourseState;
41
+ exports.getSupervideosInCourse = getSupervideosInCourse;
42
+ exports.getForumsApi = getForumsApi;
43
+ exports.getForumDiscussionsApi = getForumDiscussionsApi;
44
+ exports.getDiscussionPostsApi = getDiscussionPostsApi;
45
+ exports.getResourcesInCourse = getResourcesInCourse;
46
+ exports.getCalendarEventsApi = getCalendarEventsApi;
47
+ exports.getCourseGradesApi = getCourseGradesApi;
48
+ exports.getVideoMetadata = getVideoMetadata;
49
+ exports.downloadVideo = downloadVideo;
50
+ exports.completeVideo = completeVideo;
51
+ exports.getSiteInfoApi = getSiteInfoApi;
52
+ exports.getSupervideosInCourseApi = getSupervideosInCourseApi;
53
+ exports.getQuizzesByCoursesApi = getQuizzesByCoursesApi;
54
+ exports.getResourcesByCoursesApi = getResourcesByCoursesApi;
55
+ exports.getMessagesApi = getMessagesApi;
27
56
  const dntShim = __importStar(require("../../_dnt.shims.js"));
28
57
  const node_html_parser_1 = require("node-html-parser");
58
+ const utils_js_1 = require("./utils.js");
29
59
  // ── HTML Parsing Helpers ──────────────────────────────────────────────────
30
60
  /**
31
61
  * Get the HTML content of a page and parse it.
@@ -91,7 +121,6 @@ async function moodleApiCall(session, methodname, args) {
91
121
  }
92
122
  return result;
93
123
  }
94
- exports.moodleApiCall = moodleApiCall;
95
124
  /**
96
125
  * Send a Moodle AJAX request and return the result.
97
126
  * Uses Web Service token if available AND the function is in WS_API_FUNCTIONS,
@@ -135,7 +164,6 @@ async function moodleAjax(page, session, methodname, args) {
135
164
  return result[0].data;
136
165
  }
137
166
  }
138
- exports.moodleAjax = moodleAjax;
139
167
  // ── Course Operations ─────────────────────────────────────────────────────
140
168
  /**
141
169
  * Fetch enrolled courses via pure API (no browser required).
@@ -162,7 +190,7 @@ async function getEnrolledCoursesApi(session, options = {}) {
162
190
  });
163
191
  return (data?.courses ?? []).map((c) => ({
164
192
  id: c.id,
165
- fullname: c.fullname,
193
+ fullname: (0, utils_js_1.extractCourseName)(c.fullname),
166
194
  shortname: c.shortname,
167
195
  idnumber: c.idnumber,
168
196
  category: c.category?.name,
@@ -171,7 +199,6 @@ async function getEnrolledCoursesApi(session, options = {}) {
171
199
  enddate: c.enddate,
172
200
  }));
173
201
  }
174
- exports.getEnrolledCoursesApi = getEnrolledCoursesApi;
175
202
  /**
176
203
  * Fetch all enrolled courses via Moodle AJAX API.
177
204
  */
@@ -196,7 +223,7 @@ async function getEnrolledCourses(page, session, log, options = {}) {
196
223
  });
197
224
  const courses = (data?.courses ?? []).map((c) => ({
198
225
  id: c.id,
199
- fullname: c.fullname,
226
+ fullname: (0, utils_js_1.extractCourseName)(c.fullname),
200
227
  shortname: c.shortname,
201
228
  idnumber: c.idnumber,
202
229
  category: c.category?.name,
@@ -207,7 +234,6 @@ async function getEnrolledCourses(page, session, log, options = {}) {
207
234
  log.debug(`Found ${courses.length} course${courses.length === 1 ? "" : "s"}.`);
208
235
  return courses;
209
236
  }
210
- exports.getEnrolledCourses = getEnrolledCourses;
211
237
  /**
212
238
  * Get course state (modules) via core_courseformat_get_state.
213
239
  */
@@ -217,7 +243,6 @@ async function getCourseState(page, session, courseId) {
217
243
  });
218
244
  return typeof data === "string" ? JSON.parse(data) : data;
219
245
  }
220
- exports.getCourseState = getCourseState;
221
246
  // ── Video Operations ──────────────────────────────────────────────────────
222
247
  /**
223
248
  * Get all SuperVideo modules in a course.
@@ -243,81 +268,7 @@ async function getSupervideosInCourse(page, session, courseId, log, options = {}
243
268
  isComplete: !!cm.isoverallcomplete,
244
269
  }));
245
270
  }
246
- exports.getSupervideosInCourse = getSupervideosInCourse;
247
- // ── Quiz Operations ───────────────────────────────────────────────────────
248
- /**
249
- * Get all Quiz modules in a course.
250
- */
251
- async function getQuizzesInCourse(page, session, courseId, log) {
252
- const state = await getCourseState(page, session, courseId);
253
- const cms = state?.cm ?? [];
254
- const allQuizzes = cms.filter((cm) => cm.module === "quiz");
255
- const available = allQuizzes.filter((cm) => !("isoverallcomplete" in cm) || !cm.isoverallcomplete);
256
- log.debug(` Quiz: ${allQuizzes.length} total, ${available.length} available`);
257
- return available.map((cm) => ({
258
- cmid: cm.cmid?.toString() ?? cm.id?.toString() ?? "",
259
- name: cm.name,
260
- url: cm.url,
261
- isComplete: !!cm.isoverallcomplete,
262
- timeOpen: cm.timeopen,
263
- timeClose: cm.timeclose,
264
- }));
265
- }
266
- exports.getQuizzesInCourse = getQuizzesInCourse;
267
271
  // ── Forum Operations ──────────────────────────────────────────────────────
268
- /**
269
- * Get all forum modules in a course.
270
- * If WS token is available, fetches forum IDs directly via WS API.
271
- */
272
- async function getForumsInCourse(page, session, courseId, log) {
273
- // First get basic forum info from course state
274
- const state = await getCourseState(page, session, courseId);
275
- const cms = state?.cm ?? [];
276
- const forums = cms.filter((cm) => cm.module === "forum");
277
- log.debug(` Found ${forums.length} forum${forums.length === 1 ? "" : "s"}.`);
278
- const result = forums.map((cm) => ({
279
- cmid: cm.cmid?.toString() ?? cm.id?.toString() ?? "",
280
- forumId: 0,
281
- name: cm.name,
282
- url: cm.url,
283
- courseId,
284
- forumType: cm.modname,
285
- }));
286
- // If WS token is available, fetch forum IDs directly
287
- if (session.wsToken && forums.length > 0) {
288
- try {
289
- const wsForums = await moodleAjax(page, session, "mod_forum_get_forums_by_courses", { courseids: [courseId] });
290
- // Create maps for lookup by different fields
291
- const byId = new Map(); // cmid -> forum id
292
- const byName = new Map(); // name -> forum id
293
- for (const wsForum of wsForums || []) {
294
- if (wsForum.cmid) {
295
- byId.set(wsForum.cmid, wsForum.id);
296
- }
297
- if (wsForum.name) {
298
- byName.set(wsForum.name, wsForum.id);
299
- }
300
- }
301
- // Merge forum IDs into result
302
- for (const forum of result) {
303
- const cmid = parseInt(forum.cmid, 10);
304
- if (byId.has(cmid)) {
305
- forum.forumId = byId.get(cmid);
306
- }
307
- else if (byName.has(forum.name)) {
308
- forum.forumId = byName.get(forum.name);
309
- }
310
- }
311
- const matchedCount = result.filter(f => f.forumId > 0).length;
312
- log.debug(` WS API provided forum IDs for ${matchedCount}/${result.length} forums.`);
313
- }
314
- catch (e) {
315
- log.debug(` WS API forum lookup failed: ${e instanceof Error ? e.message : String(e)}`);
316
- }
317
- }
318
- return result;
319
- }
320
- exports.getForumsInCourse = getForumsInCourse;
321
272
  /**
322
273
  * Get all forums via pure WS API (no browser required).
323
274
  * Fast and lightweight - uses HTTP fetch directly.
@@ -331,142 +282,49 @@ async function getForumsApi(session, courseIds) {
331
282
  courseid: f.course, // API returns 'course' not 'courseid'
332
283
  }));
333
284
  }
334
- exports.getForumsApi = getForumsApi;
335
285
  /**
336
- * Extract forum ID from forum page.
337
- * First tries to find it in embedded page data, then falls back to
338
- * extracting it from discussion posts API.
286
+ * Get discussions in a forum via WS API (no browser required).
287
+ * Uses mod_forum_get_forum_discussions
339
288
  */
340
- async function getForumIdFromPage(page, cmid, session) {
341
- try {
342
- await page.goto(`https://ilearning.cycu.edu.tw/mod/forum/view.php?id=${cmid}`, { waitUntil: "domcontentloaded", timeout: 30000 });
343
- // First try: extract from page HTML
344
- const forumId = await page.evaluate(() => {
345
- // Try multiple patterns to find the forum ID
346
- const patterns = [
347
- /"forumid":(\d+)/,
348
- /"forumId":(\d+)/,
349
- /forumid=(\d+)/,
350
- /data-forum-id="(\d+)"/,
351
- ];
352
- const html = document.body.innerHTML;
353
- for (const pattern of patterns) {
354
- const match = html.match(pattern);
355
- if (match)
356
- return parseInt(match[1], 10);
357
- }
358
- // Try to find it in a script tag with forum configuration
359
- const scripts = Array.from(document.querySelectorAll('script'));
360
- for (const script of scripts) {
361
- const text = script.textContent || '';
362
- const match = text.match(/"forumid":(\d+)/);
363
- if (match)
364
- return parseInt(match[1], 10);
365
- }
366
- // Try to find from discussion links - extract from API data embedded in page
367
- const discussLinks = Array.from(document.querySelectorAll('a[href*="discuss.php"]'));
368
- for (const link of discussLinks) {
369
- const href = link.href;
370
- // The discussion page might have forum info
371
- const dMatch = href.match(/d=(\d+)/);
372
- if (dMatch) {
373
- // Try to find parent element with forum data
374
- let parent = link.parentElement;
375
- let depth = 0;
376
- while (parent && depth < 10) {
377
- const parentHtml = parent.innerHTML;
378
- const fMatch = parentHtml.match(/"forum":(\d+)/);
379
- if (fMatch)
380
- return parseInt(fMatch[1], 10);
381
- parent = parent.parentElement;
382
- depth++;
383
- }
384
- }
385
- }
386
- return null;
387
- });
388
- if (forumId)
389
- return forumId;
390
- // Fallback: if session is provided, try to get instance ID from discussion posts
391
- if (session) {
392
- // Get first discussion ID from page
393
- const firstDiscussionId = await page.evaluate(() => {
394
- const link = document.querySelector('a[href*="discuss.php"]');
395
- if (!link)
396
- return null;
397
- const href = link.href;
398
- const match = href.match(/d=(\d+)/);
399
- return match ? parseInt(match[1], 10) : null;
400
- });
401
- if (firstDiscussionId) {
402
- // Try to get posts and extract forum ID from response
403
- const data = await moodleAjax(page, session, "mod_forum_get_forum_discussion_posts", {
404
- discussionid: firstDiscussionId,
405
- });
406
- if (data?.posts && data.posts.length > 0) {
407
- const firstPost = data.posts[0];
408
- if (firstPost.forum) {
409
- return firstPost.forum;
410
- }
411
- }
412
- }
413
- }
414
- return null;
415
- }
416
- catch {
417
- return null;
418
- }
419
- }
420
- exports.getForumIdFromPage = getForumIdFromPage;
421
- /**
422
- * Get forums by course IDs via AJAX.
423
- * Returns forum instance IDs directly from Moodle API.
424
- * This is the cleanest way to get forum instance IDs.
425
- */
426
- async function getForumsByCourseIds(page, session, courseIds) {
427
- if (courseIds.length === 0)
428
- return [];
429
- try {
430
- const data = await moodleAjax(page, session, "mod_forum_get_forums_by_courses", {
431
- courseids: courseIds,
432
- });
433
- return data ?? [];
434
- }
435
- catch (e) {
436
- // Re-throw with more context
437
- throw new Error(`mod_forum_get_forums_by_courses failed: ${e?.message || e}`);
438
- }
439
- }
440
- exports.getForumsByCourseIds = getForumsByCourseIds;
441
- /**
442
- * Get discussions in a forum via AJAX.
443
- * Note: Requires forum instance ID, not cmid. Use getForumsByCourseIds() first.
444
- */
445
- async function getForumDiscussions(page, session, forumId) {
446
- const data = await moodleAjax(page, session, "mod_forum_get_forum_discussions", {
447
- forumid: forumId,
448
- });
289
+ async function getForumDiscussionsApi(session, forumId, options) {
290
+ const params = { forumid: forumId, sortorder: options?.sortorder ?? 2 };
291
+ if (options?.page !== undefined)
292
+ params.page = options.page;
293
+ if (options?.perpage !== undefined)
294
+ params.perpage = options.perpage;
295
+ if (options?.groupid !== undefined)
296
+ params.groupid = options.groupid;
297
+ const data = await moodleApiCall(session, "mod_forum_get_forum_discussions", params);
449
298
  return (data?.discussions ?? []).map((d) => ({
450
- id: d.id,
299
+ id: d.discussion,
451
300
  forumId: d.forum,
452
301
  name: d.name,
453
302
  firstPostId: d.firstpost,
454
303
  userId: d.userid,
304
+ userFullName: d.userfullname || "",
455
305
  groupId: d.groupid,
456
306
  timedue: d.timedue,
457
307
  timeModified: d.timemodified,
308
+ timeStart: d.timestart,
309
+ timeEnd: d.timeend,
458
310
  userModified: d.usermodified,
459
- postCount: d.numdiscussion,
460
- unread: d.unread,
311
+ userModifiedFullName: d.usermodifiedfullname,
312
+ postCount: d.numreplies,
313
+ unread: (d.numunread ?? 0) > 0,
314
+ subject: (0, utils_js_1.stripHtmlTags)(d.subject ?? ""),
315
+ message: d.message,
316
+ pinned: d.pinned,
317
+ locked: d.locked,
318
+ starred: d.starred,
461
319
  }));
462
320
  }
463
- exports.getForumDiscussions = getForumDiscussions;
464
321
  /**
465
- * Get posts in a discussion via AJAX.
322
+ * Get posts in a discussion via WS API (no browser required).
323
+ * Uses mod_forum_get_forum_discussion_posts
466
324
  */
467
- async function getDiscussionPosts(page, session, discussionId) {
325
+ async function getDiscussionPostsApi(session, discussionId) {
468
326
  try {
469
- const data = await moodleAjax(page, session, "mod_forum_get_forum_discussion_posts", {
327
+ const data = await moodleApiCall(session, "mod_forum_get_discussion_posts", {
470
328
  discussionid: discussionId,
471
329
  });
472
330
  if (!data?.posts || data.posts.length === 0) {
@@ -474,13 +332,13 @@ async function getDiscussionPosts(page, session, discussionId) {
474
332
  }
475
333
  return data.posts.map((p) => ({
476
334
  id: p.id,
477
- subject: p.subject || "",
478
- author: p.author?.fullname ?? p.username ?? "Unknown",
479
- authorId: p.userid,
480
- created: p.created,
481
- modified: p.modified,
482
- message: p.message || "",
483
- discussionId: p.discussion,
335
+ subject: (0, utils_js_1.stripHtmlTags)(p.subject || ""),
336
+ author: p.author?.fullname ?? "Unknown",
337
+ authorId: p.author?.id ?? p.userid,
338
+ created: p.timecreated,
339
+ modified: p.timemodified,
340
+ message: (0, utils_js_1.stripHtmlTags)(p.message || ""),
341
+ discussionId: p.discussionid,
484
342
  unread: p.unread ?? false,
485
343
  }));
486
344
  }
@@ -490,7 +348,6 @@ async function getDiscussionPosts(page, session, discussionId) {
490
348
  return [];
491
349
  }
492
350
  }
493
- exports.getDiscussionPosts = getDiscussionPosts;
494
351
  // ── Resource/Material Operations ──────────────────────────────────────────
495
352
  /**
496
353
  * Get all resource modules in a course.
@@ -511,79 +368,13 @@ async function getResourcesInCourse(page, session, courseId, log) {
511
368
  modified: 0,
512
369
  }));
513
370
  }
514
- exports.getResourcesInCourse = getResourcesInCourse;
515
- // ── Grade Operations ──────────────────────────────────────────────────────
516
- /**
517
- * Get course grades for the current user via AJAX.
518
- */
519
- async function getCourseGrades(page, session, courseId) {
520
- const data = await moodleAjax(page, session, "gradereport_user_get_grade_items", {
521
- courseid: courseId,
522
- });
523
- const userGrades = data?.usergrades?.[0];
524
- if (!userGrades) {
525
- return { courseId, courseName: "", items: [] };
526
- }
527
- return {
528
- courseId,
529
- courseName: userGrades.coursefullname ?? "",
530
- grade: userGrades.grade,
531
- gradeFormatted: userGrades.gradeformatted,
532
- rank: userGrades.rank,
533
- totalUsers: userGrades.totalusers,
534
- items: (userGrades.gradeitems ?? []).map((item) => ({
535
- id: item.id,
536
- name: item.itemname || item.itemmodule,
537
- grade: item.grade,
538
- gradeFormatted: item.gradeformatted,
539
- range: item.graderangeformatted,
540
- percentage: item.percentage,
541
- weight: item.weight,
542
- feedback: item.feedback,
543
- graded: !!item.graded,
544
- })),
545
- };
546
- }
547
- exports.getCourseGrades = getCourseGrades;
371
+ // ── Calendar Operations ─────────────────────────────────────────────────────
548
372
  /**
549
- * Get course grades for the current user via pure WS API (no browser required).
373
+ * Get calendar events via pure WS API (no browser required).
550
374
  * Fast and lightweight - uses HTTP fetch directly.
551
375
  */
552
- async function getCourseGradesApi(session, courseId) {
553
- const data = await moodleApiCall(session, "gradereport_user_get_grade_items", {
554
- courseid: courseId,
555
- });
556
- const userGrades = data?.usergrades?.[0];
557
- if (!userGrades) {
558
- return { courseId, courseName: "", items: [] };
559
- }
560
- return {
561
- courseId,
562
- courseName: userGrades.coursefullname ?? "",
563
- grade: userGrades.grade,
564
- gradeFormatted: userGrades.gradeformatted,
565
- rank: userGrades.rank,
566
- totalUsers: userGrades.totalusers,
567
- items: (userGrades.gradeitems ?? []).map((item) => ({
568
- id: item.id,
569
- name: item.itemname || item.itemmodule,
570
- grade: item.grade,
571
- gradeFormatted: item.gradeformatted,
572
- range: item.graderangeformatted,
573
- percentage: item.percentage,
574
- weight: item.weight,
575
- feedback: item.feedback,
576
- graded: !!item.graded,
577
- })),
578
- };
579
- }
580
- exports.getCourseGradesApi = getCourseGradesApi;
581
- // ── Calendar Operations ───────────────────────────────────────────────────
582
- /**
583
- * Get calendar events via AJAX.
584
- */
585
- async function getCalendarEvents(page, session, options = {}) {
586
- const data = await moodleAjax(page, session, "core_calendar_get_calendar_events", {
376
+ async function getCalendarEventsApi(session, options = {}) {
377
+ const data = await moodleApiCall(session, "core_calendar_get_calendar_events", {
587
378
  ...options,
588
379
  });
589
380
  return (data?.events ?? []).map((e) => ({
@@ -599,43 +390,39 @@ async function getCalendarEvents(page, session, options = {}) {
599
390
  modulename: e.modulename,
600
391
  instance: e.instance,
601
392
  eventtype: e.eventtype,
602
- timestart: e.timestart * 1000,
393
+ timestart: e.timestart * 1000, // Convert to milliseconds
603
394
  timeduration: e.timeduration ? e.timeduration * 1000 : undefined,
604
395
  timedue: e.timedue ? e.timedue * 1000 : undefined,
605
396
  visible: e.visible,
606
397
  location: e.location,
607
398
  }));
608
399
  }
609
- exports.getCalendarEvents = getCalendarEvents;
400
+ // ── Grade Operations ──────────────────────────────────────────────────────
610
401
  /**
611
- * Get calendar events via pure WS API (no browser required).
402
+ * Get course grades for the current user via pure WS API (no browser required).
612
403
  * Fast and lightweight - uses HTTP fetch directly.
613
404
  */
614
- async function getCalendarEventsApi(session, options = {}) {
615
- const data = await moodleApiCall(session, "core_calendar_get_calendar_events", {
616
- ...options,
617
- });
618
- return (data?.events ?? []).map((e) => ({
619
- id: e.id,
620
- name: e.name,
621
- description: e.description,
622
- format: e.format,
623
- courseid: e.courseid,
624
- categoryid: e.categoryid,
625
- groupid: e.groupid,
626
- userid: e.userid,
627
- moduleid: e.moduleid,
628
- modulename: e.modulename,
629
- instance: e.instance,
630
- eventtype: e.eventtype,
631
- timestart: e.timestart * 1000,
632
- timeduration: e.timeduration ? e.timeduration * 1000 : undefined,
633
- timedue: e.timedue ? e.timedue * 1000 : undefined,
634
- visible: e.visible,
635
- location: e.location,
636
- }));
405
+ async function getCourseGradesApi(session, courseId) {
406
+ const data = await moodleApiCall(session, "gradereport_user_get_grade_items", { courseid: courseId });
407
+ // The API returns grade items for the course
408
+ const gradeItems = (data?.usergrades ?? []);
409
+ // Return a single CourseGrade object with items array
410
+ return {
411
+ courseId,
412
+ courseName: gradeItems[0]?.coursefullname ?? "",
413
+ grade: gradeItems[0]?.grade,
414
+ gradeFormatted: gradeItems[0]?.gradeformatted,
415
+ rank: gradeItems[0]?.rank,
416
+ totalUsers: gradeItems[0]?.totalusers,
417
+ items: gradeItems.map((g) => ({
418
+ id: g.id,
419
+ name: g.itemname || g.itemtype,
420
+ grade: g.grade,
421
+ gradeFormatted: g.gradeformatted,
422
+ range: g.grade ? `${g.grademin ?? 0}-${g.grademax ?? 100}` : undefined,
423
+ })),
424
+ };
637
425
  }
638
- exports.getCalendarEventsApi = getCalendarEventsApi;
639
426
  // ── Video Metadata (from original course.ts) ───────────────────────────────
640
427
  /**
641
428
  * Visit a SuperVideo activity page and extract view_id + duration.
@@ -754,7 +541,6 @@ async function getVideoMetadata(page, activityUrl, log) {
754
541
  youtubeIds,
755
542
  };
756
543
  }
757
- exports.getVideoMetadata = getVideoMetadata;
758
544
  // ── Video Download ─────────────────────────────────────────────────────────────
759
545
  /**
760
546
  * Download a video from SuperVideo activity.
@@ -821,7 +607,6 @@ async function downloadVideo(page, metadata, outputPath, log) {
821
607
  error: "未找到影片來源",
822
608
  };
823
609
  }
824
- exports.downloadVideo = downloadVideo;
825
610
  // ── Progress Completion (from original progress.ts) ───────────────────────
826
611
  /**
827
612
  * Complete a video by forging progress AJAX call.
@@ -862,7 +647,6 @@ async function completeVideo(page, session, video, log) {
862
647
  return false;
863
648
  }
864
649
  }
865
- exports.completeVideo = completeVideo;
866
650
  // ── Site Info (Get User ID) ───────────────────────────────────────────────────
867
651
  /**
868
652
  * Get site info including current user ID via pure WS API.
@@ -876,7 +660,6 @@ async function getSiteInfoApi(session) {
876
660
  sitename: data.sitename,
877
661
  };
878
662
  }
879
- exports.getSiteInfoApi = getSiteInfoApi;
880
663
  // ── Videos via WS API ─────────────────────────────────────────────────────────
881
664
  /**
882
665
  * Get course contents and filter for SuperVideo modules via pure WS API.
@@ -903,7 +686,6 @@ async function getSupervideosInCourseApi(session, courseId) {
903
686
  }
904
687
  return videos;
905
688
  }
906
- exports.getSupervideosInCourseApi = getSupervideosInCourseApi;
907
689
  /**
908
690
  * Get quizzes in courses via pure WS API.
909
691
  */
@@ -915,13 +697,12 @@ async function getQuizzesByCoursesApi(session, courseIds) {
915
697
  cmid: q.coursemodule.toString(),
916
698
  name: q.name,
917
699
  url: q.viewurl,
918
- isComplete: false,
700
+ isComplete: false, // API doesn't provide completion status
919
701
  timeOpen: q.timeopen,
920
702
  timeClose: q.timeclose,
921
703
  courseId: q.course,
922
704
  }));
923
705
  }
924
- exports.getQuizzesByCoursesApi = getQuizzesByCoursesApi;
925
706
  // ── Materials via WS API ──────────────────────────────────────────────────────
926
707
  /**
927
708
  * Get resources in courses via pure WS API.
@@ -938,14 +719,13 @@ async function getResourcesByCoursesApi(session, courseIds) {
938
719
  name: r.name,
939
720
  url: firstFile?.fileurl ?? "",
940
721
  courseId: r.course,
941
- modType: "resource",
722
+ modType: "resource", // This API only returns resources
942
723
  mimetype: firstFile?.mimetype,
943
724
  filesize: firstFile?.filesize,
944
725
  modified: r.timemodified,
945
726
  };
946
727
  });
947
728
  }
948
- exports.getResourcesByCoursesApi = getResourcesByCoursesApi;
949
729
  /**
950
730
  * Get messages for the current user via pure WS API.
951
731
  */
@@ -963,4 +743,3 @@ async function getMessagesApi(session, userIdTo, options = {}) {
963
743
  fullmessagehtml: m.fullmessagehtml,
964
744
  }));
965
745
  }
966
- exports.getMessagesApi = getMessagesApi;
@@ -5,3 +5,4 @@ import type { AppConfig, Logger, SessionInfo } from "./types.js";
5
5
  * The sesskey is required for all AJAX calls.
6
6
  */
7
7
  export declare function extractSessionInfo(page: Page, config: AppConfig, log: Logger, wsToken?: string): Promise<SessionInfo>;
8
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA6CtB"}
@@ -1,30 +1,6 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.extractSessionInfo = void 0;
27
- const dntShim = __importStar(require("../../_dnt.shims.js"));
3
+ exports.extractSessionInfo = extractSessionInfo;
28
4
  /**
29
5
  * Extract Moodle sesskey from the current page.
30
6
  * The sesskey is required for all AJAX calls.
@@ -38,9 +14,8 @@ async function extractSessionInfo(page, config, log, wsToken) {
38
14
  });
39
15
  }
40
16
  // Try extracting sesskey from M.cfg (Moodle's JS config object)
41
- let sesskey = await page.evaluate(() => {
42
- return dntShim.dntGlobalThis.M?.cfg?.sesskey ?? null;
43
- });
17
+ // Use string to avoid dnt transforming globalThis/window to dntShim
18
+ let sesskey = await page.evaluate("() => self.M?.cfg?.sesskey ?? null");
44
19
  // Fallback: extract from a hidden input
45
20
  if (!sesskey) {
46
21
  sesskey = await page.evaluate(() => {
@@ -68,4 +43,3 @@ async function extractSessionInfo(page, config, log, wsToken) {
68
43
  }
69
44
  return sessionInfo;
70
45
  }
71
- exports.extractSessionInfo = extractSessionInfo;