@robertying/thu-learn-lib 0.0.0-stage → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +324 -2
- package/lib/auth.d.ts +11 -0
- package/lib/auth.d.ts.map +1 -0
- package/lib/auth.js +99 -0
- package/lib/auth.js.map +1 -0
- package/lib/calendar.d.ts +16 -0
- package/lib/calendar.d.ts.map +1 -0
- package/lib/calendar.js +81 -0
- package/lib/calendar.js.map +1 -0
- package/lib/contents.d.ts +9 -0
- package/lib/contents.d.ts.map +1 -0
- package/lib/contents.js +52 -0
- package/lib/contents.js.map +1 -0
- package/lib/courses.d.ts +9 -0
- package/lib/courses.d.ts.map +1 -0
- package/lib/courses.js +82 -0
- package/lib/courses.js.map +1 -0
- package/lib/discussions.d.ts +11 -0
- package/lib/discussions.d.ts.map +1 -0
- package/lib/discussions.js +56 -0
- package/lib/discussions.js.map +1 -0
- package/lib/favorites.d.ts +35 -0
- package/lib/favorites.d.ts.map +1 -0
- package/lib/favorites.js +150 -0
- package/lib/favorites.js.map +1 -0
- package/lib/files.d.ts +13 -0
- package/lib/files.d.ts.map +1 -0
- package/lib/files.js +167 -0
- package/lib/files.js.map +1 -0
- package/lib/homework.d.ts +9 -0
- package/lib/homework.d.ts.map +1 -0
- package/lib/homework.js +193 -0
- package/lib/homework.js.map +1 -0
- package/lib/index.d.ts +105 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +181 -0
- package/lib/index.js.map +1 -0
- package/lib/notices.d.ts +6 -0
- package/lib/notices.d.ts.map +1 -0
- package/lib/notices.js +84 -0
- package/lib/notices.js.map +1 -0
- package/lib/questionnaires.d.ts +7 -0
- package/lib/questionnaires.d.ts.map +1 -0
- package/lib/questionnaires.js +62 -0
- package/lib/questionnaires.js.map +1 -0
- package/lib/request.d.ts +31 -0
- package/lib/request.d.ts.map +1 -0
- package/lib/request.js +90 -0
- package/lib/request.js.map +1 -0
- package/lib/types.d.ts +392 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +97 -0
- package/lib/types.js.map +1 -0
- package/lib/urls.d.ts +78 -0
- package/lib/urls.d.ts.map +1 -0
- package/lib/urls.js +160 -0
- package/lib/urls.js.map +1 -0
- package/lib/user.d.ts +7 -0
- package/lib/user.d.ts.map +1 -0
- package/lib/user.js +22 -0
- package/lib/user.js.map +1 -0
- package/lib/utils.d.ts +13 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +115 -0
- package/lib/utils.js.map +1 -0
- package/package.json +67 -4
- package/src/auth.ts +121 -0
- package/src/calendar.ts +102 -0
- package/src/contents.ts +67 -0
- package/src/courses.ts +97 -0
- package/src/discussions.ts +80 -0
- package/src/favorites.ts +167 -0
- package/src/files.ts +197 -0
- package/src/homework.ts +279 -0
- package/src/index.ts +282 -0
- package/src/notices.ts +110 -0
- package/src/questionnaires.ts +76 -0
- package/src/request.ts +101 -0
- package/src/types.ts +445 -0
- package/src/urls.ts +257 -0
- package/src/user.ts +28 -0
- package/src/utils.ts +120 -0
package/src/files.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Course files (课程文件) and file categories.
|
|
2
|
+
|
|
3
|
+
import type { Session } from './request.ts';
|
|
4
|
+
import type { File, FileCategory } from './types.ts';
|
|
5
|
+
import { ContentType, CourseType, FailReason } from './types.ts';
|
|
6
|
+
import * as URLS from './urls.ts';
|
|
7
|
+
import { decodeHTML, formatFileSize } from './utils.ts';
|
|
8
|
+
|
|
9
|
+
/** Get all files (课程文件) of the specified course. */
|
|
10
|
+
export async function getFileList(
|
|
11
|
+
s: Session,
|
|
12
|
+
courseID: string,
|
|
13
|
+
courseType: CourseType = CourseType.STUDENT,
|
|
14
|
+
): Promise<File[]> {
|
|
15
|
+
const json = await (await s.fetchWithToken(URLS.LEARN_FILE_LIST(courseID, courseType))).json();
|
|
16
|
+
if (json.result !== 'success') {
|
|
17
|
+
return Promise.reject({
|
|
18
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
19
|
+
extra: json,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let result: any[] = [];
|
|
24
|
+
if (Array.isArray(json.object?.resultsList)) {
|
|
25
|
+
// teacher
|
|
26
|
+
result = json.object.resultsList;
|
|
27
|
+
} else if (Array.isArray(json.object)) {
|
|
28
|
+
// student
|
|
29
|
+
result = json.object;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const categories = new Map((await getFileCategoryList(s, courseID, courseType)).map((c) => [c.id, c]));
|
|
33
|
+
|
|
34
|
+
return result.map((f) => {
|
|
35
|
+
const title = decodeHTML(f.bt);
|
|
36
|
+
const fileId = f.wjid;
|
|
37
|
+
const uploadTime = f.scsj;
|
|
38
|
+
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, courseType);
|
|
39
|
+
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, courseType, s.previewFirstPage);
|
|
40
|
+
return {
|
|
41
|
+
id: f.kjxxid,
|
|
42
|
+
fileId,
|
|
43
|
+
category: categories.get(f.kjflid),
|
|
44
|
+
title,
|
|
45
|
+
description: decodeHTML(f.ms),
|
|
46
|
+
rawSize: f.wjdx,
|
|
47
|
+
size: f.fileSize,
|
|
48
|
+
uploadTime,
|
|
49
|
+
publishTime: uploadTime,
|
|
50
|
+
downloadUrl,
|
|
51
|
+
previewUrl,
|
|
52
|
+
isNew: f.isNew ?? false,
|
|
53
|
+
markedImportant: f.sfqd === 1,
|
|
54
|
+
visitCount: f.xsllcs ?? f.llcs ?? 0,
|
|
55
|
+
downloadCount: f.xzcs ?? 0,
|
|
56
|
+
fileType: f.wjlx,
|
|
57
|
+
remoteFile: {
|
|
58
|
+
id: fileId,
|
|
59
|
+
name: title,
|
|
60
|
+
downloadUrl,
|
|
61
|
+
previewUrl,
|
|
62
|
+
size: f.fileSize,
|
|
63
|
+
},
|
|
64
|
+
} satisfies File;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Get file categories of the specified course. */
|
|
69
|
+
export async function getFileCategoryList(
|
|
70
|
+
s: Session,
|
|
71
|
+
courseID: string,
|
|
72
|
+
courseType: CourseType = CourseType.STUDENT,
|
|
73
|
+
): Promise<FileCategory[]> {
|
|
74
|
+
const json = await (await s.fetchWithToken(URLS.LEARN_FILE_CATEGORY_LIST(courseID, courseType))).json();
|
|
75
|
+
if (json.result !== 'success') {
|
|
76
|
+
return Promise.reject({
|
|
77
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
78
|
+
extra: json,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = (json.object?.rows ?? []) as any[];
|
|
83
|
+
|
|
84
|
+
return result.map(
|
|
85
|
+
(c) =>
|
|
86
|
+
({
|
|
87
|
+
id: c.kjflid,
|
|
88
|
+
title: decodeHTML(c.bt),
|
|
89
|
+
creationTime: c.czsj,
|
|
90
|
+
}) satisfies FileCategory,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get all files of the specified category of the specified course.
|
|
96
|
+
* Note: this cannot get correct `visitCount` and `downloadCount` for student
|
|
97
|
+
*/
|
|
98
|
+
export async function getFileListByCategory(
|
|
99
|
+
s: Session,
|
|
100
|
+
courseID: string,
|
|
101
|
+
categoryId: string,
|
|
102
|
+
courseType: CourseType = CourseType.STUDENT,
|
|
103
|
+
): Promise<File[]> {
|
|
104
|
+
if (courseType === CourseType.STUDENT) {
|
|
105
|
+
const json = await (await s.fetchWithToken(URLS.LEARN_FILE_LIST_BY_CATEGORY_STUDENT(courseID, categoryId))).json();
|
|
106
|
+
if (json.result !== 'success') {
|
|
107
|
+
return Promise.reject({
|
|
108
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
109
|
+
extra: json,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const result = (json.object ?? []) as any[];
|
|
114
|
+
|
|
115
|
+
return result.map((f) => {
|
|
116
|
+
const fileId = f[7];
|
|
117
|
+
const title = decodeHTML(f[1]);
|
|
118
|
+
const rawSize = f[9];
|
|
119
|
+
const size = formatFileSize(rawSize);
|
|
120
|
+
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, courseType);
|
|
121
|
+
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, courseType, s.previewFirstPage);
|
|
122
|
+
return {
|
|
123
|
+
id: f[0],
|
|
124
|
+
fileId,
|
|
125
|
+
title,
|
|
126
|
+
description: decodeHTML(f[5]),
|
|
127
|
+
rawSize,
|
|
128
|
+
size,
|
|
129
|
+
uploadTime: f[6],
|
|
130
|
+
publishTime: f[10],
|
|
131
|
+
downloadUrl,
|
|
132
|
+
previewUrl,
|
|
133
|
+
isNew: f[8] === 1,
|
|
134
|
+
markedImportant: f[2] === 1,
|
|
135
|
+
visitCount: 0,
|
|
136
|
+
downloadCount: 0,
|
|
137
|
+
fileType: f[13],
|
|
138
|
+
remoteFile: {
|
|
139
|
+
id: fileId,
|
|
140
|
+
name: title,
|
|
141
|
+
downloadUrl,
|
|
142
|
+
previewUrl,
|
|
143
|
+
size,
|
|
144
|
+
},
|
|
145
|
+
isFavorite: f[11],
|
|
146
|
+
comment: f[14] ?? undefined,
|
|
147
|
+
} satisfies File;
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
const json = await (
|
|
151
|
+
await s.fetchWithToken(URLS.LEARN_FILE_LIST_BY_CATEGORY_TEACHER, {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
body: URLS.LEARN_FILE_LIST_BY_CATEGORY_TEACHER_FORM_DATA(courseID, categoryId),
|
|
154
|
+
})
|
|
155
|
+
).json();
|
|
156
|
+
if (json.result !== 'success') {
|
|
157
|
+
return Promise.reject({
|
|
158
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
159
|
+
extra: json,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const result = (json.object.aaData ?? []) as any[];
|
|
164
|
+
|
|
165
|
+
return result.map((f) => {
|
|
166
|
+
const title = decodeHTML(f.bt);
|
|
167
|
+
const fileId = f.wjid;
|
|
168
|
+
const uploadTime = f.scsj;
|
|
169
|
+
const downloadUrl = URLS.LEARN_FILE_DOWNLOAD(fileId, courseType);
|
|
170
|
+
const previewUrl = URLS.LEARN_FILE_PREVIEW(ContentType.FILE, fileId, courseType, s.previewFirstPage);
|
|
171
|
+
return {
|
|
172
|
+
id: f.kjxxid,
|
|
173
|
+
fileId,
|
|
174
|
+
title,
|
|
175
|
+
description: decodeHTML(f.ms),
|
|
176
|
+
rawSize: f.wjdx,
|
|
177
|
+
size: f.fileSize,
|
|
178
|
+
uploadTime,
|
|
179
|
+
publishTime: uploadTime,
|
|
180
|
+
downloadUrl,
|
|
181
|
+
previewUrl,
|
|
182
|
+
isNew: f.isNew ?? false,
|
|
183
|
+
markedImportant: f.sfqd === 1,
|
|
184
|
+
visitCount: f.xsllcs ?? f.llcs ?? 0,
|
|
185
|
+
downloadCount: f.xzcs ?? 0,
|
|
186
|
+
fileType: f.wjlx,
|
|
187
|
+
remoteFile: {
|
|
188
|
+
id: fileId,
|
|
189
|
+
name: title,
|
|
190
|
+
downloadUrl,
|
|
191
|
+
previewUrl,
|
|
192
|
+
size: f.fileSize,
|
|
193
|
+
},
|
|
194
|
+
} satisfies File;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
package/src/homework.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Homework (课程作业), excellent homework and homework submission.
|
|
2
|
+
|
|
3
|
+
import type { Element } from 'cheerio';
|
|
4
|
+
import { Base64 } from 'js-base64';
|
|
5
|
+
|
|
6
|
+
import type { Session } from './request.ts';
|
|
7
|
+
import { $, YES } from './request.ts';
|
|
8
|
+
import type {
|
|
9
|
+
ApiError,
|
|
10
|
+
ExcellentHomework,
|
|
11
|
+
Homework,
|
|
12
|
+
HomeworkTA,
|
|
13
|
+
IExcellentHomework,
|
|
14
|
+
IHomework,
|
|
15
|
+
IHomeworkDetail,
|
|
16
|
+
IHomeworkStatus,
|
|
17
|
+
IHomeworkSubmitAttachment,
|
|
18
|
+
RemoteFile,
|
|
19
|
+
} from './types.ts';
|
|
20
|
+
import { ContentType, CourseType, FailReason } from './types.ts';
|
|
21
|
+
import * as URLS from './urls.ts';
|
|
22
|
+
import { GRADE_LEVEL_MAP, decodeHTML, trimAndDefine } from './utils.ts';
|
|
23
|
+
|
|
24
|
+
/** Get all homeworks (课程作业) of the specified course. */
|
|
25
|
+
export function getHomeworkList(s: Session, courseID: string): Promise<Homework[]>;
|
|
26
|
+
|
|
27
|
+
export function getHomeworkList(s: Session, courseID: string, courseType: CourseType.STUDENT): Promise<Homework[]>;
|
|
28
|
+
|
|
29
|
+
export function getHomeworkList(s: Session, courseID: string, courseType: CourseType.TEACHER): Promise<HomeworkTA[]>;
|
|
30
|
+
|
|
31
|
+
export async function getHomeworkList(
|
|
32
|
+
s: Session,
|
|
33
|
+
courseID: string,
|
|
34
|
+
courseType: CourseType = CourseType.STUDENT,
|
|
35
|
+
): Promise<Homework[] | HomeworkTA[]> {
|
|
36
|
+
if (courseType === CourseType.TEACHER) {
|
|
37
|
+
const json = await (
|
|
38
|
+
await s.fetchWithToken(URLS.LEARN_HOMEWORK_LIST_TEACHER, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: URLS.LEARN_PAGE_LIST_FORM_DATA(courseID),
|
|
41
|
+
})
|
|
42
|
+
).json();
|
|
43
|
+
if (json.result !== 'success') {
|
|
44
|
+
return Promise.reject({
|
|
45
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
46
|
+
extra: json,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = (json.object?.aaData ?? []) as any[];
|
|
51
|
+
|
|
52
|
+
return result.map(
|
|
53
|
+
(d) =>
|
|
54
|
+
({
|
|
55
|
+
id: d.zyid,
|
|
56
|
+
index: d.wz,
|
|
57
|
+
title: decodeHTML(d.bt),
|
|
58
|
+
description: decodeHTML(Base64.decode(d.nr)),
|
|
59
|
+
publisherId: d.fbr,
|
|
60
|
+
publishTime: d.fbsj,
|
|
61
|
+
startTime: d.kssj,
|
|
62
|
+
deadline: d.jzsj,
|
|
63
|
+
lateSubmissionDeadline: d.bjjzsj ?? undefined,
|
|
64
|
+
url: URLS.LEARN_HOMEWORK_DETAIL_TEACHER(courseID, d.zyid),
|
|
65
|
+
completionType: d.zywcfs,
|
|
66
|
+
submissionType: d.zytjfs,
|
|
67
|
+
gradedCount: d.ypys,
|
|
68
|
+
submittedCount: d.yjs,
|
|
69
|
+
unsubmittedCount: d.wjs,
|
|
70
|
+
}) satisfies HomeworkTA,
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
return Promise.all(
|
|
74
|
+
URLS.LEARN_HOMEWORK_LIST_SOURCE.map((source) => getHomeworkListAtUrl(s, courseID, source.url, source.status)),
|
|
75
|
+
).then((r) => r.flat());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function getHomeworkListAtUrl(
|
|
80
|
+
s: Session,
|
|
81
|
+
courseID: string,
|
|
82
|
+
url: string,
|
|
83
|
+
status: IHomeworkStatus,
|
|
84
|
+
): Promise<Homework[]> {
|
|
85
|
+
const json = await (
|
|
86
|
+
await s.fetchWithToken(url, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
body: URLS.LEARN_PAGE_LIST_FORM_DATA(courseID),
|
|
89
|
+
})
|
|
90
|
+
).json();
|
|
91
|
+
if (json.result !== 'success') {
|
|
92
|
+
return Promise.reject({
|
|
93
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
94
|
+
extra: json,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = (json.object?.aaData ?? []) as any[];
|
|
99
|
+
|
|
100
|
+
let excellentHomeworkListByHomework: { [id: string]: ExcellentHomework[] } = {};
|
|
101
|
+
try {
|
|
102
|
+
excellentHomeworkListByHomework = await getExcellentHomeworkListByHomework(s, courseID);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// Don't block the whole process if excellent homework list cannot be fetched
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return Promise.all(
|
|
108
|
+
result
|
|
109
|
+
.map(
|
|
110
|
+
(h) =>
|
|
111
|
+
({
|
|
112
|
+
id: h.xszyid,
|
|
113
|
+
studentHomeworkId: h.xszyid,
|
|
114
|
+
baseId: h.zyid,
|
|
115
|
+
title: decodeHTML(h.bt),
|
|
116
|
+
url: URLS.LEARN_HOMEWORK_PAGE(h.wlkcid, h.xszyid),
|
|
117
|
+
deadline: h.jzsj,
|
|
118
|
+
lateSubmissionDeadline: h.bjjzsj ? h.bjjzsj : undefined,
|
|
119
|
+
isLateSubmission: h.sfbj === YES,
|
|
120
|
+
completionType: h.zywcfs,
|
|
121
|
+
submissionType: h.zytjfs,
|
|
122
|
+
submitUrl: URLS.LEARN_HOMEWORK_SUBMIT_PAGE(h.wlkcid, h.xszyid),
|
|
123
|
+
submitTime: h.scsj === null ? undefined : h.scsj,
|
|
124
|
+
grade: h.cj === null ? undefined : h.cj,
|
|
125
|
+
gradeLevel: GRADE_LEVEL_MAP.get(h.cj),
|
|
126
|
+
graderName: trimAndDefine(h.jsm),
|
|
127
|
+
gradeContent: trimAndDefine(h.pynr),
|
|
128
|
+
gradeTime: h.pysj === null ? undefined : h.pysj,
|
|
129
|
+
isFavorite: h.sfsc === YES,
|
|
130
|
+
favoriteTime: h.scsj === null || h.sfsc !== YES ? undefined : h.scsj,
|
|
131
|
+
comment: h.bznr ?? undefined,
|
|
132
|
+
excellentHomeworkList: excellentHomeworkListByHomework[h.zyid],
|
|
133
|
+
...status,
|
|
134
|
+
}) satisfies IHomework,
|
|
135
|
+
)
|
|
136
|
+
.map(
|
|
137
|
+
async (h) =>
|
|
138
|
+
({
|
|
139
|
+
...h,
|
|
140
|
+
...(await parseHomeworkAtUrl(s, h.url)),
|
|
141
|
+
...(await getHomeworkDetail(s, h.baseId)),
|
|
142
|
+
}) satisfies Homework,
|
|
143
|
+
),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function getExcellentHomeworkListByHomework(
|
|
148
|
+
s: Session,
|
|
149
|
+
courseID: string,
|
|
150
|
+
): Promise<{ [id: string]: ExcellentHomework[] }> {
|
|
151
|
+
const json = await (
|
|
152
|
+
await s.fetchWithToken(URLS.LEARN_HOMEWORK_LIST_EXCELLENT, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
body: URLS.LEARN_PAGE_LIST_FORM_DATA(courseID),
|
|
155
|
+
})
|
|
156
|
+
).json();
|
|
157
|
+
if (json.result !== 'success') {
|
|
158
|
+
return Promise.reject({
|
|
159
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
160
|
+
extra: json,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const result = (json.object?.aaData ?? []) as any[];
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
await Promise.all(
|
|
168
|
+
result
|
|
169
|
+
.map(
|
|
170
|
+
(h) =>
|
|
171
|
+
({
|
|
172
|
+
id: h.xszyid,
|
|
173
|
+
baseId: h.zyid,
|
|
174
|
+
title: decodeHTML(h.bt),
|
|
175
|
+
url: URLS.LEARN_HOMEWORK_EXCELLENT_PAGE(h.wlkcid, h.xszyid),
|
|
176
|
+
completionType: h.zywcfs,
|
|
177
|
+
author: {
|
|
178
|
+
id: h.cy?.split(' ')?.[0],
|
|
179
|
+
name: h.cy?.split(' ')?.[1],
|
|
180
|
+
anonymous: h.sfzm === YES,
|
|
181
|
+
},
|
|
182
|
+
}) satisfies IExcellentHomework,
|
|
183
|
+
)
|
|
184
|
+
.map(
|
|
185
|
+
async (h) =>
|
|
186
|
+
({
|
|
187
|
+
...h,
|
|
188
|
+
...(await parseHomeworkAtUrl(s, h.url)),
|
|
189
|
+
...(await getHomeworkDetail(s, h.baseId)),
|
|
190
|
+
}) satisfies ExcellentHomework,
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
).reduce<{ [id: string]: ExcellentHomework[] }>((acc, cur) => {
|
|
194
|
+
if (!acc[cur.baseId]) {
|
|
195
|
+
acc[cur.baseId] = [];
|
|
196
|
+
}
|
|
197
|
+
acc[cur.baseId].push(cur);
|
|
198
|
+
return acc;
|
|
199
|
+
}, {});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function parseHomeworkAtUrl(s: Session, url: string): Promise<IHomeworkDetail> {
|
|
203
|
+
const response = await s.fetchWithToken(url);
|
|
204
|
+
const result = $(await response.text());
|
|
205
|
+
|
|
206
|
+
const fileDivs = result('div.list.fujian.clearfix');
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
description: trimAndDefine(result('div.list.calendar.clearfix > div.fl.right > div.c55').slice(0, 1).html()),
|
|
210
|
+
answerContent: trimAndDefine(result('div.list.calendar.clearfix > div.fl.right > div.c55').slice(1, 2).html()),
|
|
211
|
+
submittedContent: trimAndDefine($(result('div.boxbox').slice(1, 2).toArray())('div.right').slice(2, 3).html()),
|
|
212
|
+
attachment: parseHomeworkFile(s, fileDivs[0]),
|
|
213
|
+
answerAttachment: parseHomeworkFile(s, fileDivs[1]),
|
|
214
|
+
submittedAttachment: parseHomeworkFile(s, fileDivs[2]),
|
|
215
|
+
gradeAttachment: parseHomeworkFile(s, fileDivs[3]),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function getHomeworkDetail(s: Session, baseId: string): Promise<IHomeworkDetail> {
|
|
220
|
+
const json = await (
|
|
221
|
+
await s.fetchWithToken(URLS.LEARN_HOMEWORK_DETAIL, {
|
|
222
|
+
method: 'POST',
|
|
223
|
+
body: URLS.LEARN_HOMEWORK_DETAIL_FORM_DATA(baseId),
|
|
224
|
+
})
|
|
225
|
+
).json();
|
|
226
|
+
if (json.result !== 'success') {
|
|
227
|
+
throw {
|
|
228
|
+
reason: FailReason.INVALID_RESPONSE,
|
|
229
|
+
extra: json,
|
|
230
|
+
} as ApiError;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
description: trimAndDefine(json.msg),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function parseHomeworkFile(s: Session, fileDiv: Element): RemoteFile | undefined {
|
|
238
|
+
const fileNode = $(fileDiv)('.ftitle').children('a')[0] ?? $(fileDiv)('.fl').children('a')[0];
|
|
239
|
+
if (fileNode !== undefined) {
|
|
240
|
+
const size = trimAndDefine($(fileDiv)('.fl > span[class^="color"]').first().text())!;
|
|
241
|
+
const params = new URLSearchParams(fileNode.attribs.href.split('?').slice(-1)[0]);
|
|
242
|
+
const attachmentId = params.get('fileId')!;
|
|
243
|
+
// so dirty here...
|
|
244
|
+
let downloadUrl = URLS.LEARN_PREFIX + fileNode.attribs.href;
|
|
245
|
+
if (params.has('downloadUrl')) {
|
|
246
|
+
downloadUrl = URLS.LEARN_PREFIX + params.get('downloadUrl')!;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
id: attachmentId,
|
|
250
|
+
name: (fileNode.children[0] as { data: string }).data,
|
|
251
|
+
downloadUrl,
|
|
252
|
+
previewUrl: URLS.LEARN_FILE_PREVIEW(ContentType.HOMEWORK, attachmentId, CourseType.STUDENT, s.previewFirstPage),
|
|
253
|
+
size,
|
|
254
|
+
};
|
|
255
|
+
} else {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export async function submitHomework(
|
|
261
|
+
s: Session,
|
|
262
|
+
id: string,
|
|
263
|
+
content = '',
|
|
264
|
+
attachment?: IHomeworkSubmitAttachment,
|
|
265
|
+
removeAttachment = false,
|
|
266
|
+
): Promise<void> {
|
|
267
|
+
const json = await (
|
|
268
|
+
await s.fetchWithToken(URLS.LEARN_HOMEWORK_SUBMIT(), {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
body: URLS.LEARN_HOMEWORK_SUBMIT_FORM_DATA(id, content, attachment, removeAttachment),
|
|
271
|
+
})
|
|
272
|
+
).json();
|
|
273
|
+
if (json.result !== 'success') {
|
|
274
|
+
return Promise.reject({
|
|
275
|
+
reason: FailReason.OPERATION_FAILED,
|
|
276
|
+
extra: json,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|