@mo7yw4ng/openape 1.0.5 → 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.
Files changed (152) hide show
  1. package/bin/openape +29 -0
  2. package/bin/openape.js +29 -0
  3. package/package.json +22 -28
  4. package/LICENSE +0 -21
  5. package/README.md +0 -135
  6. package/esm/_dnt.polyfills.d.ts +0 -101
  7. package/esm/_dnt.polyfills.d.ts.map +0 -1
  8. package/esm/_dnt.polyfills.js +0 -127
  9. package/esm/_dnt.shims.d.ts +0 -6
  10. package/esm/_dnt.shims.d.ts.map +0 -1
  11. package/esm/_dnt.shims.js +0 -61
  12. package/esm/deno.d.ts +0 -25
  13. package/esm/deno.d.ts.map +0 -1
  14. package/esm/deno.js +0 -23
  15. package/esm/package.json +0 -3
  16. package/esm/src/commands/announcements.d.ts +0 -3
  17. package/esm/src/commands/announcements.d.ts.map +0 -1
  18. package/esm/src/commands/announcements.js +0 -134
  19. package/esm/src/commands/assignments.d.ts +0 -3
  20. package/esm/src/commands/assignments.d.ts.map +0 -1
  21. package/esm/src/commands/assignments.js +0 -230
  22. package/esm/src/commands/auth.d.ts +0 -3
  23. package/esm/src/commands/auth.d.ts.map +0 -1
  24. package/esm/src/commands/auth.js +0 -290
  25. package/esm/src/commands/calendar.d.ts +0 -3
  26. package/esm/src/commands/calendar.d.ts.map +0 -1
  27. package/esm/src/commands/calendar.js +0 -179
  28. package/esm/src/commands/courses.d.ts +0 -3
  29. package/esm/src/commands/courses.d.ts.map +0 -1
  30. package/esm/src/commands/courses.js +0 -348
  31. package/esm/src/commands/forums.d.ts +0 -3
  32. package/esm/src/commands/forums.d.ts.map +0 -1
  33. package/esm/src/commands/forums.js +0 -318
  34. package/esm/src/commands/grades.d.ts +0 -3
  35. package/esm/src/commands/grades.d.ts.map +0 -1
  36. package/esm/src/commands/grades.js +0 -121
  37. package/esm/src/commands/materials.d.ts +0 -3
  38. package/esm/src/commands/materials.d.ts.map +0 -1
  39. package/esm/src/commands/materials.js +0 -413
  40. package/esm/src/commands/quizzes.d.ts +0 -3
  41. package/esm/src/commands/quizzes.d.ts.map +0 -1
  42. package/esm/src/commands/quizzes.js +0 -271
  43. package/esm/src/commands/skills.d.ts +0 -3
  44. package/esm/src/commands/skills.d.ts.map +0 -1
  45. package/esm/src/commands/skills.js +0 -106
  46. package/esm/src/commands/upload.d.ts +0 -3
  47. package/esm/src/commands/upload.d.ts.map +0 -1
  48. package/esm/src/commands/upload.js +0 -58
  49. package/esm/src/commands/videos.d.ts +0 -3
  50. package/esm/src/commands/videos.d.ts.map +0 -1
  51. package/esm/src/commands/videos.js +0 -336
  52. package/esm/src/index.d.ts +0 -27
  53. package/esm/src/index.d.ts.map +0 -1
  54. package/esm/src/index.js +0 -160
  55. package/esm/src/lib/auth.d.ts +0 -47
  56. package/esm/src/lib/auth.d.ts.map +0 -1
  57. package/esm/src/lib/auth.js +0 -227
  58. package/esm/src/lib/config.d.ts +0 -6
  59. package/esm/src/lib/config.d.ts.map +0 -1
  60. package/esm/src/lib/config.js +0 -36
  61. package/esm/src/lib/logger.d.ts +0 -3
  62. package/esm/src/lib/logger.d.ts.map +0 -1
  63. package/esm/src/lib/logger.js +0 -27
  64. package/esm/src/lib/moodle.d.ts +0 -433
  65. package/esm/src/lib/moodle.d.ts.map +0 -1
  66. package/esm/src/lib/moodle.js +0 -1318
  67. package/esm/src/lib/session.d.ts +0 -8
  68. package/esm/src/lib/session.d.ts.map +0 -1
  69. package/esm/src/lib/session.js +0 -42
  70. package/esm/src/lib/token.d.ts +0 -38
  71. package/esm/src/lib/token.d.ts.map +0 -1
  72. package/esm/src/lib/token.js +0 -178
  73. package/esm/src/lib/types.d.ts +0 -189
  74. package/esm/src/lib/types.d.ts.map +0 -1
  75. package/esm/src/lib/types.js +0 -2
  76. package/esm/src/lib/utils.d.ts +0 -57
  77. package/esm/src/lib/utils.d.ts.map +0 -1
  78. package/esm/src/lib/utils.js +0 -129
  79. package/script/_dnt.polyfills.d.ts +0 -101
  80. package/script/_dnt.polyfills.d.ts.map +0 -1
  81. package/script/_dnt.polyfills.js +0 -130
  82. package/script/_dnt.shims.d.ts +0 -6
  83. package/script/_dnt.shims.d.ts.map +0 -1
  84. package/script/_dnt.shims.js +0 -65
  85. package/script/deno.d.ts +0 -25
  86. package/script/deno.d.ts.map +0 -1
  87. package/script/deno.js +0 -25
  88. package/script/package.json +0 -3
  89. package/script/src/commands/announcements.d.ts +0 -3
  90. package/script/src/commands/announcements.d.ts.map +0 -1
  91. package/script/src/commands/announcements.js +0 -140
  92. package/script/src/commands/assignments.d.ts +0 -3
  93. package/script/src/commands/assignments.d.ts.map +0 -1
  94. package/script/src/commands/assignments.js +0 -269
  95. package/script/src/commands/auth.d.ts +0 -3
  96. package/script/src/commands/auth.d.ts.map +0 -1
  97. package/script/src/commands/auth.js +0 -296
  98. package/script/src/commands/calendar.d.ts +0 -3
  99. package/script/src/commands/calendar.d.ts.map +0 -1
  100. package/script/src/commands/calendar.js +0 -185
  101. package/script/src/commands/courses.d.ts +0 -3
  102. package/script/src/commands/courses.d.ts.map +0 -1
  103. package/script/src/commands/courses.js +0 -354
  104. package/script/src/commands/forums.d.ts +0 -3
  105. package/script/src/commands/forums.d.ts.map +0 -1
  106. package/script/src/commands/forums.js +0 -324
  107. package/script/src/commands/grades.d.ts +0 -3
  108. package/script/src/commands/grades.d.ts.map +0 -1
  109. package/script/src/commands/grades.js +0 -127
  110. package/script/src/commands/materials.d.ts +0 -3
  111. package/script/src/commands/materials.d.ts.map +0 -1
  112. package/script/src/commands/materials.js +0 -419
  113. package/script/src/commands/quizzes.d.ts +0 -3
  114. package/script/src/commands/quizzes.d.ts.map +0 -1
  115. package/script/src/commands/quizzes.js +0 -277
  116. package/script/src/commands/skills.d.ts +0 -3
  117. package/script/src/commands/skills.d.ts.map +0 -1
  118. package/script/src/commands/skills.js +0 -112
  119. package/script/src/commands/upload.d.ts +0 -3
  120. package/script/src/commands/upload.d.ts.map +0 -1
  121. package/script/src/commands/upload.js +0 -64
  122. package/script/src/commands/videos.d.ts +0 -3
  123. package/script/src/commands/videos.d.ts.map +0 -1
  124. package/script/src/commands/videos.js +0 -342
  125. package/script/src/index.d.ts +0 -27
  126. package/script/src/index.d.ts.map +0 -1
  127. package/script/src/index.js +0 -167
  128. package/script/src/lib/auth.d.ts +0 -47
  129. package/script/src/lib/auth.d.ts.map +0 -1
  130. package/script/src/lib/auth.js +0 -269
  131. package/script/src/lib/config.d.ts +0 -6
  132. package/script/src/lib/config.d.ts.map +0 -1
  133. package/script/src/lib/config.js +0 -42
  134. package/script/src/lib/logger.d.ts +0 -3
  135. package/script/src/lib/logger.d.ts.map +0 -1
  136. package/script/src/lib/logger.js +0 -30
  137. package/script/src/lib/moodle.d.ts +0 -433
  138. package/script/src/lib/moodle.d.ts.map +0 -1
  139. package/script/src/lib/moodle.js +0 -1389
  140. package/script/src/lib/session.d.ts +0 -8
  141. package/script/src/lib/session.d.ts.map +0 -1
  142. package/script/src/lib/session.js +0 -45
  143. package/script/src/lib/token.d.ts +0 -38
  144. package/script/src/lib/token.d.ts.map +0 -1
  145. package/script/src/lib/token.js +0 -189
  146. package/script/src/lib/types.d.ts +0 -189
  147. package/script/src/lib/types.d.ts.map +0 -1
  148. package/script/src/lib/types.js +0 -3
  149. package/script/src/lib/utils.d.ts +0 -57
  150. package/script/src/lib/utils.d.ts.map +0 -1
  151. package/script/src/lib/utils.js +0 -175
  152. package/skills/openape/SKILL.md +0 -115
@@ -1,324 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerForumsCommand = registerForumsCommand;
7
- const utils_js_1 = require("../lib/utils.js");
8
- const moodle_js_1 = require("../lib/moodle.js");
9
- const logger_js_1 = require("../lib/logger.js");
10
- const token_js_1 = require("../lib/token.js");
11
- const node_path_1 = __importDefault(require("node:path"));
12
- const node_fs_1 = __importDefault(require("node:fs"));
13
- function registerForumsCommand(program) {
14
- const forumsCmd = program.command("forums");
15
- forumsCmd.description("Forum operations");
16
- // Pure API context - no browser required (fast!)
17
- async function createApiContext(options, command) {
18
- const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
19
- const outputFormat = (0, utils_js_1.getOutputFormat)(command || { optsWithGlobals: () => ({ output: "json" }) });
20
- const silent = outputFormat === "json" && !opts.verbose;
21
- const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
22
- const baseDir = (0, utils_js_1.getBaseDir)();
23
- const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
24
- // Check if session exists
25
- if (!node_fs_1.default.existsSync(sessionPath)) {
26
- console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
27
- log.info(`Session 預期位置: ${sessionPath}`);
28
- return null;
29
- }
30
- // Try to load WS token
31
- const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
32
- if (!wsToken) {
33
- console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
34
- return null;
35
- }
36
- // Try to load sesskey from cache
37
- const sesskey = (0, token_js_1.loadSesskey)(sessionPath) || undefined;
38
- return {
39
- log,
40
- session: {
41
- wsToken,
42
- moodleBaseUrl: "https://ilearning.cycu.edu.tw",
43
- sesskey,
44
- },
45
- };
46
- }
47
- forumsCmd
48
- .command("list")
49
- .description("List forums from in-progress courses")
50
- .option("--output <format>", "Output format: json|csv|table|silent")
51
- .action(async (options, command) => {
52
- const apiContext = await createApiContext(options, command);
53
- if (!apiContext) {
54
- process.exitCode = 1;
55
- return;
56
- }
57
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
58
- classification: "inprogress",
59
- });
60
- // Get forums via WS API (no browser needed!)
61
- const courseIds = courses.map(c => c.id);
62
- const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
63
- const allForums = [];
64
- for (const wsForum of wsForums) {
65
- const course = courses.find(c => c.id === wsForum.courseid);
66
- if (course) {
67
- allForums.push({
68
- course_id: wsForum.courseid,
69
- course_name: course.fullname,
70
- intro: wsForum.intro,
71
- cmid: wsForum.cmid.toString(),
72
- forum_id: wsForum.id,
73
- name: wsForum.name,
74
- timemodified: wsForum.timemodified,
75
- // url: `https://ilearning.cycu.edu.tw/mod/forum/view.php?id=${wsForum.cmid}`,
76
- });
77
- }
78
- }
79
- console.log(JSON.stringify({
80
- status: "success",
81
- timestamp: new Date().toISOString(),
82
- total_courses: courses.length,
83
- total_forums: allForums.length,
84
- }));
85
- for (const forum of allForums) {
86
- console.log(JSON.stringify(forum));
87
- }
88
- });
89
- forumsCmd
90
- .command("list-all")
91
- .description("List all forums across all courses")
92
- .option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
93
- .option("--output <format>", "Output format: json|csv|table|silent")
94
- .action(async (options, command) => {
95
- const apiContext = await createApiContext(options, command);
96
- if (!apiContext) {
97
- process.exitCode = 1;
98
- return;
99
- }
100
- const classification = options.level === "all" ? undefined : "inprogress";
101
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
102
- classification,
103
- });
104
- // Get forums via WS API (no browser needed!)
105
- const courseIds = courses.map(c => c.id);
106
- const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
107
- const allForums = [];
108
- for (const wsForum of wsForums) {
109
- const course = courses.find(c => c.id === wsForum.courseid);
110
- if (course) {
111
- allForums.push({
112
- course_id: wsForum.courseid,
113
- course_name: course.fullname,
114
- intro: wsForum.intro,
115
- cmid: wsForum.cmid.toString(),
116
- forum_id: wsForum.id,
117
- name: wsForum.name,
118
- timemodified: wsForum.timemodified,
119
- });
120
- }
121
- }
122
- console.log(JSON.stringify({
123
- status: "success",
124
- timestamp: new Date().toISOString(),
125
- total_courses: courses.length,
126
- total_forums: allForums.length,
127
- }));
128
- for (const forum of allForums) {
129
- console.log(JSON.stringify(forum));
130
- }
131
- });
132
- forumsCmd
133
- .command("discussions")
134
- .description("List discussions in a forum (use forum ID)")
135
- .argument("<forum-id>", "Forum ID")
136
- .option("--output <format>", "Output format: json|csv|table|silent")
137
- .action(async (forumId, options, command) => {
138
- const apiContext = await createApiContext(options, command);
139
- if (!apiContext) {
140
- process.exitCode = 1;
141
- return;
142
- }
143
- // Get courses via WS API
144
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
145
- classification: "inprogress",
146
- });
147
- // Get forums via WS API
148
- const courseIds = courses.map(c => c.id);
149
- const wsForums = await (0, moodle_js_1.getForumsApi)(apiContext.session, courseIds);
150
- // Find forum by cmid or instance ID
151
- const targetForum = wsForums.find(f => f.cmid.toString() === forumId || f.id === parseInt(forumId, 10));
152
- if (!targetForum) {
153
- console.log(JSON.stringify({ status: "error", error: "Forum not found" }));
154
- process.exitCode = 1;
155
- return;
156
- }
157
- const course = courses.find(c => c.id === targetForum.courseid);
158
- // Get discussions via WS API
159
- const discussions = await (0, moodle_js_1.getForumDiscussionsApi)(apiContext.session, targetForum.id);
160
- // Output NDJSON: one line per discussion entry for stream-friendly parsing
161
- const meta = {
162
- status: "success",
163
- timestamp: new Date().toISOString(),
164
- forum_id: targetForum.id,
165
- forum_name: targetForum.name,
166
- forum_intro: targetForum.intro,
167
- course_id: course?.id,
168
- course_name: course?.fullname,
169
- total_discussions: discussions.length,
170
- };
171
- console.log(JSON.stringify(meta));
172
- for (const d of discussions) {
173
- console.log(JSON.stringify({
174
- id: d.id,
175
- name: d.name,
176
- user_id: d.userId,
177
- time_modified: d.timeModified,
178
- post_count: d.postCount,
179
- unread: d.unread,
180
- message: (0, utils_js_1.stripHtmlTags)(d.message || ""),
181
- }));
182
- }
183
- });
184
- forumsCmd
185
- .command("posts")
186
- .description("Show posts in a discussion")
187
- .argument("<discussion-id>", "Discussion ID")
188
- .option("--output <format>", "Output format: json|csv|table|silent")
189
- .action(async (discussionId, options, command) => {
190
- const output = (0, utils_js_1.getOutputFormat)(command);
191
- const apiContext = await createApiContext(options, command);
192
- if (!apiContext) {
193
- process.exitCode = 1;
194
- return;
195
- }
196
- const posts = await (0, moodle_js_1.getDiscussionPostsApi)(apiContext.session, parseInt(discussionId, 10));
197
- if (output === "json") {
198
- const result = {
199
- status: "success",
200
- timestamp: new Date().toISOString(),
201
- discussion_id: discussionId,
202
- posts: posts.map(p => ({
203
- id: p.id,
204
- subject: p.subject,
205
- author: p.author,
206
- author_id: p.authorId,
207
- created: (0, utils_js_1.formatTimestamp)(p.created),
208
- modified: (0, utils_js_1.formatTimestamp)(p.modified),
209
- message: p.message,
210
- unread: p.unread,
211
- })),
212
- summary: {
213
- total_posts: posts.length,
214
- },
215
- };
216
- console.log(JSON.stringify(result));
217
- }
218
- else if (output === "table") {
219
- console.log(`Discussion ${discussionId} - ${posts.length} posts`);
220
- console.log("Use --output json to see full post content");
221
- const tablePosts = posts.map(p => ({
222
- id: p.id,
223
- subject: p.subject.substring(0, 50) + (p.subject.length > 50 ? "..." : ""),
224
- author: p.author,
225
- created: new Date(p.created * 1000).toLocaleString(),
226
- }));
227
- console.table(tablePosts);
228
- }
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
- });
324
- }
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare function registerGradesCommand(program: Command): void;
3
- //# sourceMappingURL=grades.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"grades.d.ts","sourceRoot":"","sources":["../../../src/src/commands/grades.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsI5D"}
@@ -1,127 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.registerGradesCommand = registerGradesCommand;
7
- const utils_js_1 = require("../lib/utils.js");
8
- const moodle_js_1 = require("../lib/moodle.js");
9
- const logger_js_1 = require("../lib/logger.js");
10
- const token_js_1 = require("../lib/token.js");
11
- const index_js_1 = require("../index.js");
12
- const node_path_1 = __importDefault(require("node:path"));
13
- const node_fs_1 = __importDefault(require("node:fs"));
14
- function registerGradesCommand(program) {
15
- const gradesCmd = program.command("grades");
16
- gradesCmd.description("Grade operations");
17
- function getOutputFormat(command) {
18
- const opts = command.optsWithGlobals();
19
- return opts.output || "json";
20
- }
21
- // Pure API context - no browser required (fast!)
22
- async function createApiContext(options, command) {
23
- const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
24
- const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
25
- const silent = outputFormat === "json" && !opts.verbose;
26
- const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
27
- const baseDir = (0, utils_js_1.getBaseDir)();
28
- const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
29
- // Check if session exists
30
- if (!node_fs_1.default.existsSync(sessionPath)) {
31
- console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
32
- log.info(`Session 預期位置: ${sessionPath}`);
33
- return null;
34
- }
35
- // Try to load WS token
36
- const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
37
- if (!wsToken) {
38
- console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
39
- return null;
40
- }
41
- return {
42
- log,
43
- session: {
44
- wsToken,
45
- moodleBaseUrl: "https://ilearning.cycu.edu.tw",
46
- },
47
- };
48
- }
49
- gradesCmd
50
- .command("summary")
51
- .description("Show grade summary across all courses")
52
- .option("--output <format>", "Output format: json|csv|table|silent")
53
- .action(async (options, command) => {
54
- const output = getOutputFormat(command);
55
- const apiContext = await createApiContext(options, command);
56
- if (!apiContext) {
57
- process.exitCode = 1;
58
- return;
59
- }
60
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
61
- const gradeSummaries = [];
62
- for (const course of courses) {
63
- const grades = await (0, moodle_js_1.getCourseGradesApi)(apiContext.session, course.id);
64
- gradeSummaries.push({
65
- courseId: course.id,
66
- courseName: course.fullname,
67
- grade: grades.grade,
68
- gradeFormatted: grades.gradeFormatted,
69
- rank: grades.rank,
70
- totalUsers: grades.totalUsers,
71
- });
72
- }
73
- // Calculate overall statistics
74
- const gradedCourses = gradeSummaries.filter(g => g.grade !== undefined && g.grade !== null && g.grade !== "-");
75
- const averageRank = gradeSummaries
76
- .filter(g => g.rank !== undefined && g.rank !== null)
77
- .reduce((sum, g) => sum + (g.rank || 0), 0) /
78
- (gradeSummaries.filter(g => g.rank !== undefined && g.rank !== null).length || 1);
79
- const summaryData = {
80
- total_courses: courses.length,
81
- graded_courses: gradedCourses.length,
82
- average_rank: averageRank.toFixed(1),
83
- grades: gradeSummaries,
84
- };
85
- (0, index_js_1.formatAndOutput)(summaryData, output, apiContext.log);
86
- });
87
- gradesCmd
88
- .command("course")
89
- .description("Show detailed grades for a specific course")
90
- .argument("<course-id>", "Course ID")
91
- .option("--output <format>", "Output format: json|csv|table|silent")
92
- .action(async (courseId, options, command) => {
93
- const output = getOutputFormat(command);
94
- const apiContext = await createApiContext(options, command);
95
- if (!apiContext) {
96
- process.exitCode = 1;
97
- return;
98
- }
99
- const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
100
- const course = courses.find(c => c.id === parseInt(courseId, 10));
101
- if (!course) {
102
- apiContext.log.error(`Course not found: ${courseId}`);
103
- process.exitCode = 1;
104
- return;
105
- }
106
- const grades = await (0, moodle_js_1.getCourseGradesApi)(apiContext.session, course.id);
107
- const gradeData = {
108
- courseId: grades.courseId,
109
- courseName: grades.courseName,
110
- grade: grades.grade,
111
- gradeFormatted: grades.gradeFormatted,
112
- rank: grades.rank,
113
- totalUsers: grades.totalUsers,
114
- items: grades.items?.map(item => ({
115
- name: item.name,
116
- grade: item.grade,
117
- gradeFormatted: item.gradeFormatted,
118
- range: item.range,
119
- percentage: item.percentage,
120
- weight: item.weight,
121
- feedback: item.feedback,
122
- graded: item.graded,
123
- })),
124
- };
125
- (0, index_js_1.formatAndOutput)(gradeData, output, apiContext.log);
126
- });
127
- }
@@ -1,3 +0,0 @@
1
- import { Command } from "commander";
2
- export declare function registerMaterialsCommand(program: Command): void;
3
- //# sourceMappingURL=materials.d.ts.map
@@ -1 +0,0 @@
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"}