@mo7yw4ng/openape 1.0.5 → 1.0.6
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 +11 -8
- package/esm/deno.js +1 -1
- package/esm/src/commands/announcements.d.ts.map +1 -1
- package/esm/src/commands/announcements.js +22 -85
- package/esm/src/commands/assignments.d.ts.map +1 -1
- package/esm/src/commands/assignments.js +2 -3
- package/esm/src/commands/calendar.d.ts.map +1 -1
- package/esm/src/commands/calendar.js +32 -84
- package/esm/src/commands/courses.d.ts.map +1 -1
- package/esm/src/commands/courses.js +2 -38
- package/esm/src/commands/forums.d.ts.map +1 -1
- package/esm/src/commands/forums.js +47 -175
- package/esm/src/commands/grades.d.ts.map +1 -1
- package/esm/src/commands/grades.js +10 -47
- package/esm/src/commands/materials.d.ts.map +1 -1
- package/esm/src/commands/materials.js +47 -58
- package/esm/src/commands/quizzes.d.ts.map +1 -1
- package/esm/src/commands/quizzes.js +2 -37
- package/esm/src/commands/skills.js +3 -3
- package/esm/src/commands/upload.d.ts.map +1 -1
- package/esm/src/commands/upload.js +2 -5
- package/esm/src/commands/videos.d.ts.map +1 -1
- package/esm/src/commands/videos.js +6 -76
- package/esm/src/index.d.ts +2 -1
- package/esm/src/index.d.ts.map +1 -1
- package/esm/src/index.js +5 -1
- package/esm/src/lib/auth.d.ts +21 -2
- package/esm/src/lib/auth.d.ts.map +1 -1
- package/esm/src/lib/auth.js +78 -19
- package/esm/src/lib/logger.d.ts +2 -2
- package/esm/src/lib/logger.d.ts.map +1 -1
- package/esm/src/lib/logger.js +1 -2
- package/esm/src/lib/moodle.d.ts +14 -0
- package/esm/src/lib/moodle.d.ts.map +1 -1
- package/esm/src/lib/moodle.js +35 -0
- package/esm/src/lib/utils.d.ts +3 -8
- package/esm/src/lib/utils.d.ts.map +1 -1
- package/esm/src/lib/utils.js +3 -10
- 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 +23 -89
- package/script/src/commands/assignments.d.ts.map +1 -1
- package/script/src/commands/assignments.js +2 -3
- package/script/src/commands/calendar.d.ts.map +1 -1
- package/script/src/commands/calendar.js +33 -85
- package/script/src/commands/courses.d.ts.map +1 -1
- package/script/src/commands/courses.js +9 -48
- package/script/src/commands/forums.d.ts.map +1 -1
- package/script/src/commands/forums.js +50 -181
- package/script/src/commands/grades.d.ts.map +1 -1
- package/script/src/commands/grades.js +14 -54
- package/script/src/commands/materials.d.ts.map +1 -1
- package/script/src/commands/materials.js +47 -58
- package/script/src/commands/quizzes.d.ts.map +1 -1
- package/script/src/commands/quizzes.js +11 -49
- package/script/src/commands/skills.js +3 -3
- package/script/src/commands/upload.d.ts.map +1 -1
- package/script/src/commands/upload.js +2 -5
- package/script/src/commands/videos.d.ts.map +1 -1
- package/script/src/commands/videos.js +11 -81
- package/script/src/index.d.ts +2 -1
- package/script/src/index.d.ts.map +1 -1
- package/script/src/index.js +5 -1
- package/script/src/lib/auth.d.ts +21 -2
- package/script/src/lib/auth.d.ts.map +1 -1
- package/script/src/lib/auth.js +83 -56
- package/script/src/lib/logger.d.ts +2 -2
- package/script/src/lib/logger.d.ts.map +1 -1
- package/script/src/lib/logger.js +1 -2
- package/script/src/lib/moodle.d.ts +14 -0
- package/script/src/lib/moodle.d.ts.map +1 -1
- package/script/src/lib/moodle.js +36 -0
- package/script/src/lib/utils.d.ts +3 -8
- package/script/src/lib/utils.d.ts.map +1 -1
- package/script/src/lib/utils.js +3 -11
- package/skills/openape/SKILL.md +6 -6
|
@@ -1,51 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerAnnouncementsCommand = registerAnnouncementsCommand;
|
|
7
4
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
5
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
6
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
7
|
+
const index_js_1 = require("../index.js");
|
|
13
8
|
function registerAnnouncementsCommand(program) {
|
|
14
9
|
const announcementsCmd = program.command("announcements");
|
|
15
10
|
announcementsCmd.description("Announcement operations");
|
|
16
|
-
// Helper to get output format from global or local options
|
|
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
11
|
announcementsCmd
|
|
50
12
|
.command("list-all")
|
|
51
13
|
.description("List all announcements across all courses")
|
|
@@ -54,55 +16,31 @@ function registerAnnouncementsCommand(program) {
|
|
|
54
16
|
.option("--limit <n>", "Maximum number of announcements to show", "20")
|
|
55
17
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
56
18
|
.action(async (options, command) => {
|
|
19
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
57
20
|
const limit = parseInt(options.limit, 10);
|
|
58
|
-
const apiContext = await createApiContext(options, command);
|
|
21
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
59
22
|
if (!apiContext) {
|
|
60
23
|
process.exitCode = 1;
|
|
61
24
|
return;
|
|
62
25
|
}
|
|
63
|
-
// Get site info to retrieve userid
|
|
64
26
|
const siteInfo = await (0, moodle_js_1.getSiteInfoApi)(apiContext.session);
|
|
65
|
-
// Get messages for the current user
|
|
66
27
|
const messages = await (0, moodle_js_1.getMessagesApi)(apiContext.session, siteInfo.userid, {
|
|
67
28
|
limitnum: limit,
|
|
68
29
|
});
|
|
69
|
-
// Convert messages to announcement format
|
|
70
30
|
const allAnnouncements = messages.map(m => ({
|
|
71
|
-
course_id: 0,
|
|
31
|
+
course_id: 0,
|
|
72
32
|
course_name: "Notifications",
|
|
73
33
|
id: m.id,
|
|
74
34
|
subject: m.subject,
|
|
75
35
|
author: `User ${m.useridfrom}`,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
unread: false,
|
|
80
|
-
forumId: 0,
|
|
36
|
+
author_id: m.useridfrom,
|
|
37
|
+
created_at: (0, utils_js_1.formatTimestamp)(m.timecreated),
|
|
38
|
+
modified_at: (0, utils_js_1.formatTimestamp)(m.timecreated),
|
|
39
|
+
unread: false,
|
|
81
40
|
}));
|
|
82
|
-
|
|
83
|
-
allAnnouncements.
|
|
84
|
-
|
|
85
|
-
let filteredAnnouncements = allAnnouncements.slice(0, limit);
|
|
86
|
-
console.log(JSON.stringify({
|
|
87
|
-
status: "success",
|
|
88
|
-
timestamp: new Date().toISOString(),
|
|
89
|
-
level: options.level,
|
|
90
|
-
total_announcements: allAnnouncements.length,
|
|
91
|
-
shown: filteredAnnouncements.length,
|
|
92
|
-
}));
|
|
93
|
-
for (const a of filteredAnnouncements) {
|
|
94
|
-
console.log(JSON.stringify({
|
|
95
|
-
course_id: a.course_id,
|
|
96
|
-
course_name: a.course_name,
|
|
97
|
-
id: a.id,
|
|
98
|
-
subject: a.subject,
|
|
99
|
-
author: a.author,
|
|
100
|
-
author_id: a.authorId,
|
|
101
|
-
created_at: (0, utils_js_1.formatTimestamp)(a.createdAt),
|
|
102
|
-
modified_at: (0, utils_js_1.formatTimestamp)(a.modifiedAt),
|
|
103
|
-
unread: a.unread,
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
41
|
+
allAnnouncements.sort((a, b) => b.created_at > a.created_at ? 1 : -1);
|
|
42
|
+
const shown = allAnnouncements.slice(0, limit);
|
|
43
|
+
(0, index_js_1.formatAndOutput)(shown, output, apiContext.log, { status: "success", timestamp: new Date().toISOString(), total_announcements: allAnnouncements.length, shown: shown.length });
|
|
106
44
|
});
|
|
107
45
|
announcementsCmd
|
|
108
46
|
.command("read")
|
|
@@ -110,7 +48,8 @@ function registerAnnouncementsCommand(program) {
|
|
|
110
48
|
.argument("<announcement-id>", "Discussion ID of the announcement")
|
|
111
49
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
112
50
|
.action(async (announcementId, options, command) => {
|
|
113
|
-
const
|
|
51
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
52
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
114
53
|
if (!apiContext) {
|
|
115
54
|
process.exitCode = 1;
|
|
116
55
|
return;
|
|
@@ -122,19 +61,14 @@ function registerAnnouncementsCommand(program) {
|
|
|
122
61
|
return;
|
|
123
62
|
}
|
|
124
63
|
const firstPost = posts[0];
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
modified_at: (0, utils_js_1.formatTimestamp)(firstPost.modified),
|
|
135
|
-
message: firstPost.message,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
console.log(JSON.stringify(output));
|
|
64
|
+
(0, index_js_1.formatAndOutput)({
|
|
65
|
+
id: announcementId,
|
|
66
|
+
subject: firstPost.subject,
|
|
67
|
+
author: firstPost.author,
|
|
68
|
+
author_id: firstPost.authorId,
|
|
69
|
+
created_at: (0, utils_js_1.formatTimestamp)(firstPost.created),
|
|
70
|
+
modified_at: (0, utils_js_1.formatTimestamp)(firstPost.modified),
|
|
71
|
+
message: firstPost.message,
|
|
72
|
+
}, output, apiContext.log);
|
|
139
73
|
});
|
|
140
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assignments.d.ts","sourceRoot":"","sources":["../../../src/src/commands/assignments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"assignments.d.ts","sourceRoot":"","sources":["../../../src/src/commands/assignments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyPjE"}
|
|
@@ -197,9 +197,9 @@ function registerAssignmentsCommand(program) {
|
|
|
197
197
|
// Upload file if --file option is provided
|
|
198
198
|
if (options.file) {
|
|
199
199
|
const resolvedPath = node_path_1.default.resolve(options.file);
|
|
200
|
-
|
|
200
|
+
let stats;
|
|
201
201
|
try {
|
|
202
|
-
await promises_1.default.
|
|
202
|
+
stats = await promises_1.default.stat(resolvedPath);
|
|
203
203
|
}
|
|
204
204
|
catch {
|
|
205
205
|
const errorResult = {
|
|
@@ -210,7 +210,6 @@ function registerAssignmentsCommand(program) {
|
|
|
210
210
|
process.exitCode = 1;
|
|
211
211
|
return;
|
|
212
212
|
}
|
|
213
|
-
const stats = await promises_1.default.stat(resolvedPath);
|
|
214
213
|
const fileSizeKB = (0, utils_js_1.formatFileSize)(stats.size);
|
|
215
214
|
const uploadResult = await (0, moodle_js_1.uploadFileApi)(apiContext.session, resolvedPath);
|
|
216
215
|
if (!uploadResult.success) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/src/commands/calendar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/src/commands/calendar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqJ9D"}
|
|
@@ -6,46 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.registerCalendarCommand = registerCalendarCommand;
|
|
7
7
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
8
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
10
|
+
const index_js_1 = require("../index.js");
|
|
12
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
12
|
function registerCalendarCommand(program) {
|
|
14
13
|
const calendarCmd = program.command("calendar");
|
|
15
14
|
calendarCmd.description("Calendar operations");
|
|
16
|
-
// Helper to get output format from global or local options
|
|
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
15
|
calendarCmd
|
|
50
16
|
.command("events")
|
|
51
17
|
.description("List calendar events")
|
|
@@ -54,19 +20,18 @@ function registerCalendarCommand(program) {
|
|
|
54
20
|
.option("--course <id>", "Filter by course ID")
|
|
55
21
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
56
22
|
.action(async (options, command) => {
|
|
23
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
57
24
|
const days = parseInt(options.days, 10);
|
|
58
|
-
const apiContext = await createApiContext(options, command);
|
|
25
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
59
26
|
if (!apiContext) {
|
|
60
27
|
process.exitCode = 1;
|
|
61
28
|
return;
|
|
62
29
|
}
|
|
63
30
|
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
|
|
64
|
-
// Calculate time range
|
|
65
31
|
const now = Math.floor(Date.now() / 1000);
|
|
66
32
|
const endTime = now + (days * 24 * 60 * 60);
|
|
67
33
|
let allEvents = [];
|
|
68
34
|
if (options.course) {
|
|
69
|
-
// Get events for specific course
|
|
70
35
|
const courseId = parseInt(options.course, 10);
|
|
71
36
|
const events = await (0, moodle_js_1.getCalendarEventsApi)(apiContext.session, {
|
|
72
37
|
startTime: now,
|
|
@@ -75,29 +40,32 @@ function registerCalendarCommand(program) {
|
|
|
75
40
|
allEvents = events.filter(e => e.courseid === courseId);
|
|
76
41
|
}
|
|
77
42
|
else {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
allEvents.push(...events);
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
apiContext.log.debug(`Failed to fetch calendar events for ${course.fullname}: ${err}`);
|
|
90
|
-
}
|
|
43
|
+
const results = await Promise.allSettled(courses.map(course => (0, moodle_js_1.getCalendarEventsApi)(apiContext.session, {
|
|
44
|
+
courseId: course.id,
|
|
45
|
+
startTime: now,
|
|
46
|
+
endTime: endTime,
|
|
47
|
+
})));
|
|
48
|
+
for (const result of results) {
|
|
49
|
+
if (result.status === "fulfilled")
|
|
50
|
+
allEvents.push(...result.value);
|
|
91
51
|
}
|
|
92
52
|
}
|
|
93
|
-
// Sort by start time
|
|
94
53
|
allEvents.sort((a, b) => a.timestart - b.timestart);
|
|
95
|
-
// Filter upcoming only if requested
|
|
96
54
|
let filteredEvents = allEvents;
|
|
97
55
|
if (options.upcoming) {
|
|
98
56
|
filteredEvents = allEvents.filter(e => e.timestart > now);
|
|
99
57
|
}
|
|
100
|
-
|
|
58
|
+
const items = filteredEvents.map(e => ({
|
|
59
|
+
id: e.id,
|
|
60
|
+
name: e.name,
|
|
61
|
+
description: e.description,
|
|
62
|
+
course_id: e.courseid,
|
|
63
|
+
event_type: e.eventtype,
|
|
64
|
+
start_time: (0, utils_js_1.formatTimestamp)(e.timestart),
|
|
65
|
+
end_time: e.timeduration ? (0, utils_js_1.formatTimestamp)(e.timestart + Math.floor(e.timeduration / 1000)) : null,
|
|
66
|
+
location: e.location,
|
|
67
|
+
}));
|
|
68
|
+
(0, index_js_1.formatAndOutput)(items, output, apiContext.log, {
|
|
101
69
|
status: "success",
|
|
102
70
|
timestamp: new Date().toISOString(),
|
|
103
71
|
total_events: allEvents.length,
|
|
@@ -106,19 +74,7 @@ function registerCalendarCommand(program) {
|
|
|
106
74
|
acc[e.eventtype] = (acc[e.eventtype] || 0) + 1;
|
|
107
75
|
return acc;
|
|
108
76
|
}, {}),
|
|
109
|
-
})
|
|
110
|
-
for (const e of filteredEvents) {
|
|
111
|
-
console.log(JSON.stringify({
|
|
112
|
-
id: e.id,
|
|
113
|
-
name: e.name,
|
|
114
|
-
description: e.description,
|
|
115
|
-
course_id: e.courseid,
|
|
116
|
-
event_type: e.eventtype,
|
|
117
|
-
start_time: (0, utils_js_1.formatTimestamp)(e.timestart),
|
|
118
|
-
end_time: e.timeduration ? (0, utils_js_1.formatTimestamp)(e.timestart + Math.floor(e.timeduration / 1000)) : null,
|
|
119
|
-
location: e.location,
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
77
|
+
});
|
|
122
78
|
});
|
|
123
79
|
calendarCmd
|
|
124
80
|
.command("export")
|
|
@@ -126,33 +82,26 @@ function registerCalendarCommand(program) {
|
|
|
126
82
|
.option("--output <path>", "Output file path", "./calendar.json")
|
|
127
83
|
.option("--days <n>", "Number of days ahead to include", "30")
|
|
128
84
|
.action(async (options, command) => {
|
|
129
|
-
const apiContext = await createApiContext(options, command);
|
|
85
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
130
86
|
if (!apiContext) {
|
|
131
87
|
process.exitCode = 1;
|
|
132
88
|
return;
|
|
133
89
|
}
|
|
134
90
|
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session);
|
|
135
|
-
// Calculate time range
|
|
136
91
|
const now = Math.floor(Date.now() / 1000);
|
|
137
92
|
const days = parseInt(options.days, 10);
|
|
138
93
|
const endTime = now + (days * 24 * 60 * 60);
|
|
139
94
|
const allEvents = [];
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
allEvents.push(...
|
|
148
|
-
}
|
|
149
|
-
catch (err) {
|
|
150
|
-
apiContext.log.debug(`Failed to fetch calendar events for ${course.fullname}: ${err}`);
|
|
151
|
-
}
|
|
95
|
+
const results = await Promise.allSettled(courses.map(course => (0, moodle_js_1.getCalendarEventsApi)(apiContext.session, {
|
|
96
|
+
courseId: course.id,
|
|
97
|
+
startTime: now,
|
|
98
|
+
endTime: endTime,
|
|
99
|
+
})));
|
|
100
|
+
for (const result of results) {
|
|
101
|
+
if (result.status === "fulfilled")
|
|
102
|
+
allEvents.push(...result.value);
|
|
152
103
|
}
|
|
153
|
-
// Sort by start time
|
|
154
104
|
allEvents.sort((a, b) => a.timestart - b.timestart);
|
|
155
|
-
// Export data
|
|
156
105
|
const exportData = {
|
|
157
106
|
exported_at: new Date().toISOString(),
|
|
158
107
|
time_range: {
|
|
@@ -178,7 +127,6 @@ function registerCalendarCommand(program) {
|
|
|
178
127
|
}, {}),
|
|
179
128
|
},
|
|
180
129
|
};
|
|
181
|
-
// Write to file
|
|
182
130
|
node_fs_1.default.writeFileSync(options.output, JSON.stringify(exportData));
|
|
183
131
|
apiContext.log.success(`Exported ${allEvents.length} events to ${options.output}`);
|
|
184
132
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"courses.d.ts","sourceRoot":"","sources":["../../../src/src/commands/courses.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"courses.d.ts","sourceRoot":"","sources":["../../../src/src/commands/courses.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkW7D"}
|
|
@@ -1,52 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.registerCoursesCommand = registerCoursesCommand;
|
|
7
4
|
const utils_js_1 = require("../lib/utils.js");
|
|
8
5
|
const moodle_js_1 = require("../lib/moodle.js");
|
|
9
|
-
const
|
|
10
|
-
const token_js_1 = require("../lib/token.js");
|
|
6
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
11
7
|
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
8
|
function registerCoursesCommand(program) {
|
|
15
9
|
const coursesCmd = program.command("courses");
|
|
16
10
|
coursesCmd.description("Course operations");
|
|
17
|
-
// Helper to get output format from global or local options
|
|
18
|
-
function getOutputFormat(command) {
|
|
19
|
-
const opts = command.optsWithGlobals();
|
|
20
|
-
return opts.output || "json";
|
|
21
|
-
}
|
|
22
|
-
// Pure API context - no browser required (fast!)
|
|
23
|
-
async function createApiContext(options, command) {
|
|
24
|
-
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
25
|
-
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
26
|
-
const silent = outputFormat === "json" && !opts.verbose;
|
|
27
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
28
|
-
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
29
|
-
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
30
|
-
// Check if session exists
|
|
31
|
-
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
32
|
-
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
33
|
-
log.info(`Session 預期位置: ${sessionPath}`);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
// Try to load WS token
|
|
37
|
-
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
38
|
-
if (!wsToken) {
|
|
39
|
-
console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
log,
|
|
44
|
-
session: {
|
|
45
|
-
wsToken,
|
|
46
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
11
|
coursesCmd
|
|
51
12
|
.command("list")
|
|
52
13
|
.description("List enrolled courses")
|
|
@@ -54,8 +15,8 @@ function registerCoursesCommand(program) {
|
|
|
54
15
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
55
16
|
.option("--level <type>", "Course level: in_progress (default) | past | future | all", "in_progress")
|
|
56
17
|
.action(async (options, command) => {
|
|
57
|
-
const output = getOutputFormat(command);
|
|
58
|
-
const apiContext = await createApiContext(options, command);
|
|
18
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
19
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
59
20
|
if (!apiContext) {
|
|
60
21
|
process.exitCode = 1;
|
|
61
22
|
return;
|
|
@@ -79,8 +40,8 @@ function registerCoursesCommand(program) {
|
|
|
79
40
|
.argument("<course-id>", "Course ID")
|
|
80
41
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
81
42
|
.action(async (courseId, options, command) => {
|
|
82
|
-
const output = getOutputFormat(command);
|
|
83
|
-
const apiContext = await createApiContext(options, command);
|
|
43
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
44
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
84
45
|
if (!apiContext) {
|
|
85
46
|
process.exitCode = 1;
|
|
86
47
|
return;
|
|
@@ -100,8 +61,8 @@ function registerCoursesCommand(program) {
|
|
|
100
61
|
.argument("<course-id>", "Course ID")
|
|
101
62
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
102
63
|
.action(async (courseId, options, command) => {
|
|
103
|
-
const output = getOutputFormat(command);
|
|
104
|
-
const apiContext = await createApiContext(options, command);
|
|
64
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
65
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
105
66
|
if (!apiContext) {
|
|
106
67
|
process.exitCode = 1;
|
|
107
68
|
return;
|
|
@@ -309,8 +270,8 @@ function registerCoursesCommand(program) {
|
|
|
309
270
|
.argument("<course-id>", "Course ID")
|
|
310
271
|
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
311
272
|
.action(async (courseId, options, command) => {
|
|
312
|
-
const output = getOutputFormat(command);
|
|
313
|
-
const apiContext = await createApiContext(options, command);
|
|
273
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
274
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
314
275
|
if (!apiContext) {
|
|
315
276
|
process.exitCode = 1;
|
|
316
277
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forums.d.ts","sourceRoot":"","sources":["../../../src/src/commands/forums.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"forums.d.ts","sourceRoot":"","sources":["../../../src/src/commands/forums.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+O5D"}
|