@mo7yw4ng/openape 1.0.3 → 1.0.5
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/README.md +30 -5
- package/esm/deno.js +1 -1
- package/esm/src/commands/announcements.d.ts.map +1 -1
- package/esm/src/commands/announcements.js +16 -17
- package/esm/src/commands/assignments.d.ts +3 -0
- package/esm/src/commands/assignments.d.ts.map +1 -0
- package/esm/src/commands/assignments.js +230 -0
- package/esm/src/commands/auth.d.ts.map +1 -1
- package/esm/src/commands/auth.js +45 -15
- package/esm/src/commands/calendar.d.ts.map +1 -1
- package/esm/src/commands/calendar.js +20 -21
- package/esm/src/commands/courses.js +6 -6
- package/esm/src/commands/forums.d.ts.map +1 -1
- package/esm/src/commands/forums.js +128 -36
- package/esm/src/commands/grades.js +3 -3
- package/esm/src/commands/materials.d.ts.map +1 -1
- package/esm/src/commands/materials.js +115 -224
- package/esm/src/commands/quizzes.d.ts.map +1 -1
- package/esm/src/commands/quizzes.js +179 -68
- package/esm/src/commands/skills.d.ts.map +1 -1
- package/esm/src/commands/skills.js +4 -8
- package/esm/src/commands/upload.d.ts +3 -0
- package/esm/src/commands/upload.d.ts.map +1 -0
- package/esm/src/commands/upload.js +58 -0
- package/esm/src/commands/videos.d.ts.map +1 -1
- package/esm/src/commands/videos.js +10 -9
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +12 -1
- package/esm/src/lib/auth.d.ts +23 -1
- package/esm/src/lib/auth.d.ts.map +1 -1
- package/esm/src/lib/auth.js +36 -3
- package/esm/src/lib/logger.d.ts +1 -1
- package/esm/src/lib/logger.d.ts.map +1 -1
- package/esm/src/lib/logger.js +7 -4
- package/esm/src/lib/moodle.d.ts +183 -1
- package/esm/src/lib/moodle.d.ts.map +1 -1
- package/esm/src/lib/moodle.js +498 -13
- package/esm/src/lib/types.d.ts +81 -164
- package/esm/src/lib/types.d.ts.map +1 -1
- package/esm/src/lib/types.js +1 -0
- package/esm/src/lib/utils.d.ts +20 -0
- package/esm/src/lib/utils.d.ts.map +1 -1
- package/esm/src/lib/utils.js +48 -1
- package/package.json +1 -1
- package/script/deno.js +1 -1
- package/script/src/commands/announcements.d.ts.map +1 -1
- package/script/src/commands/announcements.js +15 -16
- package/script/src/commands/assignments.d.ts +3 -0
- package/script/src/commands/assignments.d.ts.map +1 -0
- package/script/src/commands/assignments.js +269 -0
- package/script/src/commands/auth.d.ts.map +1 -1
- package/script/src/commands/auth.js +44 -14
- package/script/src/commands/calendar.d.ts.map +1 -1
- package/script/src/commands/calendar.js +19 -20
- package/script/src/commands/courses.js +5 -5
- package/script/src/commands/forums.d.ts.map +1 -1
- package/script/src/commands/forums.js +128 -36
- package/script/src/commands/grades.js +3 -3
- package/script/src/commands/materials.d.ts.map +1 -1
- package/script/src/commands/materials.js +115 -224
- package/script/src/commands/quizzes.d.ts.map +1 -1
- package/script/src/commands/quizzes.js +177 -66
- package/script/src/commands/skills.d.ts.map +1 -1
- package/script/src/commands/skills.js +4 -8
- package/script/src/commands/upload.d.ts +3 -0
- package/script/src/commands/upload.d.ts.map +1 -0
- package/script/src/commands/upload.js +64 -0
- package/script/src/commands/videos.d.ts.map +1 -1
- package/script/src/commands/videos.js +10 -9
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +12 -1
- package/script/src/lib/auth.d.ts +23 -1
- package/script/src/lib/auth.d.ts.map +1 -1
- package/script/src/lib/auth.js +70 -3
- package/script/src/lib/logger.d.ts +1 -1
- package/script/src/lib/logger.d.ts.map +1 -1
- package/script/src/lib/logger.js +7 -4
- package/script/src/lib/moodle.d.ts +183 -1
- package/script/src/lib/moodle.d.ts.map +1 -1
- package/script/src/lib/moodle.js +511 -13
- package/script/src/lib/types.d.ts +81 -164
- package/script/src/lib/types.d.ts.map +1 -1
- package/script/src/lib/types.js +1 -0
- package/script/src/lib/utils.d.ts +20 -0
- package/script/src/lib/utils.d.ts.map +1 -1
- package/script/src/lib/utils.js +52 -0
- package/skills/openape/SKILL.md +74 -270
|
@@ -13,28 +13,24 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
13
13
|
function registerForumsCommand(program) {
|
|
14
14
|
const forumsCmd = program.command("forums");
|
|
15
15
|
forumsCmd.description("Forum operations");
|
|
16
|
-
function getOutputFormat(command) {
|
|
17
|
-
const opts = command.optsWithGlobals();
|
|
18
|
-
return opts.output || "json";
|
|
19
|
-
}
|
|
20
16
|
// Pure API context - no browser required (fast!)
|
|
21
17
|
async function createApiContext(options, command) {
|
|
22
18
|
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
23
|
-
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
19
|
+
const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
24
20
|
const silent = outputFormat === "json" && !opts.verbose;
|
|
25
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
21
|
+
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
26
22
|
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
27
23
|
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
28
24
|
// Check if session exists
|
|
29
25
|
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
30
|
-
|
|
26
|
+
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
31
27
|
log.info(`Session 預期位置: ${sessionPath}`);
|
|
32
28
|
return null;
|
|
33
29
|
}
|
|
34
30
|
// Try to load WS token
|
|
35
31
|
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
36
32
|
if (!wsToken) {
|
|
37
|
-
|
|
33
|
+
console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
38
34
|
return null;
|
|
39
35
|
}
|
|
40
36
|
// Try to load sesskey from cache
|
|
@@ -71,6 +67,7 @@ function registerForumsCommand(program) {
|
|
|
71
67
|
allForums.push({
|
|
72
68
|
course_id: wsForum.courseid,
|
|
73
69
|
course_name: course.fullname,
|
|
70
|
+
intro: wsForum.intro,
|
|
74
71
|
cmid: wsForum.cmid.toString(),
|
|
75
72
|
forum_id: wsForum.id,
|
|
76
73
|
name: wsForum.name,
|
|
@@ -79,16 +76,15 @@ function registerForumsCommand(program) {
|
|
|
79
76
|
});
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
|
-
|
|
79
|
+
console.log(JSON.stringify({
|
|
83
80
|
status: "success",
|
|
84
81
|
timestamp: new Date().toISOString(),
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
console.log(JSON.stringify(result));
|
|
82
|
+
total_courses: courses.length,
|
|
83
|
+
total_forums: allForums.length,
|
|
84
|
+
}));
|
|
85
|
+
for (const forum of allForums) {
|
|
86
|
+
console.log(JSON.stringify(forum));
|
|
87
|
+
}
|
|
92
88
|
});
|
|
93
89
|
forumsCmd
|
|
94
90
|
.command("list-all")
|
|
@@ -115,6 +111,7 @@ function registerForumsCommand(program) {
|
|
|
115
111
|
allForums.push({
|
|
116
112
|
course_id: wsForum.courseid,
|
|
117
113
|
course_name: course.fullname,
|
|
114
|
+
intro: wsForum.intro,
|
|
118
115
|
cmid: wsForum.cmid.toString(),
|
|
119
116
|
forum_id: wsForum.id,
|
|
120
117
|
name: wsForum.name,
|
|
@@ -122,16 +119,15 @@ function registerForumsCommand(program) {
|
|
|
122
119
|
});
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
|
-
|
|
122
|
+
console.log(JSON.stringify({
|
|
126
123
|
status: "success",
|
|
127
124
|
timestamp: new Date().toISOString(),
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
console.log(JSON.stringify(result));
|
|
125
|
+
total_courses: courses.length,
|
|
126
|
+
total_forums: allForums.length,
|
|
127
|
+
}));
|
|
128
|
+
for (const forum of allForums) {
|
|
129
|
+
console.log(JSON.stringify(forum));
|
|
130
|
+
}
|
|
135
131
|
});
|
|
136
132
|
forumsCmd
|
|
137
133
|
.command("discussions")
|
|
@@ -161,27 +157,29 @@ function registerForumsCommand(program) {
|
|
|
161
157
|
const course = courses.find(c => c.id === targetForum.courseid);
|
|
162
158
|
// Get discussions via WS API
|
|
163
159
|
const discussions = await (0, moodle_js_1.getForumDiscussionsApi)(apiContext.session, targetForum.id);
|
|
164
|
-
|
|
160
|
+
// Output NDJSON: one line per discussion entry for stream-friendly parsing
|
|
161
|
+
const meta = {
|
|
165
162
|
status: "success",
|
|
166
163
|
timestamp: new Date().toISOString(),
|
|
167
164
|
forum_id: targetForum.id,
|
|
168
165
|
forum_name: targetForum.name,
|
|
166
|
+
forum_intro: targetForum.intro,
|
|
169
167
|
course_id: course?.id,
|
|
170
168
|
course_name: course?.fullname,
|
|
171
|
-
|
|
169
|
+
total_discussions: discussions.length,
|
|
170
|
+
};
|
|
171
|
+
console.log(JSON.stringify(meta));
|
|
172
|
+
for (const d of discussions) {
|
|
173
|
+
console.log(JSON.stringify({
|
|
172
174
|
id: d.id,
|
|
173
175
|
name: d.name,
|
|
174
176
|
user_id: d.userId,
|
|
175
177
|
time_modified: d.timeModified,
|
|
176
178
|
post_count: d.postCount,
|
|
177
179
|
unread: d.unread,
|
|
178
|
-
message: (
|
|
179
|
-
}))
|
|
180
|
-
|
|
181
|
-
total_discussions: discussions.length,
|
|
182
|
-
},
|
|
183
|
-
};
|
|
184
|
-
console.log(JSON.stringify(result));
|
|
180
|
+
message: (0, utils_js_1.stripHtmlTags)(d.message || ""),
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
185
183
|
});
|
|
186
184
|
forumsCmd
|
|
187
185
|
.command("posts")
|
|
@@ -189,7 +187,7 @@ function registerForumsCommand(program) {
|
|
|
189
187
|
.argument("<discussion-id>", "Discussion ID")
|
|
190
188
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
191
189
|
.action(async (discussionId, options, command) => {
|
|
192
|
-
const output = getOutputFormat(command);
|
|
190
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
193
191
|
const apiContext = await createApiContext(options, command);
|
|
194
192
|
if (!apiContext) {
|
|
195
193
|
process.exitCode = 1;
|
|
@@ -206,8 +204,8 @@ function registerForumsCommand(program) {
|
|
|
206
204
|
subject: p.subject,
|
|
207
205
|
author: p.author,
|
|
208
206
|
author_id: p.authorId,
|
|
209
|
-
created:
|
|
210
|
-
modified:
|
|
207
|
+
created: (0, utils_js_1.formatTimestamp)(p.created),
|
|
208
|
+
modified: (0, utils_js_1.formatTimestamp)(p.modified),
|
|
211
209
|
message: p.message,
|
|
212
210
|
unread: p.unread,
|
|
213
211
|
})),
|
|
@@ -229,4 +227,98 @@ function registerForumsCommand(program) {
|
|
|
229
227
|
console.table(tablePosts);
|
|
230
228
|
}
|
|
231
229
|
});
|
|
230
|
+
forumsCmd
|
|
231
|
+
.command("post")
|
|
232
|
+
.description("Post a new discussion to a forum")
|
|
233
|
+
.argument("<forum-id>", "Forum ID")
|
|
234
|
+
.argument("<subject>", "Discussion subject")
|
|
235
|
+
.argument("<message>", "Discussion message")
|
|
236
|
+
.option("--subscribe", "Subscribe to the discussion", false)
|
|
237
|
+
.option("--pin", "Pin the discussion", false)
|
|
238
|
+
.action(async (forumId, subject, message, options, command) => {
|
|
239
|
+
const apiContext = await createApiContext(options, command);
|
|
240
|
+
if (!apiContext) {
|
|
241
|
+
process.exitCode = 1;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const { log, session } = apiContext;
|
|
245
|
+
// Get courses to find the forum
|
|
246
|
+
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(session, {
|
|
247
|
+
classification: "inprogress",
|
|
248
|
+
});
|
|
249
|
+
const courseIds = courses.map(c => c.id);
|
|
250
|
+
const wsForums = await (0, moodle_js_1.getForumsApi)(session, courseIds);
|
|
251
|
+
// Find forum by cmid or instance ID
|
|
252
|
+
const targetForum = wsForums.find(f => f.cmid.toString() === forumId || f.id === parseInt(forumId, 10));
|
|
253
|
+
if (!targetForum) {
|
|
254
|
+
log.error(`Forum not found: ${forumId}`);
|
|
255
|
+
process.exitCode = 1;
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const course = courses.find(c => c.id === targetForum.courseid);
|
|
259
|
+
log.info(`Posting to forum: ${targetForum.name} (${course?.fullname})`);
|
|
260
|
+
const result = await (0, moodle_js_1.addForumDiscussionApi)(session, targetForum.id, subject, message);
|
|
261
|
+
if (result.success) {
|
|
262
|
+
log.success(`✓ Discussion posted successfully!`);
|
|
263
|
+
log.info(` Discussion ID: ${result.discussionId}`);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
log.error(`✗ Failed to post discussion: ${result.error}`);
|
|
267
|
+
process.exitCode = 1;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
forumsCmd
|
|
271
|
+
.command("reply")
|
|
272
|
+
.description("Reply to a discussion post")
|
|
273
|
+
.argument("<post-id>", "Parent post ID to reply to")
|
|
274
|
+
.argument("<subject>", "Reply subject")
|
|
275
|
+
.argument("<message>", "Reply message")
|
|
276
|
+
.option("--attachment-id <id>", "Draft file ID for attachment")
|
|
277
|
+
.option("--inline-attachment-id <id>", "Draft file ID for inline attachment")
|
|
278
|
+
.action(async (postId, subject, message, options, command) => {
|
|
279
|
+
const apiContext = await createApiContext(options, command);
|
|
280
|
+
if (!apiContext) {
|
|
281
|
+
process.exitCode = 1;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const { log, session } = apiContext;
|
|
285
|
+
log.info(`Replying to post: ${postId}`);
|
|
286
|
+
log.info(` Subject: ${subject}`);
|
|
287
|
+
log.info(` Message: ${message}`);
|
|
288
|
+
if (options.attachmentId) {
|
|
289
|
+
log.info(` Attachment ID: ${options.attachmentId}`);
|
|
290
|
+
}
|
|
291
|
+
const result = await (0, moodle_js_1.addForumPostApi)(session, parseInt(postId, 10), subject, message, {
|
|
292
|
+
attachmentId: options.attachmentId ? parseInt(options.attachmentId, 10) : undefined,
|
|
293
|
+
inlineAttachmentId: options.inlineAttachmentId ? parseInt(options.inlineAttachmentId, 10) : undefined,
|
|
294
|
+
});
|
|
295
|
+
if (result.success) {
|
|
296
|
+
log.success(`✓ Reply posted successfully!`);
|
|
297
|
+
log.info(` Post ID: ${result.postId}`);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
log.error(`✗ Failed to post reply: ${result.error}`);
|
|
301
|
+
process.exitCode = 1;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
forumsCmd
|
|
305
|
+
.command("delete")
|
|
306
|
+
.description("Delete a forum post or discussion (by post ID)")
|
|
307
|
+
.argument("<post-id>", "Post ID to delete (deletes entire discussion if it's the first post)")
|
|
308
|
+
.action(async (postId, options, command) => {
|
|
309
|
+
const apiContext = await createApiContext(options, command);
|
|
310
|
+
if (!apiContext) {
|
|
311
|
+
process.exitCode = 1;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const { log, session } = apiContext;
|
|
315
|
+
const result = await (0, moodle_js_1.deleteForumPostApi)(session, parseInt(postId, 10));
|
|
316
|
+
if (result.success) {
|
|
317
|
+
log.success(`✓ Post ${postId} deleted successfully!`);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
log.error(`✗ Failed to delete post: ${result.error}`);
|
|
321
|
+
process.exitCode = 1;
|
|
322
|
+
}
|
|
323
|
+
});
|
|
232
324
|
}
|
|
@@ -23,19 +23,19 @@ function registerGradesCommand(program) {
|
|
|
23
23
|
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
24
24
|
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
25
25
|
const silent = outputFormat === "json" && !opts.verbose;
|
|
26
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
26
|
+
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
27
27
|
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
28
28
|
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
29
29
|
// Check if session exists
|
|
30
30
|
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
31
|
-
|
|
31
|
+
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
32
32
|
log.info(`Session 預期位置: ${sessionPath}`);
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
// Try to load WS token
|
|
36
36
|
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
37
37
|
if (!wsToken) {
|
|
38
|
-
|
|
38
|
+
console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
41
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../../src/src/commands/materials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../../src/src/commands/materials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAid/D"}
|