From 18605dfc64be3a1e4d0903cf6eb1d036a5de996f Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 25 Mar 2025 21:48:41 +0100 Subject: [PATCH] Big Update --- README.md | 13 +- classes/VirtuellerStundenplan.js | 303 +++++++++++++++++++++++++------ classes/parsers/AbsentDay.js | 36 ++++ classes/parsers/AbsentSummary.js | 20 ++ classes/parsers/Functions.js | 16 ++ classes/parsers/Head.js | 37 ++++ classes/parsers/Homework.js | 33 ++++ classes/parsers/Message.js | 22 +++ classes/parsers/ParseTools.js | 23 +++ classes/parsers/SchoolYear.js | 24 +++ classes/parsers/Teacher.js | 28 +++ classes/parsers/TimetableDay.js | 33 ++++ classes/parsers/TimetableHour.js | 60 ++++++ classes/parsers/index.js | 9 + npm-shrinkwrap.json | 16 +- package.json | 5 +- 16 files changed, 615 insertions(+), 63 deletions(-) create mode 100644 classes/parsers/AbsentDay.js create mode 100644 classes/parsers/AbsentSummary.js create mode 100644 classes/parsers/Functions.js create mode 100644 classes/parsers/Head.js create mode 100644 classes/parsers/Homework.js create mode 100644 classes/parsers/Message.js create mode 100644 classes/parsers/ParseTools.js create mode 100644 classes/parsers/SchoolYear.js create mode 100644 classes/parsers/Teacher.js create mode 100644 classes/parsers/TimetableDay.js create mode 100644 classes/parsers/TimetableHour.js create mode 100644 classes/parsers/index.js diff --git a/README.md b/README.md index ec23ff2..df2486c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ You can install this library from [npm](https://npmjs.com/package/virtueller-stu npm install virtueller-stundenplan ``` +# Note + +This project in **very early developement**, expect things to break and change. + # Usage This is a example on how to use the package: @@ -17,7 +21,10 @@ This is a example on how to use the package: ```javascript const { VirtuellerStundenplan } = require("virtueller-stundenplan"); -const vs = new VirtuellerStundenplan("user@example.com", "Password"); +const vs = new VirtuellerStundenplan({ + username: "user@example.com", + password: "Password" +}); (async() => { const timetable = await vs.getMyTimetable(); @@ -25,7 +32,3 @@ const vs = new VirtuellerStundenplan("user@example.com", "Password"); console.log(timetable); })() ``` - -# Note - -This project in **very early developement**, expect things to break and change. \ No newline at end of file diff --git a/classes/VirtuellerStundenplan.js b/classes/VirtuellerStundenplan.js index 998ff17..ebf4070 100644 --- a/classes/VirtuellerStundenplan.js +++ b/classes/VirtuellerStundenplan.js @@ -1,110 +1,269 @@ 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/"; - constructor(username, password, key) { - this.username = username; - this.password = password; - if(key) this.key = key; + #password; + + constructor(config) { + this.username = config.username; + this.#password = config.password; + if(config.key) this.key = config.key; + + //this.teacherList = config.teachers; } async fetchKey(returnKey=false) { const url = new URL("RestGetKeyphrase.php", this.baseUrl); - try { - const req = await axios(url.href, { - headers: { - Authorization: "Basic " + Buffer.from(this.username + ":" + this.password).toString('base64'), - }, - }); + const req = await axios(url.href, { + headers: { + Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'), + }, + }); - this.key = req.data.KeyPhrase; - } catch (err) { - return false; - } + this.key = req.data.KeyPhrase; return returnKey ? this.key : true; } - async getEnabledFunctions() { - return await this.request("RESTEnabledFunctions.php"); + /** + * 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() { - return await this.request("RESTKursliste.php"); + const data = await this.request("RESTKursliste.php"); + return { + head: new Head(data.Msg), + content: data.Kurse, + }; } + /** + * Returns all Messages. + * + * @returns {Message[]} + */ async getMessages() { - return await this.request("RESTMessages.php"); + 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} from - * @param {String|Date} to - * @returns {Object} + * @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)); - return await this.request("RESTHausaufgaben.php", query); - } - async getSchoolYears() { - return await this.request("RESTSchuljahre.php"); + 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 * - * @param {string|number} schoolYear ID of the school year - * @returns + * @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); - return await this.request("RESTMeineNoten.php", query); + 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) : 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()) { - const query = new URLSearchParams(); - query.set("KlaBuDateVon", this.getDateString(from)); - return await this.request("RESTMeinStundenplan.php", query); - } - - async getTimetable(Class, from=new Date()) { - const query = new URLSearchParams(); - query.set("KlaBuDateVon", this.getDateString(from)); - query.set("KLASSE", Class); - return await this.request("RESTKlassenStundenplan.php", query); + 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() { - return await this.request("RESTSuSMeinBild.php"); + const data = await this.request("RESTSuSMeinBild.php"); + + return { + head: new Head(data.Msg), + content: { + schoolName: data.Schulname, + picture: data.Bild, + }, + }; } /** - * BBS ID QR + * Returns own BBS ID QR Code. + * This Response is currently unparsed. + * + * @returns {Object} */ async getMyBBSIDQR() { return await this.request("RESTBBSIDQR.php"); } /** - * BBS GUID QR + * 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)); @@ -112,20 +271,60 @@ class VirtuellerStundenplan { 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)); - return await this.request("RESTFehltageliste.php", query); + + 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)); - return await this.request("RESTKlausurliste.php", query); + + 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); @@ -141,6 +340,9 @@ class VirtuellerStundenplan { 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.`); } @@ -157,16 +359,11 @@ class VirtuellerStundenplan { if(query) url.search = query.toString(); url.searchParams.set("key", EncryptionHelper.makeEncryptedParameter(this.username, this.key)); - let req; - try { - req = await axios(url.href, { - headers: { - Authorization: "Basic " + Buffer.from(this.username + ":" + this.password).toString('base64'), - }, - }); - } catch (err) { - return err; - } + const req = await axios(url.href, { + headers: { + Authorization: "Basic " + Buffer.from(this.username + ":" + this.#password).toString('base64'), + }, + }); return req.data; } diff --git a/classes/parsers/AbsentDay.js b/classes/parsers/AbsentDay.js new file mode 100644 index 0000000..becbf27 --- /dev/null +++ b/classes/parsers/AbsentDay.js @@ -0,0 +1,36 @@ +const ParseTools = require("./ParseTools"); + +class AbsentDay { + constructor(data) { + this.date = ParseTools.parseDateTime(data.Datum); + this.hour = data.Stunde; + this.status = data.Status; + this.comment = ParseTools.isFilled(data.Bemerkung); + } + + get present() { + switch (this.status) { + case "o": + case "G5": + case "L5": + case "V5": + return true; + case "--": + case "K": + case "E": + case "A": + case "( )": + case "U": + return false; + default: + return null; + } + } + + get absent() { + if(this.present === null) return null; + return !this.present; + } +} + +module.exports = AbsentDay \ No newline at end of file diff --git a/classes/parsers/AbsentSummary.js b/classes/parsers/AbsentSummary.js new file mode 100644 index 0000000..ea9dc49 --- /dev/null +++ b/classes/parsers/AbsentSummary.js @@ -0,0 +1,20 @@ +const ParseTools = require("./ParseTools"); +const AbsentDay = require("./AbsentDay"); + +class AbsentSummary { + days = []; + + constructor(data) { + this.presentDays = ParseTools.daysToArray(data.Summary['Anwesend am']); + this.absentDays = ParseTools.daysToArray(data.Summary['Fehlte am']); + this.partlyAbsentDays = ParseTools.daysToArray(data.Summary['Fehlte teilweise am']); + this.unexcusedDays = ParseTools.daysToArray(data.Summary['unentschuldigte Tage']); + this.excusedDays = ParseTools.daysToArray(data.Summary['unentschuldigte Tage']); + + data.Details.forEach((day, i) => { + this.days[i] = new AbsentDay(day); + }); + } +} + +module.exports = AbsentSummary \ No newline at end of file diff --git a/classes/parsers/Functions.js b/classes/parsers/Functions.js new file mode 100644 index 0000000..2a9f9ee --- /dev/null +++ b/classes/parsers/Functions.js @@ -0,0 +1,16 @@ +class Functions { + #data; + + constructor(data) { + this.#data = data; + + this.enabled = data.Enabled; + this.disabled = data.Disabled; + } + + getAll() { + return [...this.enabled, ...this.disabled]; + } +} + +module.exports = Functions \ No newline at end of file diff --git a/classes/parsers/Head.js b/classes/parsers/Head.js new file mode 100644 index 0000000..d4c029a --- /dev/null +++ b/classes/parsers/Head.js @@ -0,0 +1,37 @@ +const ParseTools = require("./ParseTools"); + +class Head { + #data; + + constructor(data) { + this.#data = data; + + this.httpStatus = data.Status ? Number(data.Status) : null; + + this.from = data.KlaBuDateVon ? ParseTools.parseDateTime(data.KlaBuDateVon) : undefined; + this.to = data.KlaBuDateBis ? ParseTools.parseDateTime(data.KlaBuDateBis) : undefined; + + if(data['Von h']) this.fromHour = data['Von h']; + if(data['Bis h']) this.toHour = data['Bis h']; + + if(data['Von Wochentag']) this.fromWeekDay = data['Von Wochentag']; + if(data['Bis Wochentag']) this.toWeekDay = data['Bis Wochentag']; + + if(data.AktuelleID) this.currentID = Number(data.AktuelleID); + + if(data.Schuljahr) this.schoolYear = data.Schuljahr; + + if(data.AnzahlLeistungen) this.totalPerformances = data.AnzahlLeistungen; + + // Timetable: + if(ParseTools.isFilled(data.Name)) this.name = data.Name; + if(ParseTools.isFilled(data.Klasse)) this.class = data.Klasse; + if(data.Schulnummer) this.schoolNumber = data.Schulnummer; + if(ParseTools.isFilled(data.InKlasse)) this.inClass = data.InKlasse; + if(ParseTools.isFilled(data.InKursen)) this.inCourses = data.InKursen; + if(typeof data.EffPlan !== "undefined") this.effectiveTimetable = !!data.EffPlan; + if(typeof data.CompactPlan !== "undefined") this.compactTimetable = !!data.CompactPlan; + } +} + +module.exports = Head \ No newline at end of file diff --git a/classes/parsers/Homework.js b/classes/parsers/Homework.js new file mode 100644 index 0000000..304b93e --- /dev/null +++ b/classes/parsers/Homework.js @@ -0,0 +1,33 @@ +const PT = require("./ParseTools"); +const Teacher = require("./Teacher"); + +class Homework { + #data; + + constructor(data) { + this.#data = data; + + this.id = data.ID; + this.date = PT.parseDateTime(this.rawDate, "dd.MM.yyyy"); + this.hour = PT.isFilled(data.Stunde) ? Number(data.Stunde): null; + this.class = PT.isFilled(data.Klasse, true); + this.course = PT.isFilled(data.Kurs, true); + this.course = PT.isFilled(data.Kursschiene, true); + this.teacher = PT.isFilled(data.LK, true); + //data.Vertretung - value in testing always "" or "0" + this.subject = PT.isFilled(data.Fach, true); + this.room = PT.isFilled(data.Raum, true); + this.note = PT.isFilled(data.Bemerkung, true); + this.homework = PT.isFilled(data.Hausaufgaben, true); + } + + get rawDate() { + return this.#data.Datum; + } + + get raw() { + return this.#data; + } +} + +module.exports = Homework \ No newline at end of file diff --git a/classes/parsers/Message.js b/classes/parsers/Message.js new file mode 100644 index 0000000..d085a2a --- /dev/null +++ b/classes/parsers/Message.js @@ -0,0 +1,22 @@ +const ParseTools = require("./ParseTools"); + +class Message { + #data; + + constructor(data) { + this.#data = data; + + this.content = data.Message; + this.timestamp = ParseTools.parseDateTime(data.rawTimestamp, "yyyy-MM-dd HH:mm:ss"); + this.target = data.Target; + this.type = data.Typ; + this.course = data.Kurs; + this.class = data.Klasse; + } + + get rawTimestamp() { + return this.#data.Datum; + } +} + +module.exports = Message \ No newline at end of file diff --git a/classes/parsers/ParseTools.js b/classes/parsers/ParseTools.js new file mode 100644 index 0000000..342c707 --- /dev/null +++ b/classes/parsers/ParseTools.js @@ -0,0 +1,23 @@ +const { DateTime } = require("luxon"); + +class ParseTools { + static parseDateTime(date, format="yyyy-MM-dd") { + return DateTime.fromFormat(date, format, { zone: "Europe/Berlin" }); + } + + static isFilled(value, returnValOrNull=false) { + const res = value !== "" && value !== "-"; + return returnValOrNull ? (res ? value : null) : res; + } + + static daysToArray(days, seperator=", ", format="dd.MM.yyyy") { + let res = []; + days.split(seperator).forEach(day => { + if(day === "") return; + res.push(this.parseDateTime(day, format)); + }); + return res; + } +} + +module.exports = ParseTools \ No newline at end of file diff --git a/classes/parsers/SchoolYear.js b/classes/parsers/SchoolYear.js new file mode 100644 index 0000000..f34f9cc --- /dev/null +++ b/classes/parsers/SchoolYear.js @@ -0,0 +1,24 @@ +const ParseTools = require("./ParseTools"); + +class SchoolYear { + #data; + + constructor(data) { + this.#data = data; + + this.id = data.ID; + this.title = data.SJ; + this.from = ParseTools.parseDateTime(this.rawFrom); + this.to = ParseTools.parseDateTime(this.rawTo); + } + + get rawFrom() { + return this.#data.von; + } + + get rawTo() { + return this.#data.bis; + } +} + +module.exports = SchoolYear \ No newline at end of file diff --git a/classes/parsers/Teacher.js b/classes/parsers/Teacher.js new file mode 100644 index 0000000..2c6d8ff --- /dev/null +++ b/classes/parsers/Teacher.js @@ -0,0 +1,28 @@ +class Teacher { + /** + * Creates a Instance of Teacher + * + * @param {String} teacher - Code for the teacher. + * @param {Object} teacherList - Object of teachers. + */ + constructor(teacher, teacherList) { + /** @returns {String} Teacher's code */ + this.code = teacher; + + teacher = teacherList && teacherList[this.code] ? teacherList[this.code] : {}; + + /** @returns {String} First name(s) of the teacher. If unset the teacher's code. */ + this.firstName = teacher.firstName ?? this.code; + /** @returns {String} Last name of the teacher. If unset the teacher's code. */ + this.lastName = teacher.lastName ?? this.code; + + let name = []; + if(teacher.firstName) name.push(teacher.firstName); + if(teacher.lastName) name.push(teacher.lastName); + + /** @returns {String} Full name of the teacher. If unset the teacher's code. */ + this.fullName = name.join(" ") || this.code; + } +} + +module.exports = Teacher \ No newline at end of file diff --git a/classes/parsers/TimetableDay.js b/classes/parsers/TimetableDay.js new file mode 100644 index 0000000..d14c251 --- /dev/null +++ b/classes/parsers/TimetableDay.js @@ -0,0 +1,33 @@ +const TimetableHour = require("./TimetableHour"); + +class TimetableDay { + first = null; + last = null; + + constructor(data) { + this.hours = []; + + //console.log(data) + + for (let i = 0; i < data[0].length; i++) { + const hour = [ + data[0][i], + data[1][i], + data[2][i], + data[3][i], + data[4][i], + ] + + const hasContent = /[\da-zA-Z]/.test(JSON.stringify(hour)); + + if(hasContent) { + if(this.first === null) this.first = i; + this.last = i; + } + + this.hours[i] = hasContent ? new TimetableHour(hour) : null; + } + } +} + +module.exports = TimetableDay; \ No newline at end of file diff --git a/classes/parsers/TimetableHour.js b/classes/parsers/TimetableHour.js new file mode 100644 index 0000000..b46a0d5 --- /dev/null +++ b/classes/parsers/TimetableHour.js @@ -0,0 +1,60 @@ +const ParseTools = require("./ParseTools"); + +const parser = (text) => { + if(!ParseTools.isFilled(text)) return null; + + const data = { + original: text, + changes: false, + note: null, + planned: text, + actual: text, + }; + + const hasBold = text.includes(" "); + const hasDel = text.includes(""); + + if(hasBold) { + data.actual = text.split(" ")[1].split("")[0]; + if(hasDel) data.actual = data.actual.substring(1); + } + + if(hasDel) { + data.planned = text.split("")[1].split("")[0]; + data.changes = true; + if(text.substring(text.search("") + 6) !== "") { // If it has reasoning/note + + if(!hasBold) { + data.actual = null; + data.note = text.substring(text.search("") + 6); + } else data.note = text.substring(text.search("") + 5); + + data.note = data.note.trim(); + + if(data.note === "") data.note = null; + + } else data.actual = null; + } else data.planned = data.actual; + + return data; +} + +class TimetableDay { + #data = []; + + anyChanges = false; + + constructor(data) { + data.forEach((part, i) => { + this.#data[i] = parser(part); + if(this.#data[i]?.changes) this.anyChanges = true; + }); + + this.teacher = this.#data[0]; + this.subject = this.#data[1]; + this.room = this.#data[2]; + this.exam = this.#data[3]; + } +} + +module.exports = TimetableDay; \ No newline at end of file diff --git a/classes/parsers/index.js b/classes/parsers/index.js new file mode 100644 index 0000000..00bfed6 --- /dev/null +++ b/classes/parsers/index.js @@ -0,0 +1,9 @@ +module.exports = { + Head: require("./Head"), + Functions: require("./Functions"), + Message: require("./Message"), + Homework: require("./Homework"), + SchoolYear: require("./SchoolYear"), + TimetableDay: require("./TimetableDay"), + AbsentSummary: require("./AbsentSummary"), +} \ No newline at end of file diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 0db8085..7c7407d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,15 +1,16 @@ { "name": "virtueller-stundenplan", - "version": "0.0.2", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "virtueller-stundenplan", - "version": "0.0.2", + "version": "1.0.0", "license": "CC-BY-NC-4.0", "dependencies": { - "axios": "^1.8.4" + "axios": "^1.8.4", + "luxon": "^3.5.0" } }, "node_modules/asynckit": { @@ -254,6 +255,15 @@ "node": ">= 0.4" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/package.json b/package.json index f097bbd..eb19acd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "virtueller-stundenplan", - "version": "0.0.2", + "version": "1.0.0", "description": "A library to access the data from virtueller-stundenplan.org", "main": "index.js", "scripts": { @@ -18,6 +18,7 @@ }, "license": "CC-BY-NC-4.0", "dependencies": { - "axios": "^1.8.4" + "axios": "^1.8.4", + "luxon": "^3.5.0" } }