@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.
- package/LICENSE +21 -21
- package/README.md +15 -5
- package/esm/_dnt.polyfills.d.ts +83 -6
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +127 -1
- package/esm/_dnt.shims.d.ts +1 -0
- package/esm/_dnt.shims.d.ts.map +1 -0
- package/esm/deno.d.ts +2 -0
- package/esm/deno.d.ts.map +1 -0
- package/esm/deno.js +2 -1
- package/esm/src/commands/announcements.d.ts +3 -0
- package/esm/src/commands/announcements.d.ts.map +1 -0
- package/esm/src/commands/announcements.js +135 -0
- package/esm/src/commands/auth.d.ts +3 -0
- package/esm/src/commands/auth.d.ts.map +1 -0
- package/esm/src/commands/auth.js +264 -0
- package/esm/src/commands/calendar.d.ts +3 -0
- package/esm/src/commands/calendar.d.ts.map +1 -0
- package/esm/src/commands/calendar.js +180 -0
- package/esm/src/commands/courses.d.ts +3 -0
- package/esm/src/commands/courses.d.ts.map +1 -0
- package/esm/src/commands/courses.js +348 -0
- package/esm/src/commands/forums.d.ts +3 -0
- package/esm/src/commands/forums.d.ts.map +1 -0
- package/esm/src/commands/forums.js +231 -0
- package/esm/src/commands/grades.d.ts +3 -0
- package/esm/src/commands/grades.d.ts.map +1 -0
- package/esm/src/commands/grades.js +121 -0
- package/esm/src/commands/materials.d.ts +3 -0
- package/esm/src/commands/materials.d.ts.map +1 -0
- package/esm/src/commands/materials.js +362 -0
- package/esm/src/commands/quizzes.d.ts +3 -0
- package/esm/src/commands/quizzes.d.ts.map +1 -0
- package/esm/src/commands/quizzes.js +160 -0
- package/esm/src/commands/skills.d.ts +3 -0
- package/esm/src/commands/skills.d.ts.map +1 -0
- package/esm/src/commands/skills.js +110 -0
- package/esm/src/commands/videos.d.ts +3 -0
- package/esm/src/commands/videos.d.ts.map +1 -0
- package/esm/src/commands/videos.js +302 -0
- package/esm/src/index.d.ts +27 -0
- package/esm/src/index.d.ts.map +1 -0
- package/esm/src/index.js +149 -0
- package/esm/src/lib/auth.d.ts +25 -0
- package/esm/src/lib/auth.d.ts.map +1 -0
- package/esm/src/lib/auth.js +194 -0
- package/esm/src/lib/config.d.ts +6 -0
- package/esm/src/lib/config.d.ts.map +1 -0
- package/esm/src/lib/config.js +36 -0
- package/esm/src/lib/logger.d.ts +3 -0
- package/esm/src/lib/logger.d.ts.map +1 -0
- package/esm/src/lib/logger.js +24 -0
- package/esm/src/lib/moodle.d.ts +205 -0
- package/esm/src/lib/moodle.d.ts.map +1 -0
- package/esm/src/lib/moodle.js +690 -0
- package/esm/src/lib/session.d.ts +8 -0
- package/esm/src/lib/session.d.ts.map +1 -0
- package/esm/src/lib/session.js +42 -0
- package/esm/src/lib/token.d.ts +38 -0
- package/esm/src/lib/token.d.ts.map +1 -0
- package/esm/src/lib/token.js +178 -0
- package/esm/src/lib/types.d.ts +271 -0
- package/esm/src/lib/types.d.ts.map +1 -0
- package/esm/src/lib/types.js +1 -0
- package/esm/src/lib/utils.d.ts +17 -0
- package/esm/src/lib/utils.d.ts.map +1 -0
- package/esm/src/lib/utils.js +51 -0
- package/package.json +7 -3
- package/script/_dnt.polyfills.d.ts +83 -6
- package/script/_dnt.polyfills.d.ts.map +1 -0
- package/script/_dnt.polyfills.js +128 -0
- package/script/_dnt.shims.d.ts +1 -0
- package/script/_dnt.shims.d.ts.map +1 -0
- package/script/deno.d.ts +2 -0
- package/script/deno.d.ts.map +1 -0
- package/script/deno.js +2 -1
- package/script/src/commands/announcements.d.ts +1 -0
- package/script/src/commands/announcements.d.ts.map +1 -0
- package/script/src/commands/announcements.js +75 -222
- package/script/src/commands/auth.d.ts +1 -0
- package/script/src/commands/auth.d.ts.map +1 -0
- package/script/src/commands/auth.js +49 -17
- package/script/src/commands/calendar.d.ts +1 -0
- package/script/src/commands/calendar.d.ts.map +1 -0
- package/script/src/commands/calendar.js +112 -301
- package/script/src/commands/courses.d.ts +1 -0
- package/script/src/commands/courses.d.ts.map +1 -0
- package/script/src/commands/courses.js +43 -173
- package/script/src/commands/forums.d.ts +1 -0
- package/script/src/commands/forums.d.ts.map +1 -0
- package/script/src/commands/forums.js +145 -311
- package/script/src/commands/grades.d.ts +1 -0
- package/script/src/commands/grades.d.ts.map +1 -0
- package/script/src/commands/grades.js +62 -194
- package/script/src/commands/materials.d.ts +1 -0
- package/script/src/commands/materials.d.ts.map +1 -0
- package/script/src/commands/materials.js +111 -166
- package/script/src/commands/quizzes.d.ts +1 -0
- package/script/src/commands/quizzes.d.ts.map +1 -0
- package/script/src/commands/quizzes.js +40 -102
- package/script/src/commands/skills.d.ts +1 -0
- package/script/src/commands/skills.d.ts.map +1 -0
- package/script/src/commands/skills.js +17 -18
- package/script/src/commands/videos.d.ts +1 -0
- package/script/src/commands/videos.d.ts.map +1 -0
- package/script/src/commands/videos.js +26 -52
- package/script/src/index.d.ts +1 -0
- package/script/src/index.d.ts.map +1 -0
- package/script/src/index.js +4 -4
- package/script/src/lib/auth.d.ts +1 -0
- package/script/src/lib/auth.d.ts.map +1 -0
- package/script/src/lib/auth.js +9 -10
- package/script/src/lib/config.d.ts +1 -0
- package/script/src/lib/config.d.ts.map +1 -0
- package/script/src/lib/config.js +6 -7
- package/script/src/lib/logger.d.ts +1 -0
- package/script/src/lib/logger.d.ts.map +1 -0
- package/script/src/lib/logger.js +1 -2
- package/script/src/lib/moodle.d.ts +25 -54
- package/script/src/lib/moodle.d.ts.map +1 -0
- package/script/src/lib/moodle.js +103 -324
- package/script/src/lib/session.d.ts +1 -0
- package/script/src/lib/session.d.ts.map +1 -0
- package/script/src/lib/session.js +3 -29
- package/script/src/lib/token.d.ts +16 -5
- package/script/src/lib/token.d.ts.map +1 -0
- package/script/src/lib/token.js +71 -36
- package/script/src/lib/types.d.ts +10 -0
- package/script/src/lib/types.d.ts.map +1 -0
- package/script/src/lib/utils.d.ts +12 -0
- package/script/src/lib/utils.d.ts.map +1 -0
- package/script/src/lib/utils.js +57 -11
- package/skills/openape/SKILL.md +331 -328
package/script/src/lib/moodle.js
CHANGED
|
@@ -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 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
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
|
-
*
|
|
337
|
-
*
|
|
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
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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.
|
|
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
|
-
|
|
460
|
-
|
|
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
|
|
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
|
|
325
|
+
async function getDiscussionPostsApi(session, discussionId) {
|
|
468
326
|
try {
|
|
469
|
-
const data = await
|
|
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 ??
|
|
479
|
-
authorId: p.userid,
|
|
480
|
-
created: p.
|
|
481
|
-
modified: p.
|
|
482
|
-
message: p.message || "",
|
|
483
|
-
discussionId: p.
|
|
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
|
-
|
|
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
|
|
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
|
|
553
|
-
const data = await moodleApiCall(session, "
|
|
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
|
-
|
|
400
|
+
// ── Grade Operations ──────────────────────────────────────────────────────
|
|
610
401
|
/**
|
|
611
|
-
* Get
|
|
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
|
|
615
|
-
const data = await moodleApiCall(session, "
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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 =
|
|
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
|
-
|
|
42
|
-
|
|
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;
|