comcigan.ts/src/client.ts

166 lines
5 KiB
TypeScript

import { BASE_URL, USER_AGENT } from './constants'
import DataManager from './data'
import HTTP from './http'
import School from './models/School'
import type { Timetable } from './models/Timetable'
import { mergeMap } from './utils/array'
import { encodeBase64, encodeEUCKR } from './utils/encode'
import { parseResponse } from './utils/parse'
export default class Comcigan {
private readonly http = new HTTP({
baseURL: BASE_URL,
headers: { 'User-Agent': USER_AGENT },
})
private readonly dataManager = new DataManager(this.http)
/** 학교를 검색합니다. */
async searchSchools(schoolName: string): Promise<School[]> {
const { mainRoute, searchRoute } = await this.dataManager.getData()
const res = await this.http
.get(`${mainRoute}?${searchRoute}l${encodeEUCKR(schoolName)}`)
.then((res) => res.body.text())
const { 학교검색: data } = parseResponse<{
: [number, string, string, number][]
}>(res)
return data.map(
([regionCode, regionName, schoolName, schoolCode]) =>
new School(this, {
code: schoolCode,
name: schoolName,
region: { code: regionCode, name: regionName },
}),
)
}
private async getRawTimetable(
schoolCode: number,
nextWeek = false,
): Promise<Timetable[][][][]> {
const {
mainRoute,
timetableRoute,
teacherCode,
originalCode,
dayCode,
subjectCode,
} = await this.dataManager.getData()
const res = await this.http
.get(
`${mainRoute}_T?${encodeBase64(`${timetableRoute}_${schoolCode}_0_${nextWeek ? 2 : 1}`)}`,
)
.then((res) => res.body.text())
const data = parseResponse(res)
const teachers = data[`자료${teacherCode}`] as string[]
const teachersLen = Math.floor(Math.log10(teachers.length - 1)) + 1
const subjects = data[`자료${subjectCode}`] as string[]
const original = data[`자료${originalCode}`] as number[][][][]
const now = data[`자료${dayCode}`] as number[][][][]
const getSubject = (code?: number) =>
code ? subjects[(code / 10 ** (teachersLen + 1)) | 0] : ''
const getTeacher = (code?: number) =>
code ? teachers[code % 10 ** teachersLen] : ''
return mergeMap(now.slice(1), original.slice(1), (gNow, gOrigin) =>
mergeMap(gNow.slice(1), gOrigin.slice(1), (cNow, cOrigin) =>
mergeMap(cNow.slice(1), cOrigin.slice(1), (dNow, dOrigin) =>
mergeMap(dNow.slice(1), dOrigin.slice(1), (pNow, pOrigin) => {
const changed = pNow !== pOrigin
return {
subject: getSubject(pNow),
teacher: getTeacher(pNow),
changed,
...(changed
? {
originalSubject: getSubject(pOrigin),
originalTeacher: getTeacher(pOrigin),
}
: {}),
} as Timetable
}),
),
),
)
}
async getTimetable(
schoolCode: number,
nextWeek?: boolean,
): Promise<Timetable[][][][]>
async getTimetable(
schoolCode: number,
grade: number,
nextWeek?: boolean,
): Promise<Timetable[][][]>
async getTimetable(
schoolCode: number,
grade: number,
cls: number,
nextWeek?: boolean,
): Promise<Timetable[][]>
async getTimetable(
schoolCode: number,
grade: number,
cls: number,
day: number,
nextWeek?: boolean,
): Promise<Timetable[]>
async getTimetable(
schoolCode: number,
grade: number,
cls: number,
day: number,
period: number,
nextWeek?: boolean,
): Promise<Timetable>
/** 학교 코드를 이용해 학교 시간표를 불러옵니다. */
async getTimetable(
schoolCode: number,
gradeOrNextWeek?: number | boolean,
clsOrNextWeek?: number | boolean,
dayOrNextWeek?: number | boolean,
periodOrNextWeek?: number | boolean,
nextWeek = false,
) {
// Parse arguments
let grade: number | undefined
let cls: number | undefined
let day: number | undefined
let period: number | undefined
let isNextWeek = nextWeek
if (typeof gradeOrNextWeek === 'boolean') {
isNextWeek = gradeOrNextWeek
} else {
grade = gradeOrNextWeek
if (typeof clsOrNextWeek === 'boolean') {
isNextWeek = clsOrNextWeek
} else {
cls = clsOrNextWeek
if (typeof dayOrNextWeek === 'boolean') {
isNextWeek = dayOrNextWeek
} else {
day = dayOrNextWeek
if (typeof periodOrNextWeek === 'boolean') {
isNextWeek = periodOrNextWeek
} else {
period = periodOrNextWeek
}
}
}
}
const raw = await this.getRawTimetable(schoolCode, isNextWeek)
if (grade === undefined) return raw
if (cls === undefined) return raw[grade - 1]
if (day === undefined) return raw[grade - 1][cls - 1]
if (period === undefined) return raw[grade - 1][cls - 1][day - 1]
return raw[grade - 1][cls - 1][day - 1][period - 1]
}
}