@robertying/thu-learn-lib 5.1.0 → 6.0.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.
Files changed (83) hide show
  1. package/README.md +81 -248
  2. package/lib/auth.d.ts +14 -3
  3. package/lib/auth.d.ts.map +1 -1
  4. package/lib/auth.js +56 -58
  5. package/lib/auth.js.map +1 -1
  6. package/lib/calendar.d.ts +7 -2
  7. package/lib/calendar.d.ts.map +1 -1
  8. package/lib/calendar.js +99 -14
  9. package/lib/calendar.js.map +1 -1
  10. package/lib/contents.d.ts.map +1 -1
  11. package/lib/contents.js +4 -9
  12. package/lib/contents.js.map +1 -1
  13. package/lib/courses.d.ts.map +1 -1
  14. package/lib/courses.js +11 -25
  15. package/lib/courses.js.map +1 -1
  16. package/lib/discussions.d.ts.map +1 -1
  17. package/lib/discussions.js +6 -17
  18. package/lib/discussions.js.map +1 -1
  19. package/lib/favorites.d.ts +1 -0
  20. package/lib/favorites.d.ts.map +1 -1
  21. package/lib/favorites.js +13 -48
  22. package/lib/favorites.js.map +1 -1
  23. package/lib/files.d.ts +3 -3
  24. package/lib/files.d.ts.map +1 -1
  25. package/lib/files.js +109 -133
  26. package/lib/files.js.map +1 -1
  27. package/lib/homework.d.ts +34 -3
  28. package/lib/homework.d.ts.map +1 -1
  29. package/lib/homework.js +135 -79
  30. package/lib/homework.js.map +1 -1
  31. package/lib/index.d.ts +39 -19
  32. package/lib/index.d.ts.map +1 -1
  33. package/lib/index.js +44 -17
  34. package/lib/index.js.map +1 -1
  35. package/lib/notices.d.ts +3 -3
  36. package/lib/notices.d.ts.map +1 -1
  37. package/lib/notices.js +4 -20
  38. package/lib/notices.js.map +1 -1
  39. package/lib/questionnaires.d.ts.map +1 -1
  40. package/lib/questionnaires.js +8 -14
  41. package/lib/questionnaires.js.map +1 -1
  42. package/lib/request.d.ts +27 -13
  43. package/lib/request.d.ts.map +1 -1
  44. package/lib/request.js +84 -51
  45. package/lib/request.js.map +1 -1
  46. package/lib/timetable.d.ts +26 -0
  47. package/lib/timetable.d.ts.map +1 -0
  48. package/lib/timetable.js +110 -0
  49. package/lib/timetable.js.map +1 -0
  50. package/lib/types.d.ts +108 -75
  51. package/lib/types.d.ts.map +1 -1
  52. package/lib/types.js +3 -0
  53. package/lib/types.js.map +1 -1
  54. package/lib/urls.d.ts +10 -20
  55. package/lib/urls.d.ts.map +1 -1
  56. package/lib/urls.js +11 -56
  57. package/lib/urls.js.map +1 -1
  58. package/lib/user.d.ts +7 -2
  59. package/lib/user.d.ts.map +1 -1
  60. package/lib/user.js +19 -5
  61. package/lib/user.js.map +1 -1
  62. package/lib/utils.d.ts +42 -4
  63. package/lib/utils.d.ts.map +1 -1
  64. package/lib/utils.js +76 -31
  65. package/lib/utils.js.map +1 -1
  66. package/package.json +8 -7
  67. package/src/auth.ts +62 -73
  68. package/src/calendar.ts +110 -16
  69. package/src/contents.ts +7 -9
  70. package/src/courses.ts +12 -26
  71. package/src/discussions.ts +8 -19
  72. package/src/favorites.ts +22 -48
  73. package/src/files.ts +121 -139
  74. package/src/homework.ts +184 -102
  75. package/src/index.ts +67 -48
  76. package/src/notices.ts +19 -26
  77. package/src/questionnaires.ts +18 -15
  78. package/src/request.ts +86 -62
  79. package/src/timetable.ts +121 -0
  80. package/src/types.ts +111 -86
  81. package/src/urls.ts +13 -64
  82. package/src/user.ts +21 -6
  83. package/src/utils.ts +94 -32
package/README.md CHANGED
@@ -7,40 +7,23 @@ This is a JavaScript library aimed to provide a program-friendly interface of [w
7
7
 
8
8
  This is [robertying](https://github.com/robertying)'s fork of [Harry-Chen/thu-learn-lib](https://github.com/Harry-Chen/thu-learn-lib), maintained for [learnX](https://github.com/robertying/learnX) and published as `@robertying/thu-learn-lib`.
9
9
 
10
- This project is licensed under MIT License.
10
+ This project is licensed under MIT License. See [CHANGELOG.md](CHANGELOG.md) for release notes and [CONTRIBUTING.md](CONTRIBUTING.md) for development, testing and releasing.
11
11
 
12
12
  ## Compatibility
13
13
 
14
14
  The library is ESM-only and uses [node-fetch-native](https://github.com/unjs/node-fetch-native) with [fetch-cookie](https://github.com/valeriangalliat/fetch-cookie) for cookie and redirect support. It runs in Node and in React Native; you can also pass your own `fetch` in `HelperConfig`.
15
15
 
16
+ Browsers block these cross-origin requests (CORS), so run the library in Node.js or React Native.
17
+
16
18
  ## Installation
17
19
 
18
20
  ```bash
19
21
  pnpm add @robertying/thu-learn-lib
20
22
  ```
21
23
 
22
- ## Development
23
-
24
- ```bash
25
- pnpm install
26
- pnpm typecheck
27
- pnpm lint
28
- pnpm format
29
- pnpm test
30
- pnpm build # emits ESM + .d.ts to lib/
31
- ```
32
-
33
- The code is split by area under `src/`: `request.ts` (session, CSRF token, re-login), `auth.ts`, `courses.ts`, `notices.ts`, `files.ts`, `homework.ts`, `discussions.ts`, `questionnaires.ts`, `favorites.ts`, `calendar.ts`, `contents.ts` and `user.ts`. `index.ts` holds the public `Learn2018Helper` class, which passes its private session to those functions.
34
-
35
- Linting uses type-aware [oxlint](https://oxc.rs/docs/guide/usage/linter) and formatting uses [oxfmt](https://oxc.rs/docs/guide/usage/formatter).
36
-
37
- ## Releasing
38
-
39
- Bump `version` in `package.json`, merge, then push a matching tag (for example `git tag v5.1.0 && git push origin v5.1.0`). The Release workflow checks, builds, publishes to npm with provenance (npm trusted publishing, no token) and creates a GitHub release.
40
-
41
24
  ## Usage
42
25
 
43
- ### Authentication related (important changes since v1.2.0)
26
+ ### Logging in
44
27
 
45
28
  ```typescript
46
29
  import { Learn2018Helper } from '@robertying/thu-learn-lib';
@@ -63,39 +46,44 @@ const helper = new Learn2018Helper({
63
46
  // again and retries once when a request finds the session expired, so we strongly recommend it.
64
47
 
65
48
  // Without a CredentialProvider you must log in yourself. With one, the first request logs in.
66
- // When resuming with a saved CSRF token (setCSRFToken) and cookie jar, call `await helper.ensureSession()`:
67
- // it logs in again only if the session has expired.
68
49
  try {
69
- await helper.login(username, password, fingerPrint);
50
+ await helper.login({ username, password, fingerPrint });
70
51
  } catch (e) {
71
52
  // e is an ApiError: check e.reason (a FailReason) and e.extra
72
53
  }
73
54
 
55
+ // When resuming with a saved CSRF token and cookie jar, ensureSession logs in again only if the
56
+ // session has expired.
57
+ helper.setCSRFToken(savedToken);
58
+ await helper.ensureSession();
59
+
74
60
  // To reuse cookies elsewhere (e.g. for file download), pass your own cookie jar in the config.
75
61
 
76
62
  // Logout if you want, but the cookie jar will not be cleared.
77
63
  await helper.logout();
78
64
  ```
79
65
 
80
- ### Content related
66
+ `HelperConfig` also takes `maxConcurrentRequests` (default 6) and `requestTimeout` (milliseconds, default 60000).
81
67
 
82
- We currently support both student and teacher (or TA) version of web learning. To keep backwards compatibility, the **default behavior of all APIs is to use the student version**. The following APIs needs `CourseType.TEACHER` as the last (optional) parameter to access the teacher version. You **should use them when you are the teacher / TA** of one course, for you will get nothing from the student version in that case.
68
+ ### Courses and content
69
+
70
+ We currently support both student and teacher (or TA) version of web learning. To keep backwards compatibility, the **default behavior of all APIs is to use the student version**. The following APIs take an optional `courseType`; pass `CourseType.TEACHER` to access the teacher version. You **should use them when you are the teacher / TA** of one course, for you will get nothing from the student version in that case.
83
71
 
84
72
  We do not maintain a global type in `Learn2018Helper` class, for there can be the situation that one can be a student and teacher of the same course simultaneously, on which by using different `CourseType` you can get different results.
85
73
 
86
- - `getCourseList`
87
- - `getNotificationList/getFileList/getHomeworkList/getDiscussionList/getAnsweredQuestionList`
88
- - `getAllContents`
74
+ - `getCourseList(semesterID, courseType, lang, options)`
75
+ - `getNotificationList(courseID, courseType, options)`, `getHomeworkList(courseID, courseType, options)`
76
+ - `getFileList/getFileCategoryList/getDiscussionList/getAnsweredQuestionList(courseID, courseType)`, `getFileListByCategory(courseID, categoryID, courseType)`
77
+ - `getNotificationAttachment(notification, courseType)`, `getUserInfo(courseType)`
78
+ - `getAllContents(courseIDs, type, { courseType })`
89
79
 
90
80
  Note that currently fetching homework information from teacher version is **partially implemented**.
91
81
 
92
82
  ```typescript
93
- import { ContentType, ApiError } from '@robertying/thu-learn-lib';
94
-
95
83
  // get ids of all semesters that current account has access to
96
84
  const semesters = await helper.getSemesterIdList();
97
85
 
98
- // get get semester info
86
+ // get semester info
99
87
  const semester = await helper.getCurrentSemester();
100
88
 
101
89
  // get courses of this semester
@@ -110,8 +98,19 @@ const homework = await helper.getHomeworkList(course.id);
110
98
  const questions = await helper.getAnsweredQuestionList(course.id);
111
99
  // questionnaires fetch their questions (one request each) unless you pass { details: 'none' }
112
100
  const questionnaires = await helper.getQuestionnaireList(course.id, { details: 'none' });
101
+ ```
102
+
103
+ Every time field is a `ServerTime`: an ISO 8601 string in Beijing time with its offset (`2026-11-07T18:28:00+08:00`), which `new Date()` reads as the right moment in any time zone.
104
+
105
+ The lists give `CourseNotification`, `CourseFile`, `Homework`, `Discussion`, `Question` and `Questionnaire` items. A type for an item without its on-demand detail ends in `Base` (`HomeworkBase`, `CourseNotificationBase`), and the detail alone in `Detail` (`HomeworkDetail`).
106
+
107
+ ### Details on demand
108
+
109
+ Details cost extra requests per item, so they are opt-in; fetch the rest on demand.
110
+
111
+ ```typescript
112
+ import { ContentType } from '@robertying/thu-learn-lib';
113
113
 
114
- // details cost extra requests per item, so they are opt-in; fetch the rest on demand
115
114
  const withDetails = await helper.getHomeworkList(course.id, undefined, { details: (h) => !h.submitted });
116
115
  const detail = await helper.getHomeworkDetail(homework[0]);
117
116
  const attachment = await helper.getNotificationAttachment(notifications[0]);
@@ -122,233 +121,67 @@ const { contents, errors } = await helper.getAllContents(
122
121
  ContentType.HOMEWORK,
123
122
  { details: 'none' },
124
123
  );
124
+ ```
125
+
126
+ ### Calendar
127
+
128
+ ```typescript
129
+ import { ApiError } from '@robertying/thu-learn-lib';
125
130
 
126
131
  // get course calendar in a period (yyyymmdd, inclusive)
127
132
  // ranges over 28 days are fetched in windows; windows that still fail are in failedRanges
128
133
  try {
129
134
  const { events, failedRanges } = await helper.getCalendar('20191001', '20191201', { graduate: false });
130
135
  } catch (e) {
131
- // every window failed
136
+ // every window failed, or signing in to the registrar failed
132
137
  const error = e as ApiError;
133
138
  // check e.reason and e.extra for information
134
139
  }
135
140
  ```
136
141
 
137
- Browsers block these cross-origin requests (CORS), so run the library in Node.js or React Native.
142
+ Off campus the registrar is reached through WebVPN, which reuses the id.tsinghua.edu.cn session of the last `login`. When that session has expired (e.g. in a session resumed from saved cookies), a helper with a `CredentialProvider` logs in again and tries once more.
143
+
144
+ ### Timetable
145
+
146
+ The registrar's calendar knows about holidays and moved classes, but it fails now and then. A timetable can also be worked out from each course's time and location (`getCourseList(..., { timeAndLocation: true })`) and the term's first day:
147
+
148
+ ```typescript
149
+ import { CLASS_PERIOD_TIMES, parseCourseMeeting, semesterWeek } from '@robertying/thu-learn-lib';
150
+
151
+ // '星期六第1节(9,11周),六教6A414' (or 'Section 1 of Sat (Week 9,11 ), 六教6A414' in English)
152
+ const meeting = parseCourseMeeting(course.timeAndLocation[0]);
153
+ // { weekday: 6, period: 1, weeks: [9, 11], location: '六教6A414' }; weeks is null for every week
154
+ const [start, end] = CLASS_PERIOD_TIMES[meeting.period]; // Beijing time, ['08:00', '09:35']
155
+
156
+ // the teaching week of a Beijing date; week 1 starts on the Monday after a weekend first day
157
+ const week = semesterWeek(semester.startDate, '2026-11-04');
158
+ ```
159
+
160
+ ### Submitting homework
161
+
162
+ ```typescript
163
+ import { homeworkContentHtml } from '@robertying/thu-learn-lib';
164
+
165
+ // the answer is HTML and replaces the submitted one; pass the submitted HTML to only change the attachment
166
+ await helper.submitHomework(homework.studentHomeworkId, homeworkContentHtml('my answer'), {
167
+ filename: 'answer.pdf',
168
+ content: blob, // in React Native: { uri, type }
169
+ });
170
+ ```
171
+
172
+ `fetch` can't report upload progress. To send the submission with another HTTP client, get the request with `getHomeworkSubmitRequest` (it checks the session first) and read the answer with `checkHomeworkSubmitResponse`:
173
+
174
+ ```typescript
175
+ import { checkHomeworkSubmitResponse } from '@robertying/thu-learn-lib';
176
+
177
+ const { url, body } = await helper.getHomeworkSubmitRequest(id, content, attachment);
178
+ const res = await axios.post(url, body, { onUploadProgress });
179
+ checkHomeworkSubmitResponse(res.data);
180
+ ```
181
+
182
+ ### Errors
138
183
 
139
- ## Typing
140
-
141
- See `src/types.ts` for type definitions. Note that `ApiError` represents the type that will be used in rejected Promises.
142
-
143
- ## Testing
144
-
145
- `pnpm test` runs the offline tests. They give the helper a fake `fetch` (`test/helpers/fake-server.ts`) that serves hand-written responses shaped like Web Learning's, simulates SSO login and session expiry, and records every request so tests can also check how many requests an API makes.
146
-
147
- `pnpm test:live` runs the tests against the real server. It needs your own credentials and a device fingerprint: copy `test/live/template.env` to `test/live/.env` and fill it in. The live tests also check what the offline tests can only assume, such as how the server answers an expired session (see [TODO.md](TODO.md)).
148
-
149
- ## Changelog
150
-
151
- - v5.1.0
152
- - Treat a 200 HTML page where JSON was expected as an expired session: log in again and retry once, like a 403
153
- - `ensureSession` checks the probe's body (the current semester) instead of only its status
154
- - Logins never overlap: `login(username, password, fingerPrint)` waits for a login in progress, and requests that find the session expired during it wait for it instead of starting another SSO login
155
- - `submitHomework` with an attachment has no timeout (the response only starts after the upload)
156
- - A caller's `signal` can still cancel reading a body after the headers arrived
157
- - `getCalendar`: timed-out windows are not retried, and after two in a row the rest go to `failedRanges` without a request; an invalid date throws a `RangeError`; a bad JSONP body rejects with an `ApiError` (`INVALID_RESPONSE`) instead of a bare string
158
- - `getNotificationAttachment` returns `undefined` when the detail page has no attachment link, instead of throwing
159
- - `getHomeworkList(courseID, undefined, options)` type-checks; `getAllContents` rejects homework of a teacher course with `NOT_IMPLEMENTED` instead of returning student data
160
- - Supports Node 22 and later (was 26)
161
- - A wrong password rejects with `BAD_CREDENTIAL` (with the SSO page's message in `extra`) instead of `ERROR_FETCH_FROM_ID`, which now means the SSO server failed or couldn't be reached
162
- - HTML pages (user info, homework page, notice detail, calendar ticket) also detect the short login page of an expired session and log in again
163
- - `logout` forgets the CSRF token, so the next request logs in instead of sending the old token
164
- - `getFileList` still returns the files (without `category`) when the category list fails
165
- - `getQuestionnaireList(courseID, { details })` can skip the questions (one request per questionnaire; default still `'all'`), with `getQuestionnaireDetail(courseID, questionnaireID)` on demand; `detail` is now optional
166
- - Live tests need a fingerprint, use readable `.env` names (`USERNAME`, `PASSWORD`, `FINGERPRINT`, `STUDENT_SEMESTER`, …; see `test/live/template.env`) and cover session expiry and resume, on-demand details and windowed calendar
167
- - v5.0.0
168
- - (_BREAKING CHANGE_) Per-item detail requests are opt-in: `getHomeworkList(id, CourseType.STUDENT, { details })`, `getNotificationList(id, type, { attachments })` (notices get `attachmentName`), `getCourseList(semester, type, lang, { timeAndLocation })` (`timeAndLocation` is now optional); fetch the rest on demand with `getHomeworkDetail`, `getExcellentHomeworkDetail`, `getNotificationAttachment` and `getCourseTimeAndLocation`
169
- - (_BREAKING CHANGE_) The excellent homework list is fetched once per course and comes without details
170
- - (_BREAKING CHANGE_) `getAllContents(ids, type, { courseType, details })` returns `{ contents, errors }` and no longer rejects when a course fails
171
- - (_BREAKING CHANGE_) `getCalendar(start, end, { graduate })` fetches ranges over 28 days in windows and returns `{ events, failedRanges }`
172
- - Requests are capped (`maxConcurrentRequests`, default 6) and time out (`requestTimeout`, default 60 s, `FailReason.REQUEST_TIMEOUT`); concurrent re-logins share one SSO login
173
- - `ensureSession()` resumes a saved session (CSRF token + cookies) and logs in only if it has expired
174
- - v4.1.0
175
- - Published as `@robertying/thu-learn-lib`
176
- - Tooling: pnpm, TypeScript 7 (`tsc` ESM + `.d.ts` build), oxlint + oxfmt, Vitest 5, Node 26
177
- - (_BREAKING CHANGE_) Drop the IIFE browser build and the browser-extension demo
178
- - Code split into modules, with offline tests
179
- - No API changes
180
- - v4.0.0
181
- - Upgrade cheerio to 1.0
182
- - Add support for questionnaire (add `getQuestionnaireList` API and some public types)
183
- - Add support for managing favorite items
184
- - Add support for sorting courses (i.e. manage the order on web learning)
185
- - Add support for managing comments on content (e.g. files, notifications)
186
- - (_BREAKING CHANGE_) Deprecate usage of `studentHomeworkId`
187
- - v3.2.1
188
- - Upgrade to eslint v9
189
- - Add `setCSRFToken` function to manually reuse previous (maybe valid) token and prevent unnecessary re-login (see [#49](https://github.com/Harry-Chen/thu-learn-lib/issues/49))
190
- _Note:_ To use this feature in Node.js, `cookieJar` should also be reused, otherwise it will not work. (not the case in browser env)
191
- - Fix `CourseContent` type: default value for type param `T` (breaking change introduced in [#53](https://github.com/Harry-Chen/thu-learn-lib/issues/53))
192
- - _(Breaking...?)_ Rename `File.id2` added in previous version to actual `id` and previously used `id` to `fileId` (see [#60](https://github.com/Harry-Chen/thu-learn-lib/issues/60))
193
- - Upgrade dependencies
194
- - v3.2.0
195
- - Support file categories (see [#57](https://github.com/Harry-Chen/thu-learn-lib/issues/57)):
196
- - Add `FileCategory` type and optional `category` field in `File` type
197
- - Now `getFileList` function automatically fetches all categories and saves them in each `File` object
198
- - Add `getFileCategoryList` function to fetch file categories
199
- - Add `getFileListByCategory` function to get file list by category
200
- - Fix content of file download url and visit count in for `CourseType.TEACHER`
201
- - v3.1.4
202
- - Allow and check for undefined credential in login (see [#52](https://github.com/Harry-Chen/thu-learn-lib/issues/52))
203
- - Discriminate `getAllContents` return type based on input content type (see [#53](https://github.com/Harry-Chen/thu-learn-lib/issues/53))
204
- - Use `searchParams` to set csrf token & add csrf token to `getCalendar` (see [#54](https://github.com/Harry-Chen/thu-learn-lib/issues/54))
205
- - Update bunches of dependencies
206
- - v3.1.3
207
- - Fix empty time and location of course (see [#145](https://github.com/Harry-Chen/Learn-Helper/issues/145))
208
- - v3.1.2
209
- - Fix empty course name parsing
210
- - (Maybe BEARKING) `Homework.gradeLevel` changed to `HomeworkGradeLevel` (a string enum) for better i18n
211
- - Fix get language
212
- - v3.1.1
213
- - Remove `fetch-cookie` dependency in browser build, reducing bundle size
214
- - Add `getUserInfo` interface (#48, thanks to @robertying)
215
- - v3.1.0
216
- - **Refactor**:
217
- - Build tool: `webpack` -> `tsup`
218
- - Development tool: `webpack` -> `vite`
219
- - Test tool: `jest` -> `vitest`
220
- - Fetch:
221
- - `cross-fetch` -> `node-fetch-native` (Node.js now has native `fetch`)
222
- - `real-isomorphic-fetch` -> `fetch-cookie` (`fetch-cookie` now can handle redirects)
223
- - `tough-cookie-no-native` -> `tough-cookie` (`tough-cookie` is now native)
224
- - **(BREAKING)** Updated library output structure, now everything exported in index, cannot use `/lib` or `/lib/types` anymore
225
- - Added:
226
- - Submit homework
227
- - Language switching and related behaviour
228
- - Get TA's homework info (partial support)
229
- - Fixed:
230
- - **Web Learning API update**
231
- - Decode course name
232
- - Other:
233
- - Move `fake-parse5` to [a seperate library](https://github.com/AsakuraMizu/fake-parse5)
234
- - v3.0.4
235
- - No feature changes, upgrade dependencies
236
- - v3.0.3
237
- - No feature changes, upgrade dependencies
238
- - v3.0.2
239
- - No feature changes, upgrade dependencies to mitigate security vulnerabilities
240
- - v3.0.1
241
- - Add config `generatePreviewUrlForFirstPage` to switch preview URL type (default to `true`)
242
- - v3.0.0
243
- - (BREAKING CHANGE) Redesign exported types, use `RemoteFile` to represent a file on Web Learning
244
- - Add support for parsing attachment sizes & preview URLs in homework & notifications (Harry-Chen/Learn-Helper#109)
245
- - v2.5.4
246
- - Fix (sometimes) incorrect publish time of notifications (#36)
247
- - v2.5.3
248
- - Add `allowFailure` in `getAllContents` for convenience
249
- - v2.5.2
250
- - Allow retrieving CSRF token via `getCSRFToken` method
251
- - Export `addCSRFTokenToUrl` function as API for convenience
252
- - v2.5.1
253
- - No feature change, add support for direct `import` in Node.js
254
- - v2.5.0
255
- - Add transparent support for CSRF token (recently deployed) in all Web Learning APIs
256
- - v2.4.2
257
- - No functionality change, bump some dependencies to mitigate security concerns
258
- - v2.4.1
259
- - Replace `parse5` with `htmlparser2` in Cheerio and remove it from bundled file (replaced with `src/fake-parse5`)
260
- - Replace TSLint with ESLint
261
- - v2.4.0
262
- - Upgrade to TypeScript 4.1 & Webpack 5.15
263
- - Add more null checking for disabled functionalities (see [xxr3376/Learn-Project#90](https://github.com/xxr3376/Learn-Project/issues/90))
264
- - v2.3.2
265
- - Fix a problem in `v2.3.1` (not published) and `v2.3.0` that build output fails to be uploaded to npm
266
- - v2.3.0
267
- - Refine error detecting & handling by using `ApiError` in usage of `Promise.reject` (might be a breaking change)
268
- - v2.2.3
269
- - Add workaround for some strange behaviors in teacher mode (thanks to @MashPlant)
270
- - v2.2.2
271
- - Return error when API return any non-success result
272
- - v2.2.1
273
- - Fix a missing parameter in `previewUrl`
274
- - v2.2.0
275
- - Use ECMAScript private fields in `Learn2018Helper` class to protect credentials
276
- - Add `previewUrl` to `File`
277
- - Upgrade to TypeScript 3.8.2
278
- - v2.1.2
279
- - Fix problem in invoking JSONP callback (because some engines might do JIT)
280
- - v2.1.1
281
- - Remove usage of `eval` in `getCalendar`
282
- - v2.1.0
283
- - Catch errors returned by calendar API and throw user-defined error
284
- - Add documentation for all public APIs
285
- - v2.0.0
286
- - Use ES2018 in generated library
287
- - Add `FailReason` to represent all strings that will be used as the reason of rejected Promises
288
- - `login` and `logout` no longer return Promises
289
- - v1.2.2
290
- - Fix a function signature to keep compatibility
291
- - v1.2.1
292
- - Support TA version of many APIs (see above for usage)
293
- - Fix some wrong URLs in fetched data
294
- - v1.2.0
295
- - Support getting course calendars from academic.tsinghua.edu.cn (thanks to robertying)
296
- - Automatic retry logging in when fetching failed and `CredentialProvider` is provided (thanks to mayeths)
297
- - Add unit tests using `jest` (thanks to mayeths)
298
- - Filter out `null` values in `getSemesterIdList` API
299
- - Switch to `https://learn.tsinghua.edu.cn/` from `learn2018` permanently
300
- - v1.1.4
301
- - Return empty array if any content module is disabled
302
- - Add `getTACourseList` to get TA's course list (temporarily can not be used by other functions)
303
- - v1.1.3
304
- - Emergency fix of wrongly decoded base64 string, add `js-base64` back
305
- - v1.1.2
306
- - Switch to `Base64.js` instead of `js-base64`, which uses evil `eval`
307
- - v1.1.1
308
- - Decode HTML entities in the title of disscussions (the last one, I promise!)
309
- - v1.1.0
310
- - Fix an typo in grade level mapping
311
- - Bump to a new minor version
312
- - v1.0.16 (no v1.0.15 due to some publishing issues)
313
- - Switch to `yarn`
314
- - Add parsing of grade levels of homework (A+/A/.../F)
315
- - v1.0.14
316
- - Add prefix for `attachmentUrl` filed of Notification
317
- - Deprecate all old versions
318
- - v1.0.13
319
- - Decode HTML entities whenever possible
320
- - v1.0.12
321
- - Add `url` for Course
322
- - Fix `url` for Question (to display correct section name)
323
- - v1.0.11
324
- - Fix `url` error in Question
325
- - v1.0.10
326
- - Decode the HTML entities in the `description` field of homework
327
- - v1.0.9
328
- - Use `entities` to decode HTML entities
329
- - v1.0.8
330
- - Export type CourseContent
331
- - v1.0.7
332
- - No change made to code, update README
333
- - v1.0.6
334
- - Add API to fetching content for a list of courses
335
- - v1.0.5
336
- - Fix HTML entity replacement.
337
- - v1.0.4
338
- - No change made to code
339
- - Remove unused build commands
340
- - Fix multiple typos in README
341
- - v1.0.3
342
- - Add real logout API (thank @zhaofeng-shu33)
343
- - v1.0.2
344
- - Add API to get IDs of all semesters (thank @jiegec)
345
- - v1.0.1
346
- - Expose CookieJar in helper class
347
- - Fix some HTML entity decoding problems
348
- - **Rename of some APIs** (break compatibility before we have actual users)
349
- - v1.0.0
350
- - First release
351
- - Support parsing of notification, homework, file, discussion and **answered** questions
184
+ Rejected promises carry an `ApiError`: `reason` is a `FailReason` and `extra` holds what the server answered, when there is something useful. For example, a wrong password rejects with `BAD_CREDENTIAL`, an expired session without a `CredentialProvider` with `NOT_LOGGED_IN`, a request that gets no answer (offline, DNS or TLS failure) with `NETWORK_ERROR`, and a slow request with `REQUEST_TIMEOUT`; `isConnectionError(e)` is true for the last two, also for a login. See `src/types.ts` for every reason and the other type definitions.
352
185
 
353
186
  ## Projects using this library
354
187
 
package/lib/auth.d.ts CHANGED
@@ -1,11 +1,22 @@
1
1
  import type { Session } from './request.ts';
2
+ import type { Credential } from './types.ts';
3
+ /** a `Credential` with everything an SSO login needs */
4
+ export type CompleteCredential = Credential & {
5
+ username: string;
6
+ password: string;
7
+ fingerPrint: string;
8
+ };
9
+ /** whether `credential` has a username, a password and a fingerprint */
10
+ export declare const isComplete: (credential: Credential | undefined) => credential is CompleteCredential;
2
11
  /**
3
12
  * If using alternative cookie management systems,
4
13
  * be sure to clear id.tsinghua.edu.cn cookies before calling this function
5
14
  */
6
- export declare function getRoamingTicket(s: Session, username: string, password: string, fingerPrint: string, fingerGenPrint?: string, fingerGenPrint3?: string): Promise<string>;
7
- /** login is necessary if you do not provide a `CredentialProvider` */
8
- export declare function login(s: Session, username?: string, password?: string, fingerPrint?: string, fingerGenPrint?: string, fingerGenPrint3?: string): Promise<void>;
15
+ export declare function getRoamingTicket(s: Session, credential: CompleteCredential): Promise<string>;
16
+ /** log in with the `CredentialProvider`; `NO_CREDENTIAL` without one, or when it gives an incomplete credential */
17
+ export declare function loginWithProvider(s: Session): Promise<void>;
18
+ /** log in through id.tsinghua.edu.cn, then read the CSRF token and the website language from the homepage */
19
+ export declare function login(s: Session, credential: CompleteCredential): Promise<void>;
9
20
  /** logout (to make everyone happy); the CSRF token is cleared, so the next request logs in again */
10
21
  export declare function logout(s: Session): Promise<void>;
11
22
  //# sourceMappingURL=auth.d.ts.map
package/lib/auth.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAM5C;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,CAAC,EAAE,OAAO,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,cAAc,GAAE,MAAW,EAC3B,eAAe,GAAE,MAAW,mBAwD7B;AAED,sEAAsE;AACtE,wBAAsB,KAAK,CACzB,CAAC,EAAE,OAAO,EACV,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED,qGAAqG;AACrG,wBAAsB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtD"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK7C,wDAAwD;AACxD,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1G,wEAAwE;AACxE,eAAO,MAAM,UAAU,eAAgB,UAAU,GAAG,SAAS,KAAG,UAAU,IAAI,kBACD,CAAC;AAE9E;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmDlG;AAaD,mHAAmH;AACnH,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAIjE;AAED,6GAA6G;AAC7G,wBAAsB,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBrF;AAED,qGAAqG;AACrG,wBAAsB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtD"}
package/lib/auth.js CHANGED
@@ -1,30 +1,27 @@
1
1
  // Login to Web Learning through id.tsinghua.edu.cn (SSO) and logout.
2
2
  import { sm2 } from 'sm-crypto';
3
- import { $ } from './request.js';
4
- import { FailReason, Language } from './types.js';
3
+ import { discard, isConnectionError } from './request.js';
4
+ import { CourseType, FailReason, Language } from './types.js';
5
5
  import * as URLS from './urls.js';
6
+ import { $, apiError } from './utils.js';
7
+ /** whether `credential` has a username, a password and a fingerprint */
8
+ export const isComplete = (credential) => !!credential?.username && !!credential.password && !!credential.fingerPrint;
6
9
  /**
7
10
  * If using alternative cookie management systems,
8
11
  * be sure to clear id.tsinghua.edu.cn cookies before calling this function
9
12
  */
10
- export async function getRoamingTicket(s, username, password, fingerPrint, fingerGenPrint = '', fingerGenPrint3 = '') {
11
- // Make sure we always start with the form page
12
- // More code is needed to handle the case where the user is already logged in (i.e. cookies are set)
13
- // It won't be a problem if it is always the same user,
14
- // but if the current cookies are from a different user than the one trying to log in,
15
- // it will cause problems.
13
+ export async function getRoamingTicket(s, credential) {
14
+ const { username, password, fingerPrint, fingerGenPrint = '', fingerGenPrint3 = '' } = credential;
15
+ // start from the login form without an id.tsinghua.edu.cn session: one left in the cookies may be another user's
16
16
  try {
17
17
  await s.cookieJar.setCookie('JSESSIONID=; path=/; HttpOnly', URLS.ID_PREFIX);
18
18
  }
19
19
  catch (err) {
20
- throw {
21
- reason: FailReason.ERROR_SETTING_COOKIES,
22
- extra: err,
23
- };
20
+ throw apiError(FailReason.ERROR_SETTING_COOKIES, err);
24
21
  }
25
22
  let page;
26
23
  try {
27
- const loginForm = await s.rawFetch(URLS.ID_LOGIN());
24
+ const loginForm = await s.rawFetch(URLS.ID_LOGIN);
28
25
  const body = $(await loginForm.text());
29
26
  const sm2publicKey = body('#sm2publicKey').text().trim();
30
27
  const formData = new FormData();
@@ -32,10 +29,10 @@ export async function getRoamingTicket(s, username, password, fingerPrint, finge
32
29
  formData.append('i_pass', '04' + sm2.doEncrypt(password, sm2publicKey));
33
30
  formData.append('singleLogin', 'on');
34
31
  formData.append('fingerPrint', fingerPrint);
35
- formData.append('fingerGenPrint', fingerGenPrint ?? '');
36
- formData.append('fingerGenPrint3', fingerGenPrint3 ?? '');
32
+ formData.append('fingerGenPrint', fingerGenPrint);
33
+ formData.append('fingerGenPrint3', fingerGenPrint3);
37
34
  formData.append('i_captcha', '');
38
- const checkResponse = await s.rawFetch(URLS.ID_LOGIN_CHECK(), {
35
+ const checkResponse = await s.rawFetch(URLS.ID_LOGIN_CHECK, {
39
36
  method: 'POST',
40
37
  body: formData,
41
38
  });
@@ -45,67 +42,68 @@ export async function getRoamingTicket(s, username, password, fingerPrint, finge
45
42
  page = await checkResponse.text();
46
43
  }
47
44
  catch (err) {
48
- throw {
49
- reason: FailReason.ERROR_FETCH_FROM_ID,
50
- extra: err,
51
- };
45
+ // no answer at all: the device is offline or id.tsinghua.edu.cn is unreachable, not a refused login
46
+ if (isConnectionError(err))
47
+ throw err;
48
+ throw apiError(FailReason.ERROR_FETCH_FROM_ID, err);
52
49
  }
53
- // id.tsinghua.edu.cn answers with a link to the roaming URL (with the ticket), or with the form and an error
50
+ // id.tsinghua.edu.cn answers with a page whose only link is the roaming URL (with the ticket), or with the login form
51
+ // again (with its links to the password reset, guides, phone and mail) for a wrong password
54
52
  const body = $(page);
55
- const redirectUrl = body('a').attr('href');
56
- if (!redirectUrl) {
57
- throw {
58
- reason: FailReason.BAD_CREDENTIAL,
59
- extra: body.root().text().replace(/\s+/g, ' ').trim().slice(0, 200),
60
- };
53
+ const ticket = ticketOf(body('a[href*="ticket="]').attr('href'));
54
+ if (ticket)
55
+ return ticket;
56
+ if (body('#theform, form[action*="/auth/login/check"]').length > 0 && body('#i_pass').length > 0) {
57
+ throw apiError(FailReason.BAD_CREDENTIAL, textOf(body('#msg_note, .alert-danger')) || undefined);
61
58
  }
62
- return redirectUrl.split('=').slice(-1)[0];
59
+ // something else, e.g. a second factor, a locked account or maintenance: not a wrong password
60
+ throw apiError(FailReason.ERROR_FETCH_FROM_ID, textOf(body('body').clone().find('script, style').remove().end()).slice(0, 200));
63
61
  }
64
- /** login is necessary if you do not provide a `CredentialProvider` */
65
- export async function login(s, username, password, fingerPrint, fingerGenPrint, fingerGenPrint3) {
66
- if (!username || !password || !fingerPrint) {
67
- if (!s.provider)
68
- return Promise.reject({
69
- reason: FailReason.NO_CREDENTIAL,
70
- });
71
- const credential = await s.provider();
72
- username = credential.username;
73
- password = credential.password;
74
- fingerPrint = credential.fingerPrint;
75
- fingerGenPrint = credential.fingerGenPrint;
76
- fingerGenPrint3 = credential.fingerGenPrint3;
77
- if (!username || !password || !fingerPrint) {
78
- return Promise.reject({
79
- reason: FailReason.NO_CREDENTIAL,
80
- });
81
- }
62
+ const textOf = (el) => el.text().replace(/\s+/g, ' ').trim();
63
+ const ticketOf = (href) => {
64
+ if (!href)
65
+ return undefined;
66
+ try {
67
+ return new URL(href, URLS.ID_PREFIX).searchParams.get('ticket') || undefined;
68
+ }
69
+ catch {
70
+ return undefined;
82
71
  }
83
- // check response from id.tsinghua.edu.cn
84
- const ticket = await getRoamingTicket(s, username, password, fingerPrint, fingerGenPrint, fingerGenPrint3);
72
+ };
73
+ /** log in with the `CredentialProvider`; `NO_CREDENTIAL` without one, or when it gives an incomplete credential */
74
+ export async function loginWithProvider(s) {
75
+ const credential = await s.provider?.();
76
+ if (!isComplete(credential))
77
+ throw apiError(FailReason.NO_CREDENTIAL);
78
+ return login(s, credential);
79
+ }
80
+ /** log in through id.tsinghua.edu.cn, then read the CSRF token and the website language from the homepage */
81
+ export async function login(s, credential) {
82
+ const ticket = await getRoamingTicket(s, credential);
83
+ const { fingerPrint, fingerGenPrint = '', fingerGenPrint3 = '' } = credential;
84
+ s.fingerprints = { fingerPrint, fingerGenPrint, fingerGenPrint3 };
85
85
  const loginResponse = await s.rawFetch(URLS.LEARN_AUTH_ROAM(ticket));
86
+ // only the status matters
87
+ discard(loginResponse);
86
88
  if (!loginResponse.ok) {
87
- return Promise.reject({
88
- reason: FailReason.ERROR_ROAMING,
89
- });
89
+ throw apiError(FailReason.ERROR_ROAMING);
90
90
  }
91
- const courseListPageSource = await (await s.rawFetch(URLS.LEARN_STUDENT_COURSE_LIST_PAGE())).text();
91
+ const courseListPageSource = await (await s.rawFetch(URLS.LEARN_HOMEPAGE(CourseType.STUDENT))).text();
92
92
  const tokenRegex = /^.*&_csrf=(\S*)".*$/gm;
93
93
  const tokenMatches = [...courseListPageSource.matchAll(tokenRegex)];
94
94
  if (tokenMatches.length === 0) {
95
- return Promise.reject({
96
- reason: FailReason.INVALID_RESPONSE,
97
- extra: 'cannot fetch CSRF token from source',
98
- });
95
+ throw apiError(FailReason.INVALID_RESPONSE, 'cannot fetch CSRF token from source');
99
96
  }
100
97
  s.csrfToken = tokenMatches[0][1];
101
- const langRegex = /<script src="\/f\/wlxt\/common\/languagejs\?lang=(zh|en)"><\/script>/g;
98
+ // the homepage loads e.g. `/f/wlxt/common/languagejs?lang=zh_CN&v=845`
99
+ const langRegex = /\/f\/wlxt\/common\/languagejs\?lang=(zh|en)/g;
102
100
  const langMatches = [...courseListPageSource.matchAll(langRegex)];
103
101
  if (langMatches.length !== 0)
104
102
  s.lang = langMatches[0][1] === 'en' ? Language.EN : Language.ZH;
105
103
  }
106
104
  /** logout (to make everyone happy); the CSRF token is cleared, so the next request logs in again */
107
105
  export async function logout(s) {
108
- await s.rawFetch(URLS.LEARN_LOGOUT(), { method: 'POST' });
106
+ discard(await s.rawFetch(URLS.LEARN_LOGOUT, { method: 'POST' }));
109
107
  s.csrfToken = '';
110
108
  }
111
109
  //# sourceMappingURL=auth.js.map
package/lib/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}