virtueller-stundenplan/classes/VirtuellerStundenplan.js

385 lines
11 KiB
JavaScript

const axios = require("axios");
const EncryptionHelper = require("./EncryptionHelper");
const { Head, Functions, Message, Homework, SchoolYear, TimetableDay, AbsentSummary } = require("./parsers");
class VirtuellerStundenplan {
baseUrl = "https://virtueller-stundenplan.de/Reservierung/";
#password;
constructor(config) {
this.username = config.username;
this.#password = config.password;
if(config.key) this.key = config.key;
this.holidays = config.holidays;
}
async fetchKey(returnKey=false) {
const url = new URL("RestGetKeyphrase.php", this.baseUrl);
const req = await axios(url.href, {
headers: {
Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'),
},
});
this.key = req.data.KeyPhrase;
return returnKey ? this.key : true;
}
/**
* Returns all enabled and disabled functions.
*
* @returns {Functions}
*/
async getFunctions() {
return new Functions(await this.request("RESTEnabledFunctions.php"));
}
/**
* Returns something with Courses.
* This Response is currently unparsed.
*
* @returns {Any[]}
*/
async getCourseList() {
const data = await this.request("RESTKursliste.php");
return {
head: new Head(data.Msg),
content: data.Kurse,
};
}
/**
* Returns all classes of the school.
*
* @returns {String[]}
*/
async getClassList() {
const data = await this.request("RESTKlassenliste.php");
return {
head: new Head(data.Msg),
content: data.Klassen,
};
}
/**
* Returns all Messages.
*
* @returns {Message[]}
*/
async getMessages() {
const data = await this.request("RESTMessages.php");
let res = [];
data.Messages.forEach(message => {
res.push(new Message(message));
});
return data;
}
/**
* getHomework response
*
* @typedef {Object} getHomeworkResponse
* @property {Head} head - Info to the response
* @property {Homework[]} content
*/
/**
* Returns all Homework (Classbook) entries.
* Date arguments are inclusive.
*
* @param {[String|Date|number]} [from=Date.now()] - First Day
* @param {[String|Date|number]} [to=Date.now()] - Last Day
* @returns {getHomeworkResponse} getHomework response
*/
async getHomework(from=new Date(), to=new Date()) {
const query = new URLSearchParams();
query.set("KlaBuDateVon", this.getDateString(from));
query.set("KlaBuDateBis", this.getDateString(to));
const data = await this.request("RESTHausaufgaben.php", query);
let res = {
head: new Head(data.Msg),
content: [],
};
data.Inhalte.forEach(homework => {
res.content.push(new Homework(homework));
});
return res;
}
/**
* getSchoolYears response
*
* @typedef {Object} getSchoolYearsResponse
* @property {Head} head - Info to the response
* @property {SchoolYear[]} content
*/
/**
* Returns all school years.
*
* @returns {getSchoolYearsResponse} getSchoolYears response
*/
async getSchoolYears() {
const data = await this.request("RESTSchuljahre.php");
let res = {
head: new Head(data.Msg),
content: [],
};
data.Schuljahre.forEach(year => {
res.content.push(new SchoolYear(year));
});
return res;
}
/**
* Returns own grades (performances).
* The response is currently mostly unparsed
*
* @param {string|number} schoolYear ID of the school year.
* @returns {Object} response
* @returns {Head} response.head - Info to the response
* @returns {Object} response.content
*/
async getMyGrades(schoolYear) {
const query = new URLSearchParams();
query.set("SJ", schoolYear);
const data = await this.request("RESTMeineNoten.php", query);
return {
head: new Head(data.Msg),
content: {
performances: data.Leistungen,
totalPerformances: data.LeistungenGesamt,
comment: data.Kommentar,
},
};
}
/**
* getTimetable response
*
* @typedef {Object} getTimetableResponse
* @property {Head} head - Info to the response
* @property {TimetableDay[]} content
*/
/**
* Returns own timetable.
*
* Small note: If you are in only one class, and want to
* fetch it with the class name, the response head will
* be slightly different -> has a bit less metadata
*
* @param {[String|Date|number]} [from=Date.now()] - Date or timestamp
* @param {[String|Date|number]} [Class] - The class of which timetable should be fetched. unset/null to own timetable.
* @returns {getTimetableResponse} getTimetable response
*/
async getTimetable(from=new Date(), Class=null) {
const query = new URLSearchParams();
query.set("KlaBuDateVon", this.getDateString(from));
if(Class) query.set("KLASSE", Class);
const data = await this.request(Class ? "RESTKlassenStundenplan.php" : "RESTMeinStundenplan.php", query);
let res = {
head: new Head(data.Msg),
content: [],
}
for (let i = 0; i < data.Stunden.length; i++) {
const day = [
data.Stunden[i],
data.StundenF[i],
data.StundenR[i],
data.StundenK[i],
data.CellStyles[i],
];
const hasContent = /[\da-zA-Z]/.test(JSON.stringify(day));
res.content[i] = hasContent ? new TimetableDay(day, this.holidays) : null;
}
return res;
}
/**
* Returns own timetable.
* This function is a alias of getTimetable(), but without the Class option
*
* @returns {getTimetableResponse} getTimetable response
*/
async getMyTimetable(from=new Date()) {
return await this.getTimetable(from);
}
/**
* Returns own Infos.
* This Response is currently unparsed.
*
* @returns {Object}
*/
async getMyInfos() {
return await this.request("RESTSuSInfo.php");
}
/**
* Returns own profile picture.
* The response is currently mostly unparsed
*
* @returns {Object} response
* @returns {Head} response.head - Info to the response
* @returns {Object} response.content
*/
async getMyPicture() {
const data = await this.request("RESTSuSMeinBild.php");
return {
head: new Head(data.Msg),
content: {
schoolName: data.Schulname,
picture: data.Bild,
},
};
}
/**
* Returns own BBS ID QR Code.
* This Response is currently unparsed.
*
* @returns {Object}
*/
async getMyBBSIDQR() {
return await this.request("RESTBBSIDQR.php");
}
/**
* Returns own BBS GUID QR Code.
* This Response is currently unparsed.
*
* @returns {Object}
*/
async getMyBBSGUID() {
return await this.request("RESTBBSIDGUID.php");
}
/**
* Reports self as sick.
*
* This Request is untested.
*
* This Response is currently unparsed.
*
* @returns {Object}
*/
async reportAsSick(from=new Date(), to=new Date()) {
const query = new URLSearchParams();
query.set("KlaBuDateVon", this.getDateString(from));
query.set("KlaBuDateBis", this.getDateString(to));
return await this.request("RESTSuSKrankmelden.php", query);
}
/**
* getAbsentDays response
*
* @typedef {Object} getAbsentDaysResponse
* @property {Head} head - Info to the response
* @property {AbsentSummary[]} content
*/
/**
* Returns absent days.
*
* @param {[String|Date|number]} [from=Date.now()] - Date or timestamp
* @param {[String|Date|number]} [to=Date.now()] - Date or timestamp
* @returns {getAbsentDaysResponse} getAbsentDays response
*/
async getAbsentDays(from=new Date(), to=new Date()) {
const query = new URLSearchParams();
query.set("KlaBuDateVon", this.getDateString(from));
query.set("KlaBuDateBis", this.getDateString(to));
const data = await this.request("RESTFehltageliste.php", query);
return {
head: new Head(data.Msg),
content: new AbsentSummary(data),
};
}
/**
* Returns list of exams.
* The response is currently mostly unparsed
*
* @returns {Object} response
* @returns {Head} response.head - Info to the response
* @returns {Object[]} response.content
*/
async getExamList(from=new Date(), to=new Date()) {
const query = new URLSearchParams();
query.set("KlaBuDateVon", this.getDateString(from));
query.set("KlaBuDateBis", this.getDateString(to));
const data = await this.request("RESTKlausurliste.php", query);
return {
head: new Head(data.Msg),
content: data.Klausuren,
};
}
/**
* Subscribes to notifications.
* This Response is currently unparsed.
*
* @returns {Object}
*/
async subscribeToNotifications(Token, Register, IOS) {
const query = new URLSearchParams();
query.set("Token", Token);
query.set("Register", Register);
query.set("IOS", IOS);
return await this.request("RESTDeviceToken.php", query);
}
getDateString(date) {
if(typeof date === "string" && /^\d{4}\-\d{2}\-\d{2}$/.test(date)) {
return date;
}
if(date instanceof Date) {
return date.toISOString().slice(0, 10);
}
if(typeof date === "number") {
return this.getDateString(new Date(date));
}
throw new Error(`"${date}" is not a valid type for date.`);
}
/**
*
* @param {string} url
* @param {URLSearchParams} query Query parameters
* @returns {Any} Response from Server. Error when an error occoured.
*/
async request(url, query) {
if(!this.key && await this.fetchKey() === false) return false;
url = new URL(url, this.baseUrl);
if(query) url.search = query.toString();
url.searchParams.set("key", EncryptionHelper.makeEncryptedParameter(this.username, this.key));
const req = await axios(url.href, {
headers: {
Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'),
},
});
return req.data;
}
}
module.exports = VirtuellerStundenplan