chuvsu-js 2.6.1 → 2.7.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.
@@ -20,6 +20,7 @@ export declare class TtClient {
20
20
  private isSessionExpired;
21
21
  private relogin;
22
22
  private authGet;
23
+ private authGetBuffer;
23
24
  private authPost;
24
25
  private fetchSchedule;
25
26
  /**
@@ -58,4 +59,16 @@ export declare class TtClient {
58
59
  period: Period;
59
60
  }): Promise<Schedule>;
60
61
  getTeacherInfo(teacherId: number): Promise<TeacherInfo | null>;
62
+ /**
63
+ * Get the teacher's photo as a Buffer.
64
+ * Returns null if the teacher has no photo.
65
+ * Uses cached teacher info when available to avoid extra requests.
66
+ */
67
+ getTeacherPhoto(teacherId: number): Promise<Buffer | null>;
68
+ /**
69
+ * Get the teacher's photo without parsing the schedule page.
70
+ * Uses the known URL pattern directly — no extra page fetch needed.
71
+ * Returns null if the teacher has no photo.
72
+ */
73
+ getTeacherPhotoLazy(teacherId: number): Promise<Buffer | null>;
61
74
  }
package/dist/tt/client.js CHANGED
@@ -88,6 +88,9 @@ export class TtClient {
88
88
  }
89
89
  return res;
90
90
  }
91
+ async authGetBuffer(url) {
92
+ return this.http.getBuffer(url);
93
+ }
91
94
  async authPost(url, data) {
92
95
  const res = await this.http.post(url, data);
93
96
  if (this.loginMode && this.isSessionExpired(res.body)) {
@@ -192,6 +195,12 @@ export class TtClient {
192
195
  const { body } = await this.authPost(url, { htype: String(period) });
193
196
  const days = parseTeacherFullSchedule(body, this.educationType);
194
197
  this.cache?.set("schedule", cacheKey, days);
198
+ // Cache teacher info from the same page to avoid extra requests
199
+ if (!this.cache?.get("teacherInfo", String(teacherId))) {
200
+ const info = parseTeacherInfo(body);
201
+ if (info)
202
+ this.cache?.set("teacherInfo", String(teacherId), info);
203
+ }
195
204
  return days;
196
205
  }
197
206
  async getTeacherSchedule(teacherId) {
@@ -212,8 +221,61 @@ export class TtClient {
212
221
  return new Schedule(opts.teacherId, schedules, opts.period, this.educationType, undefined, undefined, true);
213
222
  }
214
223
  async getTeacherInfo(teacherId) {
224
+ const cached = this.cache?.get("teacherInfo", String(teacherId));
225
+ if (cached)
226
+ return cached;
215
227
  const url = `${BASE}/index/techtt/tech/${teacherId}`;
216
228
  const { body } = await this.authGet(url);
217
- return parseTeacherInfo(body);
229
+ const info = parseTeacherInfo(body);
230
+ if (info)
231
+ this.cache?.set("teacherInfo", String(teacherId), info);
232
+ return info;
233
+ }
234
+ /**
235
+ * Get the teacher's photo as a Buffer.
236
+ * Returns null if the teacher has no photo.
237
+ * Uses cached teacher info when available to avoid extra requests.
238
+ */
239
+ async getTeacherPhoto(teacherId) {
240
+ const photoCacheKey = String(teacherId);
241
+ const cachedPhoto = this.cache?.get("teacherPhotos", photoCacheKey);
242
+ if (cachedPhoto !== null && cachedPhoto !== undefined) {
243
+ const entry = cachedPhoto;
244
+ return entry.data ? Buffer.from(entry.data, "base64") : null;
245
+ }
246
+ // Get teacher info (may already be cached from schedule fetch)
247
+ const info = await this.getTeacherInfo(teacherId);
248
+ if (!info?.photoUrl) {
249
+ this.cache?.set("teacherPhotos", photoCacheKey, { data: null });
250
+ return null;
251
+ }
252
+ const photoBuffer = await this.authGetBuffer(`${BASE}${info.photoUrl}`);
253
+ this.cache?.set("teacherPhotos", photoCacheKey, {
254
+ data: photoBuffer.toString("base64"),
255
+ });
256
+ return photoBuffer;
257
+ }
258
+ /**
259
+ * Get the teacher's photo without parsing the schedule page.
260
+ * Uses the known URL pattern directly — no extra page fetch needed.
261
+ * Returns null if the teacher has no photo.
262
+ */
263
+ async getTeacherPhotoLazy(teacherId) {
264
+ const photoCacheKey = String(teacherId);
265
+ const cachedPhoto = this.cache?.get("teacherPhotos", photoCacheKey);
266
+ if (cachedPhoto !== null && cachedPhoto !== undefined) {
267
+ const entry = cachedPhoto;
268
+ return entry.data ? Buffer.from(entry.data, "base64") : null;
269
+ }
270
+ const url = `${BASE}/index/photo/tech/${teacherId}/id/${teacherId}`;
271
+ const photoBuffer = await this.authGetBuffer(url);
272
+ if (photoBuffer.length === 0) {
273
+ this.cache?.set("teacherPhotos", photoCacheKey, { data: null });
274
+ return null;
275
+ }
276
+ this.cache?.set("teacherPhotos", photoCacheKey, {
277
+ data: photoBuffer.toString("base64"),
278
+ });
279
+ return photoBuffer;
218
280
  }
219
281
  }
package/dist/tt/parse.js CHANGED
@@ -406,7 +406,7 @@ function parseTeacherSemesterEntry(el) {
406
406
  type: typeMatch?.[1] ?? "",
407
407
  weeks: parseWeeks(weeksMatch?.[1] ?? ""),
408
408
  teacher: { name: "" },
409
- groups: groupsMatch?.[1]?.trim() ?? "",
409
+ groups: groupsMatch?.[1]?.trim().replace(/\s*\(\d+\s*подгруппа\)/, "") ?? "",
410
410
  subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
411
411
  weekParity,
412
412
  substitutions: substitutions.length > 0 ? substitutions : undefined,
@@ -497,5 +497,7 @@ export function parseTeacherInfo(html) {
497
497
  const degree = degreeEl ? text(degreeEl).trim() : undefined;
498
498
  const deptEl = doc.querySelector(".htext");
499
499
  const department = deptEl ? text(deptEl).trim() : undefined;
500
- return { name, degree: degree || undefined, department: department || undefined };
500
+ const photoImg = doc.querySelector("#photosrc");
501
+ const photoUrl = photoImg?.getAttribute("src") || undefined;
502
+ return { name, degree: degree || undefined, department: department || undefined, photoUrl };
501
503
  }
@@ -105,6 +105,8 @@ export interface TeacherInfo {
105
105
  name: string;
106
106
  degree?: string;
107
107
  department?: string;
108
+ /** Relative photo URL (e.g. "/index/photo/tech/653/id/653"), or undefined if no photo. */
109
+ photoUrl?: string;
108
110
  }
109
111
  export interface SemesterWeek {
110
112
  week: number;
@@ -116,6 +118,8 @@ export interface CacheConfig {
116
118
  faculties?: number;
117
119
  groups?: number;
118
120
  teachers?: number;
121
+ teacherInfo?: number;
122
+ teacherPhotos?: number;
119
123
  }
120
124
  export interface TtClientOptions {
121
125
  educationType?: EducationType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chuvsu-js",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Node.js library for ChuvSU student portal (lk.chuvsu.ru) and schedule (tt.chuvsu.ru)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",