@heknon/node-webtop
Version:
A Backend for an unofficial WebTop (SmartSchool - Israel) API.
203 lines (173 loc) • 8.15 kB
text/typescript
import axios, { AxiosInstance } from 'axios';
import { stringify as queryStringify } from 'query-string';
import axiosCookieJarSupport from 'axios-cookiejar-support';
import { CookieJar } from 'tough-cookie';
import { defaultApiRoutes, baseUrl, baseApiUrl, routes } from './apiConfig';
import { load as cheerioLoad } from 'cheerio';
import { constructTimeTable } from './TimeTable/TimeTable';
import { constructGrades, constructGradeSemester, constructGradeYear } from './Grades/Grades'
import { constructInbox } from './Inbox/Inbox';
import { constructMessage } from './Inbox/Message';
import { constructTestsLeft } from './TestsLeft/TestsLeft';
import { TimeTable } from './TimeTable/DataObjects';
import { constructTimeTableChanges } from './TimeTableChanges/TimeTableChanges';
import { WebtopSecurityData, LessonResponse, EventResponse, HomeworkResponse, WebTopUsersResponse, WebTopUserData, PersonalDetails, PushNotifications } from './DataObjects';
export default class Client {
id: string;
password: string;
_axiosInstance: AxiosInstance | null;
_cookieJar: CookieJar | null;
platform: string | null;
constructor(id: string, password: string) {
this.id = id;
this.password = password;
this._axiosInstance = null;
this._cookieJar = null;
this.platform = null;
}
/**
* get the security necessary for logging in
* @param {CheerioStatic} $ parsed html
* @returns {WebtopSecurityData}
*/
_getSecurityData($: CheerioStatic): WebtopSecurityData {
const keys = $("#captchaWrapper").children('input[type="hidden"]')[0];
return {
securityId: keys.attribs.id,
securityValue: keys.attribs.value,
};
};
/**
* gets platform in use
* @param {CheerioStatic} $ parsed html
* @returns platform axios is using
*/
_getPlatform($: CheerioStatic) {
const platform = $('#platform').val();
return platform;
}
async login() {
this._cookieJar = new CookieJar();
this._axiosInstance = axios.create({
jar: this._cookieJar,
withCredentials: true,
headers: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36'
},
});;
axiosCookieJarSupport(this._axiosInstance);
this._axiosInstance.defaults.jar = this._cookieJar;
const html = (await this._axiosInstance.get(baseUrl)).data;
const $ = cheerioLoad(html);
const { securityId, securityValue } = this._getSecurityData($);
const data = {
'action': 'login',
'rememberMe': 1,
'captcha': '',
'secondsToLogin': 23,
'username': this.id,
'password': this.password,
[securityId]: securityValue
};
this.platform = this._getPlatform($);
const loginReq = await this._axiosInstance.post(baseApiUrl(this.platform), queryStringify(data));
if (loginReq.data.error !== undefined) throw new Error(loginReq.data.error);
return loginReq;
}
async logout() {
const res = await this._axiosInstance!.post(baseApiUrl(this.platform), queryStringify({action: 'logout'}));
if (res.status === 200) {
this.platform = '';
this._axiosInstance = null;
this._cookieJar = null;
return res;
}
throw new Error('Failed to logout')
}
/**
* get all the grades of all 3 years of school
* @param onlyFilledGrades if only to show grades that have been filled by a teacher. if false grades that haven't been filled will be set to -1
*/
async getGrades(onlyFilledGrades: boolean = true) {
return constructGrades(this._axiosInstance!, onlyFilledGrades);
}
/**
* get the grades of a semester
* @param semester the semester. 1 or 2.
* @param studentYear the study year
* @param onlyFilledGrades if only to show grades that have been filled by a teacher. if false grades that haven't been filled will be set to -1
*/
async getSemesterGrades(studentYear = 0, semester = 0, onlyFilledGrades: boolean = true) {
return constructGradeSemester(this._axiosInstance!, studentYear - 1, semester, onlyFilledGrades);
}
/**
* get the grades of a year
* @param studentYear the study year
* @param onlyFilledGrades if only to show grades that have been filled by a teacher. if false grades that haven't been filled will be set to -1
*/
async getYearGrades(studentYear = 0, onlyFilledGrades: boolean = true) {
return constructGradeYear(this._axiosInstance!, studentYear - 1, onlyFilledGrades);
}
async getTodayLessons(): Promise<LessonResponse[]> {
return (await this._axiosInstance!.get(defaultApiRoutes.todayTimetable)).data;
}
async getTodayEvents(): Promise<EventResponse[]> {
return (await this._axiosInstance!.get(defaultApiRoutes.todayEvents)).data;
}
async getHomework(): Promise<HomeworkResponse[]> {
return (await this._axiosInstance!.get(defaultApiRoutes.homework)).data;
}
/**
* @param removeEmptyHours Remove hours which there are no lessons on from the response
*/
async getTimeTable(removeEmptyHours: boolean = false): Promise<TimeTable> {
const timetable = await this._axiosInstance!.get(routes.timetable);
return constructTimeTable(timetable.data, removeEmptyHours);
}
async getTestLeftToDo() {
const testsLeft = await this._axiosInstance!.get(routes.changesAndExams);
return constructTestsLeft(testsLeft.data);
}
async getTimeTableChanges() {
const changes = await this._axiosInstance!.get(routes.changesAndExams);
return constructTimeTableChanges(changes.data);
}
async getMainPage() {
return (await this._axiosInstance!.get(baseUrl)).data;
}
async getUsers(query: string): WebTopUsersResponse {
return (await this._axiosInstance!.post(defaultApiRoutes.searchUsers, queryStringify({ value: query }))).data;
}
async getInbox(page: number) {
return constructInbox((await this._axiosInstance!.get(routes.inbox(page))).data);
}
async getMessage(id: number) {
return constructMessage((await this._axiosInstance!.get(routes.message(id))).data, id, this._axiosInstance!);
}
async changePassword(newPassword: string) {
return (await this._axiosInstance!.post(defaultApiRoutes.changePassword, queryStringify({ newPassword }))).data;
}
async changeUsername(currentPassword: string, newUsername: string) {
return (await this._axiosInstance!.post(defaultApiRoutes.changeUsername, queryStringify({ currentPassword, newUsername }))).data;
}
async changePersonalDetails(personalDetails: PersonalDetails) {
const data = personalDetails;
data.emailUponUpdate = data.emailUponUpdate ? 1 : 0;
data.showMyCellphone = data.showMyCellphone ? 1 : 0;
data.showMyEmail = data.showMyEmail ? 1 : 0;
return (await this._axiosInstance!.post(defaultApiRoutes.changeUsername, queryStringify(data))).data;
}
async changePushNotifications(pushNotifications: PushNotifications) {
return (await this._axiosInstance!.post(defaultApiRoutes.changePushNotifications, queryStringify(pushNotifications))).data;
}
async getAllGradeUsers() {
const gradeUsers = [] as WebTopUserData[];
for (let index = 1488; index < 1515; index++) {
const users = await this.getUsers(String.fromCharCode(index));
for (const user of users) {
if (!gradeUsers.includes(user)) gradeUsers.push(user);
}
}
return gradeUsers;
}
}