@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
|
@@ -0,0 +1,269 @@
|
|
|
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 () {
|
|
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
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.registerAssignmentsCommand = registerAssignmentsCommand;
|
|
40
|
+
const utils_js_1 = require("../lib/utils.js");
|
|
41
|
+
const moodle_js_1 = require("../lib/moodle.js");
|
|
42
|
+
const auth_js_1 = require("../lib/auth.js");
|
|
43
|
+
const index_js_1 = require("../index.js");
|
|
44
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
45
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
46
|
+
function registerAssignmentsCommand(program) {
|
|
47
|
+
const assignmentsCmd = program.command("assignments");
|
|
48
|
+
assignmentsCmd.description("Assignment operations");
|
|
49
|
+
assignmentsCmd
|
|
50
|
+
.command("list")
|
|
51
|
+
.description("List assignments in a course")
|
|
52
|
+
.argument("<course-id>", "Course ID")
|
|
53
|
+
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
54
|
+
.action(async (courseId, options, command) => {
|
|
55
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
56
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
57
|
+
if (!apiContext) {
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const apiAssignments = await (0, moodle_js_1.getAssignmentsByCoursesApi)(apiContext.session, [parseInt(courseId, 10)]);
|
|
62
|
+
const assignments = apiAssignments.map(a => ({
|
|
63
|
+
id: a.id,
|
|
64
|
+
courseName: courseId,
|
|
65
|
+
name: a.name,
|
|
66
|
+
url: a.url,
|
|
67
|
+
cmid: a.cmid,
|
|
68
|
+
duedate: (0, utils_js_1.formatMoodleDate)(a.duedate),
|
|
69
|
+
cutoffdate: (0, utils_js_1.formatMoodleDate)(a.cutoffdate),
|
|
70
|
+
allowSubmissionsFromDate: (0, utils_js_1.formatMoodleDate)(a.allowSubmissionsFromDate),
|
|
71
|
+
}));
|
|
72
|
+
apiContext.log.info(`\n找到 ${assignments.length} 個作業。`);
|
|
73
|
+
(0, index_js_1.formatAndOutput)(assignments, output, apiContext.log);
|
|
74
|
+
});
|
|
75
|
+
assignmentsCmd
|
|
76
|
+
.command("list-all")
|
|
77
|
+
.description("List all assignments across all courses")
|
|
78
|
+
.option("--level <type>", "Course level: in_progress (default) | all", "in_progress")
|
|
79
|
+
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
80
|
+
.action(async (options, command) => {
|
|
81
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
82
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
83
|
+
if (!apiContext) {
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const classification = options.level === "all" ? undefined : "inprogress";
|
|
88
|
+
const courses = await (0, moodle_js_1.getEnrolledCoursesApi)(apiContext.session, {
|
|
89
|
+
classification,
|
|
90
|
+
});
|
|
91
|
+
// Get assignments via WS API (no browser needed!)
|
|
92
|
+
const courseIds = courses.map(c => c.id);
|
|
93
|
+
const apiAssignments = await (0, moodle_js_1.getAssignmentsByCoursesApi)(apiContext.session, courseIds);
|
|
94
|
+
// Build a map of courseId -> course for quick lookup
|
|
95
|
+
const courseMap = new Map(courses.map(c => [c.id, c]));
|
|
96
|
+
const allAssignments = [];
|
|
97
|
+
for (const a of apiAssignments) {
|
|
98
|
+
const course = courseMap.get(a.courseId);
|
|
99
|
+
if (course) {
|
|
100
|
+
allAssignments.push({
|
|
101
|
+
id: a.id,
|
|
102
|
+
courseName: course.fullname,
|
|
103
|
+
name: a.name,
|
|
104
|
+
url: a.url,
|
|
105
|
+
cmid: a.cmid,
|
|
106
|
+
duedate: (0, utils_js_1.formatMoodleDate)(a.duedate),
|
|
107
|
+
cutoffdate: (0, utils_js_1.formatMoodleDate)(a.cutoffdate),
|
|
108
|
+
allowSubmissionsFromDate: (0, utils_js_1.formatMoodleDate)(a.allowSubmissionsFromDate),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
apiContext.log.info(`\n總計發現 ${allAssignments.length} 個作業。`);
|
|
113
|
+
(0, index_js_1.formatAndOutput)(allAssignments, output, apiContext.log);
|
|
114
|
+
});
|
|
115
|
+
// ── Submission Status ───────────────────────────────────────────────────────
|
|
116
|
+
assignmentsCmd
|
|
117
|
+
.command("status")
|
|
118
|
+
.description("Check assignment submission status")
|
|
119
|
+
.argument("<assignment-id>", "Assignment instance ID (from list-all)")
|
|
120
|
+
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
121
|
+
.action(async (assignmentId, options, command) => {
|
|
122
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
123
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
124
|
+
if (!apiContext) {
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const id = parseInt(assignmentId, 10);
|
|
129
|
+
apiContext.log.info("檢查繳交狀態...");
|
|
130
|
+
const status = await (0, moodle_js_1.getSubmissionStatusApi)(apiContext.session, id);
|
|
131
|
+
// Build status data object
|
|
132
|
+
const statusData = {
|
|
133
|
+
submitted: status.submitted,
|
|
134
|
+
submitted_text: status.submitted ? "已繳交" : "尚未繳交",
|
|
135
|
+
graded: status.graded,
|
|
136
|
+
graded_text: status.graded ? "已評分" : "尚未評分",
|
|
137
|
+
last_modified: status.lastModified ? new Date(status.lastModified * 1000).toISOString() : null,
|
|
138
|
+
last_modified_text: status.lastModified ? new Date(status.lastModified * 1000).toLocaleString("zh-TW") : null,
|
|
139
|
+
grader: status.grader,
|
|
140
|
+
grade: status.grade,
|
|
141
|
+
feedback: status.feedback,
|
|
142
|
+
files: status.extensions.map(f => ({
|
|
143
|
+
filename: f.filename,
|
|
144
|
+
filesize: f.filesize,
|
|
145
|
+
filesize_kb: (0, utils_js_1.formatFileSize)(f.filesize),
|
|
146
|
+
})),
|
|
147
|
+
};
|
|
148
|
+
(0, index_js_1.formatAndOutput)(statusData, output, apiContext.log);
|
|
149
|
+
});
|
|
150
|
+
// ── Submit Assignment ────────────────────────────────────────────────────────
|
|
151
|
+
assignmentsCmd
|
|
152
|
+
.command("submit")
|
|
153
|
+
.description("Submit an assignment (online text or file)")
|
|
154
|
+
.argument("<assignment-id>", "Assignment instance ID (from list-all)")
|
|
155
|
+
.option("--text <content>", "Online text content to submit")
|
|
156
|
+
.option("--file-id <id>", "Draft file ID from file upload")
|
|
157
|
+
.option("--file <path>", "Upload and submit a file directly")
|
|
158
|
+
.option("--output <format>", "Output format: json|csv|table|silent")
|
|
159
|
+
.action(async (assignmentId, options, command) => {
|
|
160
|
+
const output = (0, utils_js_1.getOutputFormat)(command);
|
|
161
|
+
const apiContext = await (0, auth_js_1.createApiContext)(options, command);
|
|
162
|
+
if (!apiContext) {
|
|
163
|
+
process.exitCode = 1;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const id = parseInt(assignmentId, 10);
|
|
167
|
+
// Check submission status first
|
|
168
|
+
const status = await (0, moodle_js_1.getSubmissionStatusApi)(apiContext.session, id);
|
|
169
|
+
let fileUploaded;
|
|
170
|
+
let cancelled = false;
|
|
171
|
+
if (status.submitted) {
|
|
172
|
+
const confirm = await promptConfirm("此作業已經繳交!確定要重新繳交嗎?(y/N): ");
|
|
173
|
+
if (!confirm) {
|
|
174
|
+
cancelled = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (cancelled) {
|
|
178
|
+
const cancelResult = {
|
|
179
|
+
success: false,
|
|
180
|
+
cancelled: true,
|
|
181
|
+
message: "Submission cancelled by user",
|
|
182
|
+
};
|
|
183
|
+
(0, index_js_1.formatAndOutput)(cancelResult, output, apiContext.log);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
// Validate options
|
|
187
|
+
if (!options.text && !options.fileId && !options.file) {
|
|
188
|
+
const errorResult = {
|
|
189
|
+
success: false,
|
|
190
|
+
error: "請提供 --text、--file-id 或 --file 選項。",
|
|
191
|
+
};
|
|
192
|
+
(0, index_js_1.formatAndOutput)(errorResult, output, apiContext.log);
|
|
193
|
+
process.exitCode = 1;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
let fileId = options.fileId ? parseInt(options.fileId, 10) : undefined;
|
|
197
|
+
// Upload file if --file option is provided
|
|
198
|
+
if (options.file) {
|
|
199
|
+
const resolvedPath = node_path_1.default.resolve(options.file);
|
|
200
|
+
// Check if file exists
|
|
201
|
+
try {
|
|
202
|
+
await promises_1.default.access(resolvedPath);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
const errorResult = {
|
|
206
|
+
success: false,
|
|
207
|
+
error: `檔案不存在: ${options.file}`,
|
|
208
|
+
};
|
|
209
|
+
(0, index_js_1.formatAndOutput)(errorResult, output, apiContext.log);
|
|
210
|
+
process.exitCode = 1;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const stats = await promises_1.default.stat(resolvedPath);
|
|
214
|
+
const fileSizeKB = (0, utils_js_1.formatFileSize)(stats.size);
|
|
215
|
+
const uploadResult = await (0, moodle_js_1.uploadFileApi)(apiContext.session, resolvedPath);
|
|
216
|
+
if (!uploadResult.success) {
|
|
217
|
+
const errorResult = {
|
|
218
|
+
success: false,
|
|
219
|
+
error: `檔案上傳失敗: ${uploadResult.error}`,
|
|
220
|
+
};
|
|
221
|
+
(0, index_js_1.formatAndOutput)(errorResult, output, apiContext.log);
|
|
222
|
+
process.exitCode = 1;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
fileId = uploadResult.draftId;
|
|
226
|
+
fileUploaded = {
|
|
227
|
+
filename: node_path_1.default.basename(resolvedPath),
|
|
228
|
+
filesize: stats.size,
|
|
229
|
+
filesize_kb: (0, utils_js_1.formatFileSize)(stats.size),
|
|
230
|
+
draft_id: fileId,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
// Submit
|
|
234
|
+
const result = await (0, moodle_js_1.saveSubmissionApi)(apiContext.session, id, {
|
|
235
|
+
onlineText: options.text ? { text: options.text } : undefined,
|
|
236
|
+
fileId: fileId,
|
|
237
|
+
});
|
|
238
|
+
const submitResult = {
|
|
239
|
+
success: result.success,
|
|
240
|
+
assignment_id: id,
|
|
241
|
+
submitted: !!result.success,
|
|
242
|
+
online_text: !!options.text,
|
|
243
|
+
file_uploaded: fileUploaded,
|
|
244
|
+
file_id: fileId ?? null,
|
|
245
|
+
error: result.success ? undefined : result.error,
|
|
246
|
+
message: result.success ? "Assignment submitted successfully" : result.error,
|
|
247
|
+
};
|
|
248
|
+
(0, index_js_1.formatAndOutput)(submitResult, output, apiContext.log);
|
|
249
|
+
if (!result.success) {
|
|
250
|
+
process.exitCode = 1;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Prompt user for yes/no confirmation.
|
|
256
|
+
*/
|
|
257
|
+
async function promptConfirm(prompt) {
|
|
258
|
+
const readline = await Promise.resolve().then(() => __importStar(require("node:readline")));
|
|
259
|
+
const rl = readline.createInterface({
|
|
260
|
+
input: process.stdin,
|
|
261
|
+
output: process.stdout,
|
|
262
|
+
});
|
|
263
|
+
return new Promise((resolve) => {
|
|
264
|
+
rl.question(prompt, (answer) => {
|
|
265
|
+
rl.close();
|
|
266
|
+
resolve(/^y/i.test(answer));
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../src/src/commands/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkStD"}
|
|
@@ -9,6 +9,7 @@ const playwright_core_1 = require("playwright-core");
|
|
|
9
9
|
const logger_js_1 = require("../lib/logger.js");
|
|
10
10
|
const auth_js_1 = require("../lib/auth.js");
|
|
11
11
|
const token_js_1 = require("../lib/token.js");
|
|
12
|
+
const moodle_js_1 = require("../lib/moodle.js");
|
|
12
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
14
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
15
|
function registerCommand(program) {
|
|
@@ -134,31 +135,41 @@ function registerCommand(program) {
|
|
|
134
135
|
catch {
|
|
135
136
|
// Ignore sesskey extraction errors
|
|
136
137
|
}
|
|
137
|
-
//
|
|
138
|
+
// Acquire WS token
|
|
139
|
+
let wsToken;
|
|
138
140
|
try {
|
|
139
|
-
|
|
140
|
-
username: "",
|
|
141
|
-
password: "",
|
|
142
|
-
courseUrl: "",
|
|
143
|
-
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
144
|
-
headless: false,
|
|
145
|
-
slowMo: 0,
|
|
146
|
-
authStatePath: sessionPath,
|
|
147
|
-
ollamaBaseUrl: "",
|
|
148
|
-
};
|
|
149
|
-
const wsToken = await (0, token_js_1.acquireWsToken)(page, config, log);
|
|
141
|
+
wsToken = await (0, token_js_1.acquireWsToken)(page, { moodleBaseUrl: "https://ilearning.cycu.edu.tw" }, log);
|
|
150
142
|
(0, token_js_1.saveWsToken)(sessionPath, wsToken);
|
|
151
143
|
}
|
|
152
144
|
catch {
|
|
153
145
|
// WS token is optional, ignore errors
|
|
154
146
|
}
|
|
155
147
|
const stats = node_fs_1.default.statSync(sessionPath);
|
|
148
|
+
// Get user info via WS API
|
|
149
|
+
let user;
|
|
150
|
+
try {
|
|
151
|
+
if (wsToken) {
|
|
152
|
+
const siteInfo = await (0, moodle_js_1.getSiteInfoApi)({
|
|
153
|
+
wsToken,
|
|
154
|
+
moodleBaseUrl: "https://ilearning.cycu.edu.tw",
|
|
155
|
+
});
|
|
156
|
+
user = {
|
|
157
|
+
userid: siteInfo.userid,
|
|
158
|
+
username: siteInfo.username,
|
|
159
|
+
fullname: siteInfo.fullname,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Ignore
|
|
165
|
+
}
|
|
156
166
|
const result = {
|
|
157
167
|
status: "success",
|
|
158
168
|
message: "Login successful",
|
|
159
169
|
session_path: sessionPath,
|
|
160
170
|
session_size: stats.size,
|
|
161
|
-
updated: true
|
|
171
|
+
updated: true,
|
|
172
|
+
...(user ? { user } : {}),
|
|
162
173
|
};
|
|
163
174
|
console.log(JSON.stringify(result, null, 2));
|
|
164
175
|
}
|
|
@@ -217,6 +228,25 @@ function registerCommand(program) {
|
|
|
217
228
|
exists: false
|
|
218
229
|
}
|
|
219
230
|
};
|
|
231
|
+
// Try to get user info from WS API
|
|
232
|
+
try {
|
|
233
|
+
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
234
|
+
if (wsToken) {
|
|
235
|
+
const session = {
|
|
236
|
+
wsToken,
|
|
237
|
+
moodleBaseUrl: "https://ilearning.cycu.edu.tw"
|
|
238
|
+
};
|
|
239
|
+
const siteInfo = await (0, moodle_js_1.getSiteInfoApi)(session);
|
|
240
|
+
result.user = {
|
|
241
|
+
userid: siteInfo.userid,
|
|
242
|
+
username: siteInfo.username,
|
|
243
|
+
fullname: siteInfo.fullname
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
// WS token might not be available or expired, skip user info
|
|
249
|
+
}
|
|
220
250
|
console.log(JSON.stringify(result, null, 2));
|
|
221
251
|
}
|
|
222
252
|
catch {
|
|
@@ -233,7 +263,7 @@ function registerCommand(program) {
|
|
|
233
263
|
status: "error",
|
|
234
264
|
error: "Session not found",
|
|
235
265
|
session_path: sessionPath,
|
|
236
|
-
hint: "Run 'openape
|
|
266
|
+
hint: "Run 'openape login' first"
|
|
237
267
|
};
|
|
238
268
|
console.log(JSON.stringify(result, null, 2));
|
|
239
269
|
}
|
|
@@ -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;AAQpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/src/commands/calendar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmM9D"}
|
|
@@ -23,19 +23,19 @@ function registerCalendarCommand(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 {
|
|
@@ -97,29 +97,28 @@ function registerCalendarCommand(program) {
|
|
|
97
97
|
if (options.upcoming) {
|
|
98
98
|
filteredEvents = allEvents.filter(e => e.timestart > now);
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
console.log(JSON.stringify({
|
|
101
101
|
status: "success",
|
|
102
102
|
timestamp: new Date().toISOString(),
|
|
103
|
-
|
|
103
|
+
total_events: allEvents.length,
|
|
104
|
+
upcoming: allEvents.filter(e => e.timestart > now).length,
|
|
105
|
+
by_type: allEvents.reduce((acc, e) => {
|
|
106
|
+
acc[e.eventtype] = (acc[e.eventtype] || 0) + 1;
|
|
107
|
+
return acc;
|
|
108
|
+
}, {}),
|
|
109
|
+
}));
|
|
110
|
+
for (const e of filteredEvents) {
|
|
111
|
+
console.log(JSON.stringify({
|
|
104
112
|
id: e.id,
|
|
105
113
|
name: e.name,
|
|
106
114
|
description: e.description,
|
|
107
115
|
course_id: e.courseid,
|
|
108
116
|
event_type: e.eventtype,
|
|
109
|
-
start_time:
|
|
110
|
-
end_time: e.timeduration ?
|
|
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,
|
|
111
119
|
location: e.location,
|
|
112
|
-
}))
|
|
113
|
-
|
|
114
|
-
total_events: allEvents.length,
|
|
115
|
-
upcoming: allEvents.filter(e => e.timestart > now).length,
|
|
116
|
-
by_type: allEvents.reduce((acc, e) => {
|
|
117
|
-
acc[e.eventtype] = (acc[e.eventtype] || 0) + 1;
|
|
118
|
-
return acc;
|
|
119
|
-
}, {}),
|
|
120
|
-
},
|
|
121
|
-
};
|
|
122
|
-
console.log(JSON.stringify(output));
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
123
122
|
});
|
|
124
123
|
calendarCmd
|
|
125
124
|
.command("export")
|
|
@@ -167,8 +166,8 @@ function registerCalendarCommand(program) {
|
|
|
167
166
|
description: e.description,
|
|
168
167
|
course_id: e.courseid,
|
|
169
168
|
event_type: e.eventtype,
|
|
170
|
-
start_time:
|
|
171
|
-
end_time: e.timeduration ?
|
|
169
|
+
start_time: (0, utils_js_1.formatTimestamp)(e.timestart),
|
|
170
|
+
end_time: e.timeduration ? (0, utils_js_1.formatTimestamp)(e.timestart + Math.floor(e.timeduration / 1000)) : null,
|
|
172
171
|
location: e.location,
|
|
173
172
|
})),
|
|
174
173
|
summary: {
|
|
@@ -24,19 +24,19 @@ function registerCoursesCommand(program) {
|
|
|
24
24
|
const opts = command?.optsWithGlobals ? command.optsWithGlobals() : options;
|
|
25
25
|
const outputFormat = getOutputFormat(command || { optsWithGlobals: () => ({ output: "json" }) });
|
|
26
26
|
const silent = outputFormat === "json" && !opts.verbose;
|
|
27
|
-
const log = (0, logger_js_1.createLogger)(opts.verbose, silent);
|
|
27
|
+
const log = (0, logger_js_1.createLogger)(opts.verbose, silent, outputFormat);
|
|
28
28
|
const baseDir = (0, utils_js_1.getBaseDir)();
|
|
29
29
|
const sessionPath = node_path_1.default.resolve(baseDir, ".auth", "storage-state.json");
|
|
30
30
|
// Check if session exists
|
|
31
31
|
if (!node_fs_1.default.existsSync(sessionPath)) {
|
|
32
|
-
|
|
32
|
+
console.error("未找到登入 session。請先執行 'openape login' 進行登入。");
|
|
33
33
|
log.info(`Session 預期位置: ${sessionPath}`);
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
36
|
// Try to load WS token
|
|
37
37
|
const wsToken = (0, token_js_1.loadWsToken)(sessionPath);
|
|
38
38
|
if (!wsToken) {
|
|
39
|
-
|
|
39
|
+
console.error("未找到 WS token。請先執行 'openape login' 進行登入。");
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
return {
|
|
@@ -117,8 +117,8 @@ function registerCoursesCommand(program) {
|
|
|
117
117
|
courseId: course.id,
|
|
118
118
|
courseName: course.fullname,
|
|
119
119
|
progress: course.progress ?? 0,
|
|
120
|
-
startDate: course.startdate ?
|
|
121
|
-
endDate: course.enddate ?
|
|
120
|
+
startDate: course.startdate ? (0, utils_js_1.formatTimestamp)(course.startdate) : null,
|
|
121
|
+
endDate: course.enddate ? (0, utils_js_1.formatTimestamp)(course.enddate) : null,
|
|
122
122
|
};
|
|
123
123
|
(0, index_js_1.formatAndOutput)(progressData, output, apiContext.log);
|
|
124
124
|
});
|
|
@@ -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;AAmBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkX5D"}
|