├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── lib ├── CookieJar.js ├── RawData.js ├── ResponseTypings.js └── utils.js ├── package.json ├── src ├── ASC.js ├── Application.js ├── Assignment.js ├── Attachment.js ├── Class.js ├── Classroom.js ├── Edupage.js ├── Grade.js ├── Homework.js ├── Lesson.js ├── Message.js ├── Parent.js ├── Period.js ├── Plan.js ├── Season.js ├── Student.js ├── Subject.js ├── Teacher.js ├── Test.js ├── Timetable.js ├── User.js ├── constants.js ├── enums.js └── exceptions.js ├── tsconfig.json └── typings ├── index.d.ts ├── lib ├── CookieJar.d.ts ├── RawData.d.ts ├── ResponseTypings.d.ts └── utils.d.ts └── src ├── ASC.d.ts ├── Application.d.ts ├── Assignment.d.ts ├── Attachment.d.ts ├── Class.d.ts ├── Classroom.d.ts ├── Edupage.d.ts ├── Grade.d.ts ├── Homework.d.ts ├── Lesson.d.ts ├── Message.d.ts ├── Parent.d.ts ├── Period.d.ts ├── Plan.d.ts ├── Season.d.ts ├── Student.d.ts ├── Subject.d.ts ├── Teacher.d.ts ├── Test.d.ts ├── Timetable.d.ts ├── User.d.ts ├── constants.d.ts ├── enums.d.ts └── exceptions.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | test/ 2 | .vscode/ 3 | node_modules/ -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Edupage = require("./src/Edupage"); 2 | const User = require("./src/User"); 3 | const Student = require("./src/Student"); 4 | const Teacher = require("./src/Teacher"); 5 | const Class = require("./src/Class"); 6 | const Classroom = require("./src/Classroom"); 7 | const ASC = require("./src/ASC"); 8 | const Assignment = require("./src/Assignment"); 9 | const Attachment = require("./src/Attachment"); 10 | const Grade = require("./src/Grade"); 11 | const Homework = require("./src/Homework"); 12 | const Lesson = require("./src/Lesson"); 13 | const Message = require("./src/Message"); 14 | const Parent = require("./src/Parent"); 15 | const Period = require("./src/Period"); 16 | const Plan = require("./src/Plan"); 17 | const Season = require("./src/Season"); 18 | const Subject = require("./src/Subject"); 19 | const Test = require("./src/Test"); 20 | const Timetable = require("./src/Timetable"); 21 | const Application = require("./src/Application"); 22 | 23 | const { 24 | GENDER, 25 | ENDPOINT, 26 | ENTITY_TYPE, 27 | API_STATUS, 28 | ASSIGNMENT_TYPE, 29 | ASSIGNMENT_GROUP, 30 | TIMELINE_ITEM_TYPE 31 | } = require("./src/enums"); 32 | 33 | const { 34 | LoginError, 35 | ParseError, 36 | EdupageError, 37 | APIError, 38 | MessageError, 39 | AttachmentError, 40 | FatalError 41 | } = require("./src/exceptions"); 42 | 43 | module.exports = { 44 | ASC, 45 | Application, 46 | Assignment, 47 | Attachment, 48 | Class, 49 | Classroom, 50 | Edupage, 51 | Grade, 52 | Homework, 53 | Lesson, 54 | Message, 55 | Parent, 56 | Period, 57 | Plan, 58 | Season, 59 | Student, 60 | Subject, 61 | Teacher, 62 | Test, 63 | Timetable, 64 | User, 65 | GENDER, 66 | ENDPOINT, 67 | ENTITY_TYPE, 68 | API_STATUS, 69 | ASSIGNMENT_TYPE, 70 | ASSIGNMENT_GROUP, 71 | TIMELINE_ITEM_TYPE, 72 | LoginError, 73 | ParseError, 74 | EdupageError, 75 | APIError, 76 | MessageError, 77 | AttachmentError, 78 | FatalError 79 | }; -------------------------------------------------------------------------------- /lib/CookieJar.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | const fetch = require("node-fetch"); 3 | const http = require("http"); 4 | const {iterate} = require("./utils"); 5 | 6 | class CookieJar { 7 | /** 8 | * Creates an instance of CookieJar. 9 | * @memberof CookieJar 10 | */ 11 | constructor() { 12 | /** 13 | * @type {CookieJar.Cookie[]} 14 | */ 15 | this.cookies = []; 16 | 17 | if(arguments.length) this.setCookie.apply(this, arguments); 18 | } 19 | 20 | /** 21 | * Adds cookie to the Jar 22 | * @param {string | CookieJar.Cookie | http.ServerResponse | http.IncomingMessage | fetch.Response} cookie Cookie name (requires second parameter), Cookie String, CookieJar.Cookie object, ServerResponseLike object 23 | * @param {string} [value=undefined] 24 | * @param {Object} [options={}] 25 | * @return {CookieJar} 26 | * @memberof CookieJar 27 | */ 28 | setCookie(cookie, value = undefined, options = {}) { 29 | //Set by name=value 30 | if(typeof value !== "undefined") { 31 | var _cookie = new CookieJar.Cookie(); 32 | _cookie.name = cookie.trim(); 33 | _cookie.value = (value ?? "").trim(); 34 | 35 | for(var [i, key, _value] of iterate(options)) { 36 | if(_value == true) _cookie.flags.push(key); 37 | else if(_value == false) _cookie.flags.splice(_cookie.flags.indexOf(key), 1); 38 | else _cookie.props[CookieJar.Cookie.formatKeyword(key) || key] = _value; 39 | } 40 | 41 | this._addCookiesToJar(_cookie); 42 | return this; 43 | } 44 | 45 | //Set by Cookie object 46 | if(cookie instanceof CookieJar.Cookie) { 47 | this._addCookiesToJar(cookie); 48 | return this; 49 | } 50 | 51 | if(typeof cookie == "object") { 52 | var cookieString = cookie?.headers?.cookie; 53 | var header = cookie?.headers?.raw?.()?.["set-cookie"]; 54 | var jsonObject = "cookies" in cookie ? cookie.cookies : null; 55 | 56 | //Set by Request object 57 | if(cookieString) { 58 | var cookieStringArray = cookieString.split(";"); 59 | var cookies = CookieJar.Cookie.parse(cookieStringArray); 60 | this._addCookiesToJar(...cookies); 61 | } 62 | 63 | //Set by Response object 64 | if(header) { 65 | var cookies = CookieJar.Cookie.parse(header); 66 | this._addCookiesToJar(...cookies); 67 | } 68 | 69 | //Set by JSON object 70 | if(jsonObject) { 71 | for(var cookieObject of jsonObject) { 72 | var _cookie = new CookieJar.Cookie(); 73 | _cookie.name = cookieObject.name; 74 | _cookie.value = cookieObject.value; 75 | _cookie.props = cookieObject.props; 76 | _cookie.flags = cookieObject.flags; 77 | this._addCookiesToJar(_cookie); 78 | } 79 | } 80 | return this; 81 | } 82 | 83 | //TODO: Set by cookie string 84 | 85 | throw new TypeError("Cannot set cookie: " + cookie); 86 | } 87 | 88 | /** 89 | * Retrns cookie object found by name 90 | * @param {string} name Cookie name 91 | * @return {CookieJar.Cookie} Cookie object if found, otherwise undefined 92 | * @memberof CookieJar 93 | */ 94 | getCookie(name) { 95 | this._removeExpiredCookies(); 96 | return this.cookies.find(cookie => cookie.name == name); 97 | } 98 | 99 | /** 100 | * Removes cookie from the Jar 101 | * @param {string | CookieJar.Cookie} cookie 102 | * @return {CookieJar.Cookie} Deleted cookie 103 | * @memberof CookieJar 104 | */ 105 | deleteCookie(cookie) { 106 | var _cookie = null; 107 | if(typeof cookie === "string") _cookie = this.getCookie(cookie); 108 | else if(cookie instanceof CookieJar.Cookie) _cookie = cookie; 109 | else throw new TypeError("Invalid cookie: " + cookie); 110 | 111 | var id = this.cookies.indexOf(_cookie); 112 | if(id < 0 || !_cookie) return null; 113 | else this.cookies.splice(id, 1); 114 | return _cookie; 115 | } 116 | 117 | /** 118 | * Sends header with cookies 119 | * @param {http.ServerResponse} response Server response object 120 | * @param {boolean} [full=true] Include cookie properties and flags 121 | * @return {CookieJar} 122 | * @memberof CookieJar 123 | */ 124 | sendCookies(response, full = true) { 125 | this._removeExpiredCookies(); 126 | response.setHeader("Set-Cookie", this.cookies.map(e => e.toString(full))); 127 | return this; 128 | } 129 | 130 | /** 131 | * Converts Cookie object to cookie string 132 | * @param {boolean} [full=true] Include cookie properties and flags 133 | * @return {string} Cookie String 134 | * @memberof CookieJar 135 | */ 136 | toString(full = true) { 137 | this._removeExpiredCookies(); 138 | return this.cookies.map(e => e.toString(full)).join(""); 139 | } 140 | 141 | /** 142 | * Checks if the Jar is empty 143 | * @return {boolean} true if Jar is empty, otherwise false 144 | * @memberof CookieJar 145 | */ 146 | isEmpty() { 147 | this._removeExpiredCookies(); 148 | return this.cookies.length == 0; 149 | } 150 | 151 | /** 152 | * Checks if the Jar contains cookie with certain name 153 | * @param {string} name Cookie name 154 | * @return {boolean} true if Jar contians cookie with certain name, otherwise false 155 | * @memberof CookieJar 156 | */ 157 | includes(name) { 158 | this._removeExpiredCookies(); 159 | return !!this.getCookie(name); 160 | } 161 | 162 | /** 163 | * Adds cookies to the Jar 164 | * @param {CookieJar.Cookie[]} cookies 165 | * @memberof CookieJar 166 | */ 167 | _addCookiesToJar(...cookies) { 168 | for(var cookie of cookies) { 169 | this.deleteCookie(cookie.name); 170 | this.cookies.push(cookie); 171 | } 172 | this._removeExpiredCookies(); 173 | } 174 | 175 | /** 176 | * Removes expired cookies from the Jar 177 | * @memberof CookieJar 178 | */ 179 | _removeExpiredCookies() { 180 | for(var cookie of this.cookies) { 181 | if(cookie.props["Expires"] && new Date(cookie.props["Expires"]) < new Date()) this.deleteCookie(cookie); 182 | } 183 | } 184 | } 185 | 186 | class Cookie { 187 | /** 188 | * @typedef {Object} CookieProperties 189 | * @prop {string} [Expires] The maximum lifetime of the cookie as an HTTP-date timestamp. 190 | * @prop {string} [Max-Age] Number of seconds until the cookie expires. A zero or negative number will expire the cookie immediately. 191 | * @prop {string} [Domain] Host to which the cookie will be sent. 192 | * @prop {string} [Path] A path that must exist in the requested URL, or the browser won't send the `Cookie` header. 193 | * @prop {string} [SameSite] Controls whether a cookie is sent with cross-origin requests, providing some protection against cross-site request forgery attacks (CSRF). 194 | */ 195 | 196 | /** 197 | * Creates an instance of Cookie. 198 | */ 199 | constructor() { 200 | this.name = ""; 201 | this.value = ""; 202 | 203 | /** 204 | * @type {CookieProperties} 205 | */ 206 | this.props = {}; 207 | 208 | /** 209 | * @type {Array<"Secure" | "HttpOnly">} 210 | */ 211 | this.flags = []; 212 | } 213 | 214 | /** 215 | * Convert cookie to cookie string 216 | * @override 217 | * @param {boolean} [full=true] Include cookie properties and flags 218 | * @return {string} Cookie String 219 | */ 220 | toString(full = true) { 221 | var head = `${this.name}=${this.value}; `; 222 | var props = Object.reduce(this.props, (prev, {key, value}) => prev + `${key}=${value}; `, ""); 223 | var flags = this.flags.join("; "); 224 | 225 | return full ? (head + props + flags + (flags ? "; " : "")) : head; 226 | } 227 | 228 | static keywords = ["Expires", "Max-Age", "Domain", "Path", "Secure", "HttpOnly", "SameSite"]; 229 | static formatKeyword(key) { 230 | for(var keyword of this.keywords) { 231 | if(keyword.toLowerCase() == key.toLowerCase()) return keyword; 232 | } 233 | return false; 234 | } 235 | 236 | static parse(cookieStringArray) { 237 | return cookieStringArray.map(cookieString => { 238 | var cookie = new CookieJar.Cookie(); 239 | var properties = cookieString.split(/;\s*/); 240 | 241 | for(var property of properties) { 242 | if(!property) continue; 243 | 244 | var {key, value, flag} = property.match(/(?:(?.*?)=(?.*)|(?.*))/)?.groups || {}; 245 | 246 | if(key) { 247 | if(!cookie.name && !cookie.value) { 248 | cookie.name = key.trim(); 249 | cookie.value = value.trim(); 250 | } else { 251 | cookie.props[this.formatKeyword(key) || key] = value; 252 | } 253 | } else if(flag) { 254 | cookie.flags.push(flag); 255 | } else { 256 | //throw new TypeError("Failed to parse cookie: '" + property + "'"); 257 | console.warn("Failed to parse cookie: '" + property + "'"); 258 | } 259 | } 260 | 261 | return cookie; 262 | }); 263 | } 264 | } 265 | CookieJar.Cookie = Cookie; 266 | 267 | module.exports = CookieJar; -------------------------------------------------------------------------------- /lib/RawData.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Holds raw data received from server 3 | * @typedef {Object} RawDataObject 4 | */ 5 | class RawData { 6 | /** 7 | * Creates an instance of RawData. 8 | * @param {RawDataObject} [_data=null] 9 | * @memberof RawData 10 | */ 11 | constructor(_data = null) { 12 | Object.defineProperty(this, "_data", { 13 | enumerable: false, 14 | writable: true 15 | }); 16 | 17 | /** 18 | * Holds raw data received from server 19 | * @type {RawDataObject} 20 | */ 21 | this._data = _data; 22 | } 23 | } 24 | 25 | module.exports = RawData; -------------------------------------------------------------------------------- /lib/ResponseTypings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {Object} MAppData 3 | * @prop {string} loggedUser 4 | * @prop {number} loggedChild 5 | * @prop {string} loggedUserName 6 | * @prop {string} lang 7 | * @prop {string} edupage 8 | * @prop {string} school_type 9 | * @prop {number} timezonediff 10 | * @prop {string} school_country 11 | * @prop {string} schoolyear_turnover 12 | * @prop {number} firstDayOfWeek 13 | * @prop {string} sort_name_col 14 | * @prop {number} selectedYear 15 | * @prop {number} autoYear 16 | * @prop {string} year_turnover 17 | * @prop {boolean[]} vyucovacieDni 18 | * @prop {string} server 19 | * @prop {number} syncIntervalMultiplier 20 | * @prop {any} ascspl 21 | * @prop {boolean} jePro 22 | * @prop {boolean} jeZUS 23 | * @prop {boolean} rtl 24 | * @prop {boolean} rtlAvailable 25 | * @prop {string} uidsgn 26 | * @prop {boolean} webpageadmin 27 | * @prop {EduRequestProps} edurequestProps 28 | * @prop {string} gsechash 29 | * @prop {string} email 30 | * @prop {any[]} userrights 31 | * @prop {boolean} isAdult 32 | */ 33 | 34 | /** 35 | * @typedef {Object} EduRequestProps 36 | * @prop {string} edupage 37 | * @prop {string} lang 38 | * @prop {string} school_name 39 | * @prop {string} school_country 40 | * @prop {string} school_state 41 | * @prop {string} schoolyear_turnover 42 | * @prop {any[]} custom_turnover 43 | * @prop {number} firstDayOfWeek 44 | * @prop {number[]} weekendDays 45 | * @prop {string} timezone 46 | * @prop {string} sort_name_col 47 | * @prop {{date: string, time: string}} dtFormats 48 | * @prop {string} jsmodulemode 49 | * @prop {string} loggedUser 50 | * @prop {any[]} loggedUserRights 51 | * @prop {boolean} isAsc 52 | * @prop {boolean} isAgenda 53 | */ 54 | 55 | /** 56 | * @typedef {Object} MAuthUser 57 | * @prop {string} userid 58 | * @prop {string} typ 59 | * @prop {string} edupage 60 | * @prop {string} edumeno 61 | * @prop {string} eduheslo 62 | * @prop {string} firstname 63 | * @prop {string} lastname 64 | * @prop {string} esid 65 | * @prop {MAppData} appdata 66 | * @prop {string} portal_userid 67 | * @prop {string} portal_email 68 | * @prop {"1" | null} need2fa 69 | */ 70 | 71 | /** 72 | * @typedef {Object} MAuthResponse 73 | * @prop {MAuthUser[]} users 74 | * @prop {boolean} needEdupage 75 | * @prop {string} edid 76 | * @prop {any | undefined} [t2fasec=undefined] 77 | */ 78 | 79 | //Required to enable type checking 80 | module.exports = undefined; -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | const btoa = data => Buffer.from(data).toString("base64"); 3 | const atob = data => Buffer.from(data, "base64").toString(); 4 | 5 | // eslint-disable-next-line valid-jsdoc 6 | /** 7 | * Create iterable key-value pairs. 8 | * @param {any} iterable Iterable Object, Array or any other value. 9 | * @returns {([index: number, key: string, value: any] | [index: number, value: any])[]} Iterator 10 | * @example 11 | * iterate([4, 5, 6]); //[[0, 4], [1, 5], [2, 6]] 12 | * iterate([]); //[] 13 | * iterate({a: 4, b: 5, c: 6}); //[[0, "a", 4], [1, "b", 5], [2, "c", 6]] 14 | * iterate({}); //[] 15 | * iterate("bar"); //[[0, "b"], [1, "a"], [2, "r"]] 16 | * iterate(11); //[[0, 11]] 17 | * iterate(true); //[[0, true]] 18 | * iterate(false); //[[0, false]] 19 | * iterate(null); //[] 20 | * iterate(undefined); //[] 21 | */ 22 | function iterate(iterable) { 23 | if(iterable === undefined || iterable === null) return []; 24 | if(iterable instanceof Array || typeof iterable === "string" && (iterable = [...iterable])) return iterable.entries(); 25 | else { 26 | const iterator = Object.entries(iterable).map((value, i) => [i, ...value]); 27 | return iterator.length || iterable.constructor === Object ? iterator : [[0, iterable]]; 28 | } 29 | } 30 | 31 | Object.reduce = function(object, callbackfn, initialValue = Object.values(object)[0]) { 32 | const keys = Object.keys(object); 33 | let previousValue = initialValue; 34 | let currentIndex; 35 | 36 | if(typeof callbackfn !== "function") 37 | throw new TypeError(callbackfn + " is not a function"); 38 | if(typeof initialValue === "undefined" && !keys.length) 39 | throw new TypeError("Reduce of empty object with no initial value"); 40 | 41 | for(currentIndex = +(initialValue == Object.values(object)[0]); currentIndex < keys.length; currentIndex++) { 42 | var key = keys[currentIndex]; 43 | var value = object[key]; 44 | previousValue = callbackfn( 45 | previousValue, 46 | {key, value}, 47 | currentIndex, 48 | object 49 | ); 50 | } 51 | 52 | return previousValue; 53 | }; 54 | 55 | module.exports = { 56 | btoa, 57 | atob, 58 | iterate 59 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edupage-api", 3 | "version": "0.8.3", 4 | "description": "Simple node.js package to manage your EduPage account.", 5 | "main": "index.js", 6 | "typings": "types/EdupageAPI.d.ts", 7 | "scripts": { 8 | "debug": "set DEBUG=edupage:* && node ./test/test.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/loumadev/EdupageAPI.git" 13 | }, 14 | "keywords": [ 15 | "edupage", 16 | "edupage-org", 17 | "asc", 18 | "api", 19 | "edu", 20 | "education", 21 | "school", 22 | "study", 23 | "node", 24 | "async", 25 | "await", 26 | "promise" 27 | ], 28 | "author": "Jaroslav Louma", 29 | "license": "GPL-3.0", 30 | "bugs": { 31 | "url": "https://github.com/loumadev/EdupageAPI/issues" 32 | }, 33 | "homepage": "https://github.com/loumadev/EdupageAPI#readme", 34 | "dependencies": { 35 | "debug": "^4.3.2", 36 | "node-fetch": "^2.6.0" 37 | }, 38 | "devDependencies": { 39 | "typescript": "^4.5.5" 40 | } 41 | } -------------------------------------------------------------------------------- /src/ASC.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const Edupage = require("./Edupage"); 5 | const {FatalError, ParseError} = require("./exceptions"); 6 | 7 | debug.log = console.log.bind(console); 8 | 9 | /** 10 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 11 | */ 12 | 13 | class ASC extends RawData { 14 | /** 15 | * Creates an instance of ASC. 16 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 17 | * @param {Edupage} [edupage=null] Edupage instance to use. 18 | * @memberof ASC 19 | */ 20 | constructor(data = {}, edupage = null) { 21 | super(data); 22 | 23 | /** 24 | * Edupage instance associated to this object. 25 | * @type {Edupage | string} 26 | */ 27 | this.edupage = edupage; 28 | 29 | /** 30 | * Userstring of the currently logged in user. 31 | * @type {string} 32 | */ 33 | this.loggedUser = data.req_props.loggedUser; 34 | 35 | /** 36 | * Rights of the currently logged in user. 37 | * @type {any[]} 38 | */ 39 | this.loggedUserRights = data.req_props.loggedUserRights; 40 | 41 | /** 42 | * Timezone for the school. 43 | * @type {string} 44 | */ 45 | this.timezone = data.req_props.timezone; 46 | 47 | /** 48 | * Weekend days for the school. 49 | * @type {number[]} 50 | */ 51 | this.weekendDays = data.req_props.weekendDays; 52 | 53 | 54 | /** 55 | * Edupage server. 56 | * @type {string} 57 | * @example "edupage61" 58 | */ 59 | this.server = data.server; 60 | 61 | /** 62 | * Full name of the school. 63 | * @type {string} 64 | */ 65 | this.schoolName = data.school_name; 66 | 67 | /** 68 | * Language code of the currently logged in user. 69 | * @type {string} 70 | */ 71 | this.lang = data.lang; 72 | 73 | /** 74 | * Country code of the school. 75 | * @type {string} 76 | */ 77 | this.schoolCountry = data.school_country; 78 | 79 | /** 80 | * Turnover of the school in format "MM-DD" 81 | * @type {string} 82 | * @example "08-01" 83 | */ 84 | this.schoolyearTurnover = data.schoolyear_turnover; 85 | 86 | /** 87 | * Secure hash used for API calls 88 | * @type {string} 89 | * @example "94c3f4d3" 90 | */ 91 | this.gsecHash = data.gsechash; 92 | 93 | /** 94 | * Id used for API calls 95 | * @type {string} 96 | */ 97 | this.gpid = null; 98 | 99 | /** 100 | * List of currently available gpid ids. 101 | * @type {string[]} 102 | */ 103 | this.gpids = []; 104 | 105 | /** 106 | * First day of the week (0 = Sunday, 1 = Monday, ...). 107 | * @type {number} 108 | */ 109 | this.firstDayOfWeek = data.firstDayOfWeek; 110 | } 111 | 112 | /** 113 | * Parses the HTML page and returns raw ASC data. 114 | * @static 115 | * @param {string} html HTML page to parse. 116 | * @return {RawDataObject} Parsed data. 117 | * @memberof ASC 118 | */ 119 | static parse(html) { 120 | const data = {}; 121 | const matches = [...html.matchAll(/ASC\.([a-zA-Z0-9_$]+)\s?=\s?([\s\S]+?);/g)]; 122 | 123 | if(!matches.length) return FatalError.throw(new ParseError("Failed to parse ASC data from html"), {html}); 124 | 125 | for(const [match, key, value] of matches) { 126 | if(value.startsWith("function")) continue; 127 | 128 | try { 129 | data[key] = JSON.parse(value); 130 | } catch(e) { 131 | return FatalError.throw(new ParseError("Failed to parse JSON from ASC html"), {html, matches, match, key, value, e}); 132 | } 133 | } 134 | 135 | return data; 136 | } 137 | } 138 | 139 | module.exports = ASC; -------------------------------------------------------------------------------- /src/Application.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const {APIError, EdupageError, ParseError, FatalError} = require("./exceptions"); 4 | const RawData = require("../lib/RawData"); 5 | const Edupage = require("./Edupage"); 6 | const {ENDPOINT, API_STATUS, TIMELINE_ITEM_TYPE} = require("./enums"); 7 | 8 | debug.log = console.log.bind(console); 9 | 10 | 11 | /** 12 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 13 | */ 14 | 15 | /** 16 | * @typedef {import("./enums").EntityType} EntityType 17 | */ 18 | 19 | /** 20 | * @experimental 21 | * @class Application 22 | * @extends {RawData} 23 | */ 24 | class Application extends RawData { 25 | /** 26 | * Creates an instance of Application. 27 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 28 | * @param {Edupage} [edupage=null] Edupage instance to use. 29 | * @memberof Application 30 | */ 31 | constructor(data = {}, edupage = null) { 32 | super(data); 33 | 34 | /** 35 | * Edupage instance associated to this object. 36 | * @type {Edupage} 37 | */ 38 | this.edupage = edupage; 39 | 40 | /** 41 | * Id of the application. 42 | * @type {string} 43 | */ 44 | this.id = data.id; 45 | 46 | /** 47 | * Date from which the application is valid. 48 | * @type {Date} 49 | */ 50 | this.dateFrom = data.datefrom ? new Date(data.datefrom) : null; 51 | 52 | /** 53 | * Date until which the application is valid. 54 | * @type {Date} 55 | */ 56 | this.dateTo = data.dateto ? new Date(data.dateto) : null; 57 | 58 | /** 59 | * Name of the application. 60 | * @type {string} 61 | */ 62 | this.name = data.name; 63 | 64 | /** 65 | * Array of parameter names to be provided to `Application.post(...)` call (e.g. date, price...) 66 | * @type {string[]} 67 | */ 68 | this.parameters = data.dataColumns; 69 | 70 | /** 71 | * Type of entities this application is available for. 72 | * @type {EntityType} 73 | */ 74 | this.availableFor = data.user; 75 | 76 | /** 77 | * Flag indicating whether the application is enabled. 78 | * @type {boolean} 79 | */ 80 | this.isEnabled = data.enabled; 81 | 82 | /** 83 | * Unknown property 84 | * @type {boolean} 85 | */ 86 | this.isTextOptional = data.textOptional || false; 87 | 88 | /** 89 | * Unknown property 90 | * @type {boolean} 91 | */ 92 | this.isAdvancedWorkflow = data.isAdvancedWorkflow || false; 93 | 94 | /** 95 | * Unknown property 96 | * @type {boolean} 97 | */ 98 | this.isSimpleWorkflow = data.isSimpleWorkflow || false; 99 | 100 | if(this.edupage) Application.prototype.init.call(this); 101 | } 102 | 103 | /** 104 | * Creates Application draft 105 | * @return {Promise} Created draft id 106 | * @memberof Application 107 | */ 108 | async createDraft() { 109 | if(!this.edupage) throw new EdupageError(`Application does not have assigned Edupage instance yet`); 110 | 111 | const res = await this.edupage.api({ 112 | url: ENDPOINT.TIMELINE_CREATE_ITEM, 113 | data: { 114 | typ: TIMELINE_ITEM_TYPE.PROCESS, 115 | selectedProcessType: this.id, 116 | selectedUser: this.edupage.user.getUserString(false) 117 | } 118 | }); 119 | 120 | if(res.status !== API_STATUS.OK) { 121 | error(`Received invalid status from the server '${res.status}'`); 122 | throw new APIError(`Failed to create draft for the application: Invalid status received '${res.status}'`, res); 123 | } 124 | 125 | const draft = (res.redirect?.match(/draft=(\d+)/) || "")[1]; 126 | 127 | if(!draft) return FatalError.throw(new ParseError("Failed to parse draft id from redirect URL"), {res, draft, _data: this._data}); 128 | 129 | return draft; 130 | } 131 | 132 | /** 133 | * Posts the application with input parameters 134 | * @experimental 135 | * @param {RawDataObject} [parameters={}] Object of parameters from `Application.parameters` 136 | * @param {string} [draftId=null] Your custom created draft ID. If the paramater is not specified (or provided value is falsy), new draft is created internally. 137 | * @return {Promise} `true` if the method was successful, otherwise `false` 138 | * @memberof Application 139 | */ 140 | async post(parameters = {}, draftId = null) { 141 | return new Promise((resolve, reject) => { 142 | const tryFetch = async _count => { 143 | this.edupage.api({ 144 | url: ENDPOINT.DASHBOARD_GCALL, 145 | method: "POST", 146 | type: "text", 147 | data: new URLSearchParams({ 148 | gpid: draftId || await this.createDraft(), 149 | gsh: this.edupage.ASC.gsecHash, 150 | action: "create", 151 | _LJSL: "2052", //Seems like some -static- (it might vary) parameter (Loaded JS Libraries), keep it for a safety 152 | ...parameters 153 | }).toString(), 154 | encodeBody: false 155 | }).then(res => { 156 | const success = /"border_error":\s*false/.test(res) && !/"border_error":\s*true/.test(res); 157 | 158 | resolve(success); 159 | }).catch(err => { 160 | if(err.retry) { 161 | debug(`[Application] Got retry signal, retrying...`); 162 | tryFetch(err.count + 1); 163 | } else { 164 | error(`[Application] Could not post application`, err); 165 | reject(new EdupageError("Failed to post application: " + err.message)); 166 | } 167 | }); 168 | }; 169 | tryFetch(-1); 170 | }); 171 | } 172 | 173 | /** 174 | * Initializes instance. 175 | * @param {Edupage} [edupage=null] Edupage instance to use. 176 | * @memberof Application 177 | */ 178 | init(edupage = null) { 179 | if(edupage) this.edupage = edupage; 180 | } 181 | } 182 | 183 | module.exports = Application; -------------------------------------------------------------------------------- /src/Assignment.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const Edupage = require("./Edupage"); 5 | const Grade = require("./Grade"); 6 | const Period = require("./Period"); 7 | const Subject = require("./Subject"); 8 | const Teacher = require("./Teacher"); 9 | const User = require("./User"); 10 | const {ASSIGNMENT_TYPE, ASSIGNMENT_GROUP, ENDPOINT} = require("./enums"); 11 | const {EdupageError, APIError} = require("./exceptions"); 12 | 13 | debug.log = console.log.bind(console); 14 | 15 | /** 16 | * @typedef {import("./Homework")} Homework 17 | */ 18 | 19 | /** 20 | * @typedef {import("./Test")} Test 21 | */ 22 | 23 | /** 24 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 25 | */ 26 | 27 | class Assignment extends RawData { 28 | /** 29 | * Creates an instance of Assignment. 30 | * @param {RawDataObject} [data={}] Initializes instance with raw data. 31 | * @param {Edupage} [edupage=null] Edupage instance to use. 32 | * @memberof Assignment 33 | */ 34 | constructor(data = {}, edupage = null) { 35 | super(data); 36 | 37 | /** 38 | * Edupage instance associated to this object. 39 | * @type {Edupage} 40 | */ 41 | this.edupage = edupage; 42 | 43 | /** 44 | * Homework id of this assignment. 45 | * @type {string} 46 | * @example "superid:16647" 47 | */ 48 | this.id = data.homeworkid; 49 | 50 | /** 51 | * Super assignment id. 52 | * @type {string} 53 | */ 54 | this.superId = data.e_superid; 55 | 56 | /** 57 | * Author of the assignment. 58 | * @type {User | Teacher} 59 | */ 60 | this.owner = null; 61 | 62 | /** 63 | * Subject associated to this assignment. 64 | * @type {Subject} 65 | */ 66 | this.subject = null; 67 | 68 | /** 69 | * Title of the assignment. 70 | * @type {string} 71 | */ 72 | this.title = data.name; 73 | 74 | /** 75 | * Description of the assignment. 76 | * @type {string} 77 | */ 78 | this.details = data.details; 79 | 80 | /** 81 | * Date when the assignment was created. 82 | * @type {Date} 83 | */ 84 | this.creationDate = new Date(data.datecreated || data.timestamp); 85 | 86 | /** 87 | * Date from which the assignment is available for the students. 88 | * @type {Date} 89 | */ 90 | this.fromDate = new Date(data.datetimefrom || data.datefrom); 91 | 92 | /** 93 | * Date by which the assignment is available to students. 94 | * @type {Date} 95 | */ 96 | this.toDate = new Date(data.datetimeto || data.dateto); 97 | 98 | /** 99 | * Time to complete the assignment in seconds 100 | * @type {number} 101 | */ 102 | this.duration = Math.floor((this.toDate.getTime() - this.fromDate.getTime()) / 1000); 103 | 104 | /** 105 | * Period when the assignment is available. 106 | * @type {Period} 107 | */ 108 | this.period = null; 109 | 110 | /** 111 | * Id of the test. 112 | * @type {string} 113 | */ 114 | this.testId = data.testid; 115 | 116 | /** 117 | * Type of the assignment. 118 | * @type {ASSIGNMENT_TYPE} 119 | */ 120 | this.type = data.typ.split("|")[1] || data.typ; 121 | 122 | /** 123 | * Homework id 2. 124 | * @type {string} 125 | * @example "subid:4B1340557B68DE71" 126 | */ 127 | this.hwkid = data.hwkid; 128 | 129 | /** 130 | * Number of cards in the material. 131 | * @type {number} 132 | */ 133 | this.cardsCount = data.etestCards; 134 | 135 | /** 136 | * Number of answer cards in the material. 137 | * @type {number} 138 | */ 139 | this.answerCardsCount = data.etestAnswerCards; 140 | 141 | /** 142 | * The evaluation state of the assignment. 143 | * @type {string} 144 | */ 145 | this.state = data.stavhodnotenia; 146 | 147 | /** 148 | * Flag indicating if the assignment is new. 149 | * @type {boolean} 150 | */ 151 | this.isSeen = this.state != "new"; 152 | 153 | /** 154 | * Comment of the assignment. 155 | * @type {string} 156 | */ 157 | this.comment = data.komentarPridelenie || ""; 158 | 159 | /** 160 | * Result of the assignment. 161 | * @type {string} 162 | */ 163 | this.result = data.vysledok; 164 | 165 | /** 166 | * Flag indicating if the assignment is finished. 167 | * @type {boolean} 168 | */ 169 | this.isFinished = !!+data.skoncil; 170 | 171 | /** 172 | * Date when the assignment was last updated. 173 | * @type {Date} 174 | */ 175 | this.stateUpdatedDate = data.studentStav?.timestamp ? new Date(data.studentStav?.timestamp) : null; 176 | 177 | /** 178 | * User who last updated the assignment. 179 | * @type {User | Teacher} 180 | */ 181 | this.stateUpdatedBy = null; 182 | 183 | /** 184 | * List of grades associated to this assignment. 185 | * @type {Grade[]} 186 | */ 187 | this.grades = []; 188 | 189 | 190 | if(this.edupage) Assignment.prototype.init.call(this); 191 | } 192 | 193 | /** 194 | * Initializes the object. 195 | * @param {Edupage} [edupage=null] Edupage instance to use. 196 | * @memberof Assignment 197 | */ 198 | init(edupage = null) { 199 | if(edupage) this.edupage = edupage; 200 | 201 | this.owner = this.edupage.getUserByUserString(this._data.userid); 202 | this.subject = this.edupage.subjects.find(e => e.id == this._data.predmetid); 203 | 204 | if(this._data.period) this.period = this.edupage.periods.find(e => e.id == this._data.period); 205 | if(this._data.studentStav?.nastavil_userid) this.stateUpdatedBy = this.edupage.getUserByUserString(this._data.studentStav?.nastavil_userid); 206 | 207 | this.grades = this.edupage.grades.filter(e => this.superId == e.superId); 208 | this.grades.forEach(e => e.assignment = this); 209 | } 210 | 211 | /** 212 | * Fetches results + material data for assignment 213 | * Usually the structure is: `{resultsData: {...}, materialData: {...}, ...}` 214 | * Fetched data are cached into `this._data._resultsData` 215 | * @return {Promise} Non-parsed raw data object 216 | * @memberof Assignment 217 | */ 218 | async getData() { 219 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 220 | 221 | //Load data 222 | const res = await this.edupage.api({ 223 | url: ENDPOINT.ELEARNING_TEST_RESULTS, 224 | data: { 225 | "superid": this.superId 226 | } 227 | }); 228 | 229 | //Request failed 230 | if(!res.superid) { 231 | error(`[Reply] Received invalid status from the server '${res.status}'`); 232 | throw new APIError(`Failed to send message: Invalid status received '${res.status}'`, res); 233 | } 234 | 235 | //Cache data 236 | this._data._resultsData = res; 237 | 238 | return res; 239 | } 240 | 241 | /** 242 | * Creates an instance of Homework or Test from homework data. 243 | * @static 244 | * @param {RawDataObject} [data={}] 245 | * @param {Edupage} [edupage=null] 246 | * @return {Assignment | Homework | Test} 247 | * @memberof Assignment 248 | */ 249 | static from(data = {}, edupage = null) { 250 | const type = data.typ.split("|")[1] || data.typ; 251 | 252 | if(ASSIGNMENT_GROUP.HOMEWORK.includes(type)) return new (require("./Homework"))(data, edupage); 253 | if( 254 | ASSIGNMENT_GROUP.EXAM.includes(type) || 255 | ASSIGNMENT_GROUP.TEST.includes(type) 256 | ) return new (require("./Test"))(data, edupage); 257 | 258 | //Other types might be implemented later on 259 | return new Assignment(data, edupage); 260 | } 261 | } 262 | 263 | module.exports = Assignment; -------------------------------------------------------------------------------- /src/Attachment.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | const Edupage = require("./Edupage"); 3 | const {btoa} = require("../lib/utils"); 4 | 5 | /** 6 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 7 | */ 8 | 9 | class Attachment extends RawData { 10 | /** 11 | * Creates an instance of Attachment. 12 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 13 | * @param {Edupage} [edupage=null] Edupage instance to use. 14 | * @memberof Attachment 15 | */ 16 | constructor(data = {}, edupage = null) { 17 | super(data); 18 | 19 | /** 20 | * Edupage instance associated to this object 21 | * @type {Edupage} 22 | */ 23 | this.edupage = edupage; 24 | 25 | /** 26 | * Name of the attachment file 27 | * @type {string} 28 | */ 29 | this.name = data.name; 30 | 31 | /** 32 | * Absolute URL path to the attachment file uploaded on Edupage cloud server 33 | * @type {string} 34 | */ 35 | this.src = null; 36 | 37 | if(this.edupage) Attachment.prototype.init.call(this); 38 | } 39 | 40 | /** 41 | * Initializes instance. 42 | * @param {Edupage} [edupage=null] 43 | * @memberof Attachment 44 | */ 45 | init(edupage = null) { 46 | if(edupage) this.edupage = edupage; 47 | 48 | this.src = `https://${this.edupage.user.origin}.edupage.org` + (this._data.file || this._data.src); 49 | } 50 | 51 | /** 52 | * Converts the `Attachment` object to JSON object 53 | * @return {Object} JSON object contianing the attachment name as key and the attachment URL as value 54 | * @memberof Attachment 55 | */ 56 | toJSON() { 57 | return {[this._data.file]: this.name}; 58 | } 59 | } 60 | 61 | Attachment.formBoundary = "------EdupageAPIBoundary" + btoa((+new Date * Math.random()).toString()).slice(0, 16); 62 | 63 | module.exports = Attachment; -------------------------------------------------------------------------------- /src/Class.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | const Classroom = require("./Classroom"); 3 | const Edupage = require("./Edupage"); 4 | const Teacher = require("./Teacher"); 5 | 6 | /** 7 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 8 | */ 9 | 10 | class Class extends RawData { 11 | /** 12 | * Creates an instance of Class. 13 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 14 | * @param {Edupage} [edupage=null] Edupage instance to use. 15 | * @memberof Class 16 | */ 17 | constructor(data = {}, edupage = null) { 18 | super(data); 19 | 20 | /** 21 | * Edupage instance associated to this object 22 | * @type {Edupage} 23 | */ 24 | this.edupage = edupage; 25 | 26 | 27 | /** 28 | * Grade of the class 29 | * @type {number} 30 | */ 31 | this.grade = +data.grade ?? null; 32 | 33 | /** 34 | * ID of the class 35 | * @type {string} 36 | */ 37 | this.id = data.id; 38 | 39 | /** 40 | * Name of the class 41 | * @type {string} 42 | */ 43 | this.name = data.name; 44 | 45 | /** 46 | * Short name of the class 47 | * @type {string} 48 | */ 49 | this.short = data.short; 50 | 51 | /** 52 | * Classroom associated to this class 53 | * @type {Classroom} 54 | */ 55 | this.classroom = null; 56 | 57 | /** 58 | * Teacher associated to this class 59 | * @type {Teacher} 60 | */ 61 | this.teacher = null; 62 | 63 | /** 64 | * Teacher 2 associated to this class 65 | * @type {Teacher} 66 | */ 67 | this.teacher2 = null; 68 | 69 | if(this.edupage) Class.prototype.init.call(this); 70 | } 71 | 72 | /** 73 | * Initializes instance. 74 | * @param {Edupage} [edupage=null] Edupage instance to use. 75 | * @memberof Class 76 | */ 77 | init(edupage = null) { 78 | if(edupage) this.edupage = edupage; 79 | 80 | this.classroom = this.edupage.classrooms.find(e => e.id == this._data.classroomid); 81 | this.teacher = this.edupage.teachers.find(e => e.id == this._data.teacherid); 82 | this.teacher2 = this.edupage.teachers.find(e => e.id == this._data.teacher2id); 83 | } 84 | } 85 | 86 | module.exports = Class; -------------------------------------------------------------------------------- /src/Classroom.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | const Edupage = require("./Edupage"); 3 | 4 | /** 5 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 6 | */ 7 | 8 | class Classroom extends RawData { 9 | /** 10 | * Creates an instance of Classroom. 11 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 12 | * @param {Edupage} [edupage=null] Edupage instance to use. 13 | * @memberof Classroom 14 | */ 15 | constructor(data = {}, edupage = null) { 16 | super(data); 17 | 18 | /** 19 | * Edupage instance associated to this object. 20 | * @type {Edupage} 21 | */ 22 | this.edupage = edupage; 23 | 24 | /** 25 | * Unknown property 26 | * @type {boolean} 27 | */ 28 | this.cb_hidden = "cb_hidden" in data ? data.cb_hidden : null; 29 | 30 | /** 31 | * Classroom id 32 | * @type {string} 33 | */ 34 | this.id = data.id; 35 | 36 | /** 37 | * Classroom name 38 | * @type {string} 39 | */ 40 | this.name = data.name; 41 | 42 | /** 43 | * Classroom short name 44 | * @type {string} 45 | */ 46 | this.short = data.short; 47 | 48 | if(this.edupage) Classroom.prototype.init.call(this); 49 | } 50 | 51 | /** 52 | * Initializes instance. 53 | * @param {Edupage} [edupage=null] Edupage instance to use. 54 | * @memberof Classroom 55 | */ 56 | init(edupage = null) { 57 | if(edupage) this.edupage = edupage; 58 | } 59 | } 60 | 61 | module.exports = Classroom; 62 | -------------------------------------------------------------------------------- /src/Edupage.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const warn = require("debug")("edupage:warn"); 3 | const error = require("debug")("edupage:error"); 4 | const fs = require("fs"); 5 | const stream = require("stream"); 6 | const {default: fetch} = require("node-fetch"); 7 | const Student = require("./Student"); 8 | const Teacher = require("./Teacher"); 9 | const User = require("./User"); 10 | const {btoa, iterate} = require("../lib/utils"); 11 | const {ENDPOINT, API_STATUS, TIMELINE_ITEM_TYPE} = require("./enums"); 12 | const {SESSION_PING_INTERVAL_MS} = require("./constants"); 13 | const Class = require("./Class"); 14 | const Classroom = require("./Classroom"); 15 | const Parent = require("./Parent"); 16 | const RawData = require("../lib/RawData"); 17 | const Subject = require("./Subject"); 18 | const Period = require("./Period"); 19 | const ASC = require("./ASC"); 20 | const {LoginError, EdupageError, AttachmentError, APIError, FatalError, ParseError} = require("./exceptions"); 21 | const Timetable = require("./Timetable"); 22 | const Message = require("./Message"); 23 | const Plan = require("./Plan"); 24 | const Attachment = require("./Attachment"); 25 | const Grade = require("./Grade"); 26 | const Season = require("./Season"); 27 | const Homework = require("./Homework"); 28 | const Assignment = require("./Assignment"); 29 | const Test = require("./Test"); 30 | const Application = require("./Application"); 31 | 32 | debug.log = console.log.bind(console); 33 | 34 | /** 35 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 36 | */ 37 | 38 | /** 39 | * @typedef {import("./enums").APIEndpoint} APIEndpoint 40 | */ 41 | 42 | 43 | class Edupage extends RawData { 44 | /** 45 | * Creates an instance of Edupage. 46 | * @memberof Edupage 47 | */ 48 | constructor() { 49 | super(); 50 | 51 | /** 52 | * Instance of currently logged in user. 53 | * All the information in the edupage instance is related to this user. 54 | * This user is used to make all requests to the Edupage internal APIs. 55 | * @type {User | Teacher | Student} 56 | */ 57 | this.user = null; 58 | 59 | /** 60 | * List of all seasons or semesters. 61 | * @type {Season[]} 62 | */ 63 | this.seasons = []; 64 | 65 | /** 66 | * List of all students in the school (if the user is a teacher, otherwise the list contains classmates only). 67 | * @type {Student[]} 68 | */ 69 | this.students = []; 70 | 71 | /** 72 | * List of all teachers in the school. 73 | * @type {Teacher[]} 74 | */ 75 | this.teachers = []; 76 | 77 | /** 78 | * List of all classes in the school. 79 | * @type {Class[]} 80 | */ 81 | this.classes = []; 82 | 83 | /** 84 | * List of all classrooms in the school. 85 | * @type {Classroom[]} 86 | */ 87 | this.classrooms = []; 88 | 89 | /** 90 | * List of all parents for owned class (if the user is a teacher, otherwise the list contains parents of the student). 91 | * @type {Parent[]} 92 | */ 93 | this.parents = []; 94 | 95 | /** 96 | * List of all subjects in the school. 97 | * @type {Subject[]} 98 | */ 99 | this.subjects = []; 100 | 101 | /** 102 | * List of all periods in the school. 103 | * @type {Period[]} 104 | */ 105 | this.periods = []; 106 | 107 | /** 108 | * List of all timetables currently fetched. 109 | * There are always timetables for 2 - 4 days (current + next, if friday + weekend). 110 | * This list is used as a cache to avoid fetching the same timetable multiple times. 111 | * @type {Timetable[]} 112 | */ 113 | this.timetables = []; 114 | 115 | /** 116 | * List of all message items on the timeline for currently logged in user. 117 | * Contains visible messages as well as hidden confirmations and helper records 118 | * @type {Message[]} 119 | */ 120 | this.timelineItems = []; 121 | 122 | /** 123 | * List of all visible timeline items on the timeline for currently logged in user. 124 | * @type {Message[]} 125 | */ 126 | this.timeline = []; 127 | 128 | /** 129 | * List of all plans for currently logged in user. 130 | * @type {Plan[]} 131 | */ 132 | this.plans = []; 133 | 134 | /** 135 | * List of all assignments for currently logged in user. 136 | * @type {Assignment[]} 137 | */ 138 | this.assignments = []; 139 | 140 | /** 141 | * List of all assignments type of homework for currently logged in user. 142 | * @type {Homework[]} 143 | */ 144 | this.homeworks = []; 145 | 146 | /** 147 | * List of all assignments type of test for currently logged in user. 148 | * @type {Test[]} 149 | */ 150 | this.tests = []; 151 | 152 | /** 153 | * List of all applications in the school. 154 | * @experimental 155 | * @type {Application[]} 156 | */ 157 | this.applications = []; 158 | 159 | 160 | /** 161 | * Instance of an ASC object. 162 | * Can be used to access some general school data. 163 | * @type {ASC} 164 | */ 165 | this.ASC = null; 166 | 167 | /** 168 | * Current school year. 169 | * If the current school year is "2020/2021", then this value is `2020`. 170 | * @type {number} 171 | */ 172 | this.year = null; 173 | 174 | /** 175 | * Base edupage URL. 176 | * @example "https://example.edupage.org" 177 | * @type {string} 178 | */ 179 | this.baseUrl = null; 180 | 181 | //Used internally to keep track of Timeout object for session pinging 182 | Object.defineProperty(this, "_sessionPingTimeout", { 183 | enumerable: false, 184 | writable: true 185 | }); 186 | } 187 | 188 | /** 189 | * @typedef {import("./User").LoginOptions} LoginOptions 190 | */ 191 | 192 | /** 193 | * Logs user in for this instance 194 | * 195 | * @param {string} [username=this.user.credentials.username] Username of the user 196 | * @param {string} [password=this.user.credentials.password] Password of the user 197 | * @param {LoginOptions} [options] Login options 198 | * @return {Promise} Returns a promise that resolves with the `User` object if successful. If the 2FA is requested by the Edupage, the promise will resolve with `null`. 199 | * @memberof Edupage 200 | */ 201 | async login(username = this.user.credentials.username, password = this.user.credentials.password, options) { 202 | return new Promise((resolve, reject) => { 203 | const temp = new User(); 204 | temp.login(username, password, options).then(async user => { 205 | //Assign properties 206 | this.user = temp; 207 | this.baseUrl = `https://${this.user.origin}.edupage.org`; 208 | 209 | //Update edupage data 210 | await this.refresh().catch(reject); 211 | 212 | this.scheduleSessionPing(); 213 | 214 | resolve(this.user); 215 | }).catch(reject); 216 | }); 217 | } 218 | 219 | /** 220 | * Refreshes all fields in `Edupage` instance 221 | * @memberof Edupage 222 | */ 223 | async refresh() { 224 | //Refresh global Edupage data 225 | await this.refreshEdupage(false); 226 | 227 | //Refresh timeline data 228 | await this.refreshTimeline(false); 229 | 230 | //Refresh created timeline items 231 | await this.refreshCreatedItems(false); 232 | 233 | //Refresh grades 234 | await this.refreshGrades(false); 235 | 236 | //Update Edupage fields 237 | this._updateInternalValues(); 238 | } 239 | 240 | /** 241 | * Fetches global Edupage data (such as teachers, classes, classrooms, subjects...) 242 | * and updates internal values. 243 | * This includes ASC refresh. 244 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 245 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 246 | * have to recalculate internal values every time and save some performance. 247 | * @memberof Edupage 248 | */ 249 | async refreshEdupage(_update = true) { 250 | //Fetch data as HTML 251 | const _html = await this.api({ 252 | url: ENDPOINT.DASHBOARD_GET_USER, 253 | method: "GET", 254 | type: "text" 255 | }); 256 | const _json = Edupage.parse(_html); 257 | this._data = {...this._data, ..._json}; 258 | this.year = this._data._edubar.autoYear || this._data._edubar.selectedYear; 259 | 260 | //Parse ASC data from fetched HTML 261 | const _asc = ASC.parse(_html); 262 | this._data = {...this._data, ASC: _asc}; 263 | this.ASC = new ASC(this._data.ASC, this); 264 | 265 | if(_update) this._updateInternalValues(); 266 | } 267 | 268 | /** 269 | * Fetches timeline data (messages, notifications...) 270 | * and updates internal values. 271 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 272 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 273 | * have to recalculate internal values every time and save some performance. 274 | * @memberof Edupage 275 | */ 276 | async refreshTimeline(_update = true) { 277 | //Fetch timeline data 278 | const _timeline = await this.api({ 279 | url: ENDPOINT.TIMELINE_GET_DATA, 280 | data: { 281 | datefrom: this.getYearStart(false) 282 | } 283 | }); 284 | 285 | //Add values to source object 286 | this._data = {...this._data, ..._timeline}; 287 | 288 | if(_update) this._updateInternalValues(); 289 | } 290 | 291 | /** 292 | * Fetches timeline items data created by currently 293 | * logged user and updates internal values. 294 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 295 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 296 | * have to recalculate internal values every time and save some performance. 297 | * @memberof Edupage 298 | */ 299 | async refreshCreatedItems(_update = true) { 300 | //Fetch created items data 301 | const _created = await this.api({ 302 | url: ENDPOINT.TIMELINE_GET_CREATED_ITEMS, 303 | data: { 304 | odkedy: this.getYearStart() 305 | } 306 | }); 307 | 308 | //Add values to source object 309 | this._data = {...this._data, _created}; 310 | 311 | if(_update) this._updateInternalValues(); 312 | } 313 | 314 | /** 315 | * Fetches grades of currently logged 316 | * user and updates internal values. 317 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 318 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 319 | * have to recalculate internal values every time and save some performance. 320 | * @memberof Edupage 321 | */ 322 | async refreshGrades(_update = true) { 323 | //Fetch created items data 324 | const _grades_html = await this.api({ 325 | url: ENDPOINT.GRADES_DATA, 326 | method: "GET", 327 | type: "text" 328 | }); 329 | 330 | //Parse values and add them to source object 331 | const _grades = Grade.parse(_grades_html); 332 | this._data = {...this._data, _grades}; 333 | 334 | if(_update) this._updateInternalValues(); 335 | } 336 | 337 | /** 338 | * Internal method to update all the fields in Edupage instance. Should be called 339 | * after any of "refresh" methods (in case `_update` argument is set to `false`) 340 | * @memberof Edupage 341 | */ 342 | _updateInternalValues() { 343 | //return; 344 | //Transform events Object into Array here rather than on each Grade iteration for better performance 345 | this._data._grades._events = {}; 346 | iterate(this._data._grades.data?.vsetkyUdalosti || {}) 347 | .forEach(([i, provider, object]) => this._data._grades._events[provider] = Object.values(object)); 348 | 349 | //Merge all timeline items together and filter them down 350 | this._data.timelineItems = [...this._data.timelineItems, ...this._data._created.data.items].filter((e, i, arr) => 351 | //Remove duplicated items 352 | i == arr.findIndex(t => ( 353 | t.timelineid == e.timelineid 354 | )) 355 | //Remove useless items created only for notification to be sent 356 | && !(e.typ == TIMELINE_ITEM_TYPE.MESSAGE && e.pomocny_zaznam && arr.some(t => t.timelineid == e.reakcia_na)) 357 | ); 358 | 359 | //Reset values to prevent mutating the old ones 360 | this.timelineItems = []; 361 | this.assignments = []; 362 | this.homeworks = []; 363 | this.tests = []; 364 | 365 | //Parse json and create Objects 366 | this.seasons = Object.values(this._data._grades?.settings?.obdobia || {}).map(data => new Season(data)); 367 | this.classes = Object.values(this._data.dbi?.classes || {}).map(data => new Class(data)); 368 | this.classrooms = Object.values(this._data.dbi?.classrooms || {}).map(data => new Classroom(data, this)); 369 | this.teachers = Object.values(this._data.dbi?.teachers || {}).map(data => new Teacher(data, this)); 370 | this.parents = Object.values(this._data.dbi?.parents || {}).map(data => new Parent(data, this)); 371 | this.students = Object.values(this._data.dbi?.students || {}).map(data => new Student(data, this)); 372 | this.subjects = Object.values(this._data.dbi?.subjects || {}).map(data => new Subject(data)); 373 | this.periods = Object.values(this._data.dbi?.periods || {}).map(data => new Period(data)); 374 | this.plans = Object.values(this._data.dbi?.plans || {}).map(data => new Plan(data, this)); 375 | this.timetables = iterate(this._data?.dp?.dates || {}).map(([i, date, data]) => new Timetable(data, date)); 376 | this.grades = Object.values(this._data._grades?.data?.vsetkyZnamky || {}).map(data => new Grade(data, this)); 377 | this.applications = Object.values(this._data.dbi?.process_types || {}).map(data => new Application(data, this)); 378 | 379 | //Create assignments and add them to arrays 380 | this._data.homeworks.forEach(data => { 381 | const assignment = Assignment.from(data, this); 382 | 383 | if(assignment instanceof Homework) this.homeworks.push(assignment); 384 | if(assignment instanceof Test) this.tests.push(assignment); 385 | 386 | this.assignments.push(assignment); 387 | }); 388 | 389 | //Create Message objects for each timeline item 390 | this._data.timelineItems 391 | .sort((a, b) => new Date(a.cas_pridania).getTime() - new Date(b.cas_pridania).getTime()) 392 | .forEach(data => this.timelineItems.unshift(new Message(data, this))); 393 | 394 | //Filter out confirmation messages 395 | //TODO: filter out types prefixed with `h_`? 396 | this.timeline = this.timelineItems.filter(e => e.type != TIMELINE_ITEM_TYPE.CONFIRMATION); 397 | 398 | //Init objects if needed 399 | this.seasons.forEach(e => e.init(this)); 400 | this.classes.forEach(e => e.init(this)); 401 | this.timetables.forEach(e => e.init(this)); 402 | 403 | //Parse current user 404 | const _temp = this.user; 405 | const user = this.getUserByUserString(this._data.userid); 406 | 407 | //Error handling 408 | if(!user) throw new EdupageError(`Failed to load currently logged in user`); 409 | 410 | //Assign properties to current user 411 | this.user = User.from(this.ASC.loggedUser, user._data, this); 412 | this.user.credentials = _temp.credentials; 413 | this.user.cookies = _temp.cookies; 414 | this.user.isLoggedIn = _temp.isLoggedIn; 415 | this.user.email = this._data.userrow.p_mail; 416 | } 417 | 418 | /** 419 | * 420 | * @param {string} id 421 | * @return {User | Teacher | Student | Parent | undefined} 422 | * @memberof Edupage 423 | */ 424 | getUserById(id) { 425 | return [this.user, ...this.teachers, ...this.students, ...this.parents].find(e => e.id == id); 426 | } 427 | 428 | /** 429 | * 430 | * @param {string} userString 431 | * @return {string} 432 | * @memberof Edupage 433 | */ 434 | getUserIdByUserString(userString) { 435 | return (userString.match(/-?\d+/) || "")[0]; 436 | } 437 | 438 | /** 439 | * 440 | * @param {string} userString 441 | * @return {User | Teacher | Student | Parent | undefined} 442 | * @memberof Edupage 443 | */ 444 | getUserByUserString(userString) { 445 | return this.getUserById(this.getUserIdByUserString(userString)); 446 | } 447 | 448 | /** 449 | * 450 | * @param {boolean} time 451 | * @return {string} 452 | * @memberof Edupage 453 | */ 454 | getYearStart(time = true) { 455 | return (this._data._edubar.year_turnover || `${this.year}-${this.ASC.schoolyearTurnover}`) + (time ? " 00:00:00" : ""); 456 | } 457 | 458 | /** 459 | * 460 | * @param {Date} date 461 | * @return {Promise} 462 | * @memberof Edupage 463 | */ 464 | async getTimetableForDate(date) { 465 | const timetable = this.timetables.find(e => Edupage.compareDay(e.date, date)); 466 | 467 | if(timetable) return timetable; 468 | return (await this.fetchTimetablesForDates(date, date))[0]; 469 | } 470 | 471 | /** 472 | * @param {Date} fromDate 473 | * @param {Date} toDate 474 | * @return {Promise} 475 | * @memberof Edupage 476 | */ 477 | async fetchTimetablesForDates(fromDate, toDate) { 478 | return new Promise((resolve, reject) => { 479 | const tryFetch = async _count => { 480 | //Get 'gpid' if it doesn't exist yet 481 | if(!this.ASC.gpid) { 482 | debug(`[Timetable] 'gpid' property does not exists, trying to fetch it...`); 483 | try { 484 | const _html = await this.api({url: ENDPOINT.DASHBOARD_GET_CLASSBOOK, method: "GET", type: "text"}); 485 | const ids = [..._html.matchAll(/gpid="?(\d+)"?/gi)].map(e => e[1]); 486 | 487 | if(ids.length) { 488 | this.ASC.gpids = ids; 489 | this.ASC.gpid = ids[ids.length - 1]; 490 | } 491 | else throw new Error("Cannot find gpid value"); 492 | } catch(err) { 493 | debug(`[Timetable] Could not get 'gpid' property`, err); 494 | return reject(new EdupageError("Could not get 'gpid' property: " + err.message)); 495 | } 496 | debug(`[Timetable] 'gpid' property fetched!`); 497 | } 498 | 499 | //Load and parse data 500 | this.api({ 501 | url: ENDPOINT.DASHBOARD_GCALL, 502 | method: "POST", 503 | type: "text", 504 | data: new URLSearchParams({ 505 | gpid: this.ASC.gpid, 506 | gsh: this.ASC.gsecHash, 507 | action: "loadData", 508 | datefrom: Edupage.dateToString(fromDate), 509 | dateto: Edupage.dateToString(toDate), 510 | }).toString(), 511 | encodeBody: false 512 | }, _count).then(_html => { 513 | const _json = Timetable.parse(_html); 514 | const timetables = iterate(_json.dates).map(([i, date, data]) => new Timetable(data, date, this)); 515 | 516 | //Update timetables 517 | timetables.forEach(e => { 518 | const i = this.timetables.findIndex(t => e.date.getTime() == t.date.getTime()); 519 | 520 | if(i > -1) this.timetables[i] = e; 521 | else this.timetables.push(e); 522 | }); 523 | 524 | resolve(timetables); 525 | }).catch(err => { 526 | if(err.retry) { 527 | debug(`[Timetable] Got retry signal, retrying...`); 528 | tryFetch(err.count + 1); 529 | } else { 530 | error(`[Timetable] Could not fetch timetables`, err); 531 | reject(new EdupageError("Failed to fetch timetables: " + err.message)); 532 | } 533 | }); 534 | }; 535 | tryFetch(-1); 536 | }); 537 | } 538 | 539 | /** 540 | * 541 | * @param {string} filepath 542 | * @returns {Promise} 543 | */ 544 | async uploadAttachment(filepath) { 545 | const CRLF = "\r\n"; 546 | const filename = (filepath.match(/(?:.+[\\\/])*(.+\..+)$/m) || "")[1] || "untitled.txt"; 547 | 548 | const buffer = Buffer.concat([ 549 | Buffer.from("--" + Attachment.formBoundary + CRLF + `Content-Disposition: form-data; name="att"; filename="${filename}"` + CRLF + CRLF, "utf8"), 550 | await fs.promises.readFile(filepath).catch(err => { 551 | throw new AttachmentError(`Error while reading input file: ` + err.message, err); 552 | }), 553 | Buffer.from(CRLF + "--" + Attachment.formBoundary + "--" + CRLF, "utf8") 554 | ]); 555 | 556 | const res = await this.api({ 557 | url: ENDPOINT.TIMELINE_UPLOAD_ATTACHMENT, 558 | headers: { 559 | "content-type": `multipart/form-data; boundary=` + Attachment.formBoundary 560 | }, 561 | data: buffer, 562 | encodeBody: false 563 | }); 564 | 565 | if(res.status !== API_STATUS.OK) throw new APIError(`Failed to upload file: Invalid status received '${res.status}'`, res); 566 | 567 | return new Attachment(res.data, this); 568 | } 569 | 570 | /** 571 | * @typedef {Object} APIOptions 572 | * @prop {string | ENDPOINT} url 573 | * @prop {Object | stream.Readable | Buffer | string} [data={}] 574 | * @prop {Object} [headers={}] 575 | * @prop {string} [method="POST"] 576 | * @prop {boolean} [encodeBody=true] 577 | * @prop {"json" | "text"} [type="json"] 578 | * @prop {boolean} [autoLogin=true] 579 | */ 580 | 581 | /** 582 | * 583 | * @static 584 | * @param {APIOptions} options 585 | * @param {number} [_count=0] 586 | * @return {Promise} Resolves: Response body, Rejects: Error or retry object in case of successful invalid gsecHash error resolution 587 | * @memberof Edupage 588 | */ 589 | async api(options, _count = 0) { 590 | const { 591 | headers = {}, 592 | data = {}, 593 | method = "POST", 594 | encodeBody = true, 595 | type = "json", 596 | autoLogin = true 597 | } = options; 598 | 599 | let url = options.url; 600 | 601 | return new Promise((resolve, reject) => { 602 | const tryFetch = (tryCount = _count || 0) => { 603 | debug(`[API] Trying to send request...`); 604 | 605 | const tryLogIn = async () => { 606 | debug(`[API] Logging in...`); 607 | await this.user.login(this.user.credentials.username, this.user.credentials.password) 608 | .then(() => { 609 | tryFetch(++tryCount - 1); 610 | }).catch(err => { 611 | error(`[API] Failed to log in user:`, err); 612 | reject(err); 613 | }); 614 | }; 615 | 616 | //If there are too many tries, reject the promise 617 | if(tryCount > 1) { 618 | error(`[API] Request terminated due to multiple failures`); 619 | return reject(new Error("Failed to send request multiple times")); 620 | } 621 | 622 | //User does not have origin assigned yet 623 | if(!this.user.origin && autoLogin) { 624 | debug(`[API] User is not logged in yet`); 625 | return tryLogIn(); 626 | } 627 | 628 | //If url is APIEndpoint, convert it to url 629 | if(typeof url === "number") { 630 | url = this.buildRequestUrl(url); 631 | } 632 | 633 | //Send request 634 | debug(`[API] Sending request to '${url}'...`); 635 | fetch(url, { 636 | "headers": { 637 | "accept": "application/json, text/javascript, */*; q=0.01", 638 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 639 | "Cookie": this.user.cookies.toString(false), 640 | "x-requested-with": "XMLHttpRequest", 641 | "referrer": `https://${this.user.origin}.edupage.org/`, 642 | ...headers 643 | }, 644 | "body": "POST" == method ? ( 645 | "string" == typeof data || data instanceof stream.Readable || data instanceof Buffer ? data : ( 646 | encodeBody ? this.encodeRequestBody(data) : JSON.stringify(data) 647 | ) 648 | ) : undefined, 649 | "method": method, 650 | }).then(res => res.text()).catch(err => { 651 | //Network error 652 | error(`[API] Error while sending request:`, err); 653 | tryFetch(++tryCount); 654 | }).then(text => { 655 | if(!text) { 656 | error(`[API] Empty response body`); 657 | return tryFetch(++tryCount); 658 | } 659 | 660 | //Needs to log in 661 | if(text.includes("edubarLogin.php") && autoLogin) { 662 | //Try to log in 663 | debug(`[API] Server responded with login page`); 664 | tryLogIn(); 665 | } else if(text.includes("Error6511024354099")) { 666 | //Invalid gsecHash 667 | error(`[API] Invalid gsecHash, refreshing edupage...`); 668 | this.refreshEdupage().then(() => { 669 | debug(`[API] Edupage refreshed, trying again (${tryCount + 1})...`); 670 | reject({retry: true, count: ++tryCount}); 671 | //tryFetch(++tryCount); 672 | }).catch(err => { 673 | error(`[API] Failed to refresh edupage:`, err); 674 | reject(new APIError("Failed to refresh edupage while resolving Invalid gsecHash error", err)); 675 | }); 676 | } else { 677 | if(type == "json") { 678 | try { 679 | //Parse response as json 680 | var json = JSON.parse(text); 681 | debug(`[API] Request successful`); 682 | resolve(json); 683 | } catch(err) { 684 | //Unknown error 685 | error(`[API] Failed to parse response as '${type}':`, err, text.slice(0, 200)); 686 | tryFetch(++tryCount); 687 | } 688 | } else if(type == "text") { 689 | //Already as text, do not have to parse 690 | debug(`[API] Request successful`); 691 | resolve(text); 692 | } else { 693 | //Unknown response type 694 | error(`[API] Invalid response type provided ('${type}')`); 695 | throw new TypeError(`Invalid response type '${type}'. (Available: 'json', 'text')`); 696 | } 697 | } 698 | }); 699 | }; 700 | tryFetch(); 701 | }); 702 | } 703 | 704 | /** 705 | * Sends a session ping request 706 | * @returns {Promise} Whether the ping was successful 707 | */ 708 | async pingSession() { 709 | debug(`[Login] Sening a session ping request...`); 710 | 711 | const gpids = this.ASC.gpids; 712 | const success = await this.api({ 713 | url: ENDPOINT.SESSION_PING, 714 | method: "POST", 715 | type: "text", 716 | data: { 717 | gpids: gpids.join(";") 718 | } 719 | }).then(data => { 720 | if(data == "notlogged") return false; 721 | else if(data == "OK") return true; 722 | 723 | try { 724 | const obj = JSON.parse(data); 725 | if(obj.status == "notlogged") return false; 726 | else return true; 727 | } catch(err) { 728 | FatalError.throw(new ParseError(`Failed to parse session ping response as JSON: ${err.message}`), {data, gpids}); 729 | } 730 | }).catch(err => { 731 | FatalError.warn(new APIError(`Failed to ping session: ${err.message}`), {err, gpids}); 732 | return null; 733 | }); 734 | 735 | this.scheduleSessionPing(); 736 | 737 | //Failed to ping session 738 | if(success === null) { 739 | error(`[Login] Failed to ping session`); 740 | return false; 741 | } 742 | 743 | //Successfully pinged session 744 | if(success) { 745 | debug(`[Login] Successfully pinged session`); 746 | return true; 747 | } 748 | 749 | //Session is not logged in 750 | if(!success) { 751 | warn(`[Login] Session is not logged in, trying to log in...`); 752 | const loggedIn = await this.user.login(this.user.credentials.username, this.user.credentials.password) 753 | .then(() => { 754 | debug(`[Login] Successfully logged in`); 755 | return true; 756 | }) 757 | .catch(err => { 758 | error(`[Login] Failed to log in:`, err); 759 | return false; 760 | }); 761 | 762 | return loggedIn; 763 | } 764 | } 765 | 766 | /** 767 | * Schedules a session ping request 768 | */ 769 | scheduleSessionPing() { 770 | if(this._sessionPingTimeout) { 771 | clearTimeout(this._sessionPingTimeout); 772 | this._sessionPingTimeout = null; 773 | } 774 | 775 | this._sessionPingTimeout = setTimeout(() => this.pingSession(), SESSION_PING_INTERVAL_MS); 776 | debug(`[Login] Scheduled session ping in ${SESSION_PING_INTERVAL_MS}ms`); 777 | } 778 | 779 | /** 780 | * Stops internal timers to prevent process from hanging infinitely. 781 | */ 782 | exit() { 783 | if(this._sessionPingTimeout) { 784 | clearTimeout(this._sessionPingTimeout); 785 | this._sessionPingTimeout = null; 786 | } 787 | } 788 | 789 | /** 790 | * 791 | * @param {Date | number | string} date1 792 | * @param {Date | number | string} date2 793 | * @return {boolean} true if day of the dates is same, otherwise false 794 | * @memberof Edupage 795 | */ 796 | static compareDay(date1, date2) { 797 | //Convert primitives to Date object 798 | if(typeof date1 === "number" || typeof date1 == "string") date1 = new Date(date1); 799 | if(typeof date2 === "number" || typeof date2 == "string") date2 = new Date(date2); 800 | 801 | return date1.getDate() == date2.getDate() && 802 | date1.getMonth() == date2.getMonth() && 803 | date1.getFullYear() == date2.getFullYear(); 804 | } 805 | 806 | /** 807 | * 808 | * @param {Date} date 809 | * @return {string} string representation of the date 810 | * @memberof Edupage 811 | */ 812 | static dateToString(date) { 813 | return date.toISOString().slice(0, 10); 814 | } 815 | 816 | /** 817 | * Converts Object to form body 818 | * @private 819 | * @param {Object} data 820 | * @return {string} Form body 821 | */ 822 | encodeRequestBody(data) { 823 | const query = new URLSearchParams(data).toString(); 824 | return `eqap=${encodeURIComponent(btoa(query))}&eqaz=0`; 825 | } 826 | 827 | /** 828 | * Returns endpoint URL 829 | * @private 830 | * @param {APIEndpoint} endpoint 831 | * @return {string} Endpoint URL 832 | */ 833 | buildRequestUrl(endpoint) { 834 | if(!this.user.origin) throw new LoginError(`Failed to build URL: User is not logged in yet`); 835 | 836 | let url = null; 837 | 838 | if(endpoint == ENDPOINT.DASHBOARD_GET_USER) url = `/user/?`; 839 | if(endpoint == ENDPOINT.DASHBOARD_GET_CLASSBOOK) url = `/dashboard/eb.php?barNoSkin=1`; 840 | if(endpoint == ENDPOINT.DASHBOARD_GCALL) url = `/gcall`; 841 | if(endpoint == ENDPOINT.DASHBOARD_SIGN_ONLINE_LESSON) url = `/dashboard/server/onlinelesson.js?__func=getOnlineLessonOpenUrl`; 842 | if(endpoint == ENDPOINT.TIMELINE_GET_DATA) url = `/timeline/?akcia=getData`; 843 | if(endpoint == ENDPOINT.TIMELINE_GET_REPLIES) url = `/timeline/?akcia=getRepliesItem`; 844 | if(endpoint == ENDPOINT.TIMELINE_GET_CREATED_ITEMS) url = `/timeline/?cmd=created&akcia=getData`; 845 | if(endpoint == ENDPOINT.TIMELINE_CREATE_ITEM) url = `/timeline/?akcia=createItem`; 846 | if(endpoint == ENDPOINT.TIMELINE_CREATE_CONFIRMATION) url = `/timeline/?akcia=createConfirmation`; 847 | if(endpoint == ENDPOINT.TIMELINE_CREATE_REPLY) url = `/timeline/?akcia=createReply`; 848 | if(endpoint == ENDPOINT.TIMELINE_FLAG_HOMEWORK) url = `/timeline/?akcia=homeworkFlag`; 849 | if(endpoint == ENDPOINT.TIMELINE_UPLOAD_ATTACHMENT) url = `/timeline/?akcia=uploadAtt`; 850 | if(endpoint == ENDPOINT.ELEARNING_TEST_DATA) url = `/elearning/?cmd=MaterialPlayer&akcia=getETestData&ts=${new Date().getTime()}`; 851 | if(endpoint == ENDPOINT.ELEARNING_TEST_RESULTS) url = `/elearning/?cmd=EtestCreator&akcia=getResultsData`; 852 | if(endpoint == ENDPOINT.ELEARNING_CARDS_DATA) url = `/elearning/?cmd=EtestCreator&akcia=getCardsData`; 853 | if(endpoint == ENDPOINT.GRADES_DATA) url = `/znamky/?barNoSkin=1`; 854 | if(endpoint == ENDPOINT.SESSION_PING) url = this._data?._edubar?.sessionPingUrl || `/login/eauth?portalping`; 855 | 856 | if(!url) throw new TypeError(`Invalid API endpoint '${endpoint}'`); 857 | else return this.baseUrl + url; 858 | } 859 | 860 | /** 861 | * Parses raw JSON data from html 862 | * @private 863 | * @param {string} html 864 | * @returns {RawDataObject} 865 | */ 866 | static parse(html) { 867 | let data = { 868 | _edubar: {} 869 | }; 870 | 871 | const match = (html.match(/\.userhome\((.+?)\);$/m) || "")[1]; 872 | if(!match) return FatalError.throw(new ParseError("Failed to parse Edupage data from html"), {html}); 873 | 874 | try { 875 | data = {...JSON.parse(match)}; 876 | } catch(e) { 877 | return FatalError.throw(new ParseError("Failed to parse JSON from Edupage html"), {html, match, e}); 878 | } 879 | 880 | //Parse additional edubar data 881 | const match2 = (html.match(/edubar\(([\s\S]*?)\);/) || "")[1]; 882 | if(!match2) return FatalError.throw(new ParseError("Failed to parse edubar data from html"), {html}); 883 | 884 | try { 885 | data._edubar = JSON.parse(match2) || {}; 886 | } catch(e) { 887 | return FatalError.throw(new ParseError("Failed to parse JSON from edubar html"), {html, match2, e}); 888 | } 889 | 890 | return data; 891 | } 892 | } 893 | 894 | module.exports = Edupage; -------------------------------------------------------------------------------- /src/Grade.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const Class = require("./Class"); 5 | const Edupage = require("./Edupage"); 6 | const Assignment = require("./Assignment"); 7 | const Plan = require("./Plan"); 8 | const Season = require("./Season"); 9 | const Student = require("./Student"); 10 | const Subject = require("./Subject"); 11 | const Teacher = require("./Teacher"); 12 | const {FatalError, ParseError, EdupageError} = require("./exceptions"); 13 | 14 | debug.log = console.log.bind(console); 15 | 16 | /** 17 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 18 | */ 19 | 20 | class Grade extends RawData { 21 | /** 22 | * Creates an instance of Grade. 23 | * @param {RawDataObject} [data={}] 24 | * @param {Edupage} [edupage=null] 25 | * @memberof Grade 26 | */ 27 | constructor(data = {}, edupage = null) { 28 | super(data); 29 | 30 | /** 31 | * @type {Edupage} 32 | */ 33 | this.edupage = edupage; 34 | 35 | /** 36 | * @type {string} 37 | */ 38 | this.value = data.data; 39 | 40 | /** 41 | * @type {Date} 42 | */ 43 | this.creationDate = data.datum ? new Date(data.datum) : null; 44 | 45 | /** 46 | * @type {Season} 47 | */ 48 | this.season = null; 49 | 50 | /** 51 | * @type {Date} 52 | */ 53 | this.signedDate = data.podpisane ? new Date(data.podpisane) : null; 54 | 55 | /** 56 | * @type {Date} 57 | */ 58 | this.signedByParentDate = data.podpisane_rodic ? new Date(data.podpisane_rodic) : null; 59 | 60 | /** 61 | * @type {boolean} 62 | */ 63 | this.isSigned = !!(this.signedDate || this.signedByParentDate); 64 | 65 | /** 66 | * @type {Subject} 67 | */ 68 | this.subject = null; 69 | 70 | /** 71 | * @type {string} 72 | */ 73 | this.state = data.stav; 74 | 75 | /** 76 | * @type {Student} 77 | */ 78 | this.student = null; 79 | 80 | /** 81 | * @type {Teacher} 82 | */ 83 | this.teacher = null; 84 | 85 | /** 86 | * @type {string} 87 | */ 88 | this.id = data.znamkaid; 89 | 90 | /** 91 | * @type {string} 92 | */ 93 | this.eventId = data.udalostid; 94 | 95 | /** 96 | * @type {Class} 97 | */ 98 | this.class = null; 99 | 100 | /** 101 | * @type {Class[]} 102 | */ 103 | this.classes = null; 104 | 105 | /** 106 | * @type {string} 107 | */ 108 | this.title = ""; 109 | 110 | /** 111 | * @type {string} 112 | */ 113 | this.short = ""; 114 | 115 | /** 116 | * @type {string} 117 | * @example "31.3.2021" 118 | */ 119 | this.date = null; 120 | 121 | /** 122 | * @type {string} 123 | */ 124 | this.type = null; 125 | 126 | /** 127 | * @type {number} 128 | */ 129 | this.weight = 1; 130 | 131 | /** 132 | * @type {number} 133 | */ 134 | this.maxPoints = null; 135 | 136 | /** 137 | * @type {number} 138 | */ 139 | this.points = null; 140 | 141 | /** 142 | * @type {number} 143 | */ 144 | this.percentage = null; 145 | 146 | /** 147 | * @type {Plan} 148 | */ 149 | this.plan = null; 150 | 151 | /** 152 | * @type {string} 153 | */ 154 | 155 | this.average = null; 156 | 157 | /** 158 | * @type {string} 159 | */ 160 | this.provider = data.provider; 161 | 162 | /** 163 | * @type {string} 164 | */ 165 | this.superId = null; 166 | 167 | /** 168 | * @type {boolean} 169 | */ 170 | this.isClassified = false; 171 | 172 | /** 173 | * @type {Assignment} 174 | */ 175 | this.assignment = null; 176 | 177 | if(this.edupage) Grade.prototype.init.call(this); 178 | } 179 | 180 | /** 181 | * 182 | * @param {Edupage} [edupage=null] 183 | * @returns {any} 184 | * @memberof Grade 185 | */ 186 | init(edupage = null) { 187 | if(edupage) this.edupage = edupage; 188 | 189 | //Assign general properties 190 | this.season = this.edupage.seasons.find(e => e.id == this._data.mesiac); 191 | this.subject = this.edupage.subjects.find(e => e.id == this._data.predmetid); 192 | this.student = this.edupage.students.find(e => e.id == this._data.studentid); 193 | this.teacher = this.edupage.teachers.find(e => e.id == this._data.ucitelid); 194 | 195 | if(!this.provider) return FatalError.warn(new EdupageError("Failed to init the Grade object: Invalid provider"), {_data: this._data}); 196 | 197 | //Find event 198 | this._data._event = (this.edupage._data._grades._events[this.provider] || []).find(e => e.UdalostID == this.eventId); 199 | if(!this._data._event) return; //Edupage don't handle such case 200 | 201 | //Assign event properties 202 | this.plan = this.edupage.plans.find(e => e.id == this._data._event.planid); 203 | this.class = this.edupage.classes.find(e => e.id == this._data._event.TriedaID); 204 | this.classes = this._data._event.Triedy.map(id => this.edupage.classes.find(e => e.id == id)); 205 | this.title = this._data._event.p_meno; 206 | this.short = this._data._event.p_skratka; 207 | this.date = this._data._event.p_termin; 208 | this.type = this._data._event.p_typ_udalosti; 209 | this.weight = this._data._event.p_vaha ? +this._data._event.p_vaha / 20 : 1; 210 | this.average = this._data._event.priemer; 211 | 212 | //Find material assigned to Grade 213 | if(this._data._event.moredata?.elearning_superid) { 214 | this.superId = this._data._event.moredata.elearning_superid.toString(); 215 | this.isClassified = true; 216 | } 217 | 218 | //Simple points calculations 219 | if(this.type == "3") { 220 | const max = parseFloat(this._data._event.p_vaha_body); 221 | const pts = parseFloat(this.value); 222 | 223 | //This is going to be invalid for fallback values but whatever ¯\_(ツ)_/¯ 224 | this.maxPoints = isNaN(max) ? 100 : max; 225 | this.points = isNaN(pts) ? 100 : pts; 226 | this.percentage = this.points / this.maxPoints * 100; 227 | } 228 | } 229 | 230 | /** 231 | * 232 | * @static 233 | * @param {string} html 234 | * @return {{settings: RawDataObject, data: RawDataObject}} 235 | * @memberof Grade 236 | */ 237 | static parse(html) { 238 | const _settings = (html.match(/initZnamkovanieSettings\(([\s\S]*?)\);/) || "")[1]; 239 | const _data = (html.match(/znamkyStudentViewer\(([\s\S]*?)\);/) || "")[1]; 240 | 241 | let settings = {}; 242 | let data = {}; 243 | 244 | if(!_settings) return FatalError.throw(new ParseError("Failed to parse grade settings from html"), {html}); 245 | if(!_data) return FatalError.throw(new ParseError("Failed to parse grade data from html"), {html}); 246 | 247 | try { 248 | settings = JSON.parse(_settings); 249 | } catch(e) { 250 | return FatalError.throw(new ParseError("Failed to parse grade settings as JSON"), {html, _settings}); 251 | } 252 | 253 | try { 254 | data = JSON.parse(_data); 255 | } catch(e) { 256 | return FatalError.throw(new ParseError("Failed to parse grade data as JSON"), {html, _data}); 257 | } 258 | 259 | return {settings, data}; 260 | } 261 | } 262 | 263 | module.exports = Grade; -------------------------------------------------------------------------------- /src/Homework.js: -------------------------------------------------------------------------------- 1 | const Assignment = require("./Assignment"); 2 | const Edupage = require("./Edupage"); 3 | 4 | /** 5 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 6 | */ 7 | 8 | class Homework extends Assignment { 9 | /** 10 | * Creates an instance of Homework. 11 | * @param {RawDataObject} [data={}] 12 | * @param {Edupage} [edupage=null] 13 | * @memberof Homework 14 | */ 15 | constructor(data = {}, edupage = null) { 16 | super(data, edupage); 17 | } 18 | } 19 | 20 | module.exports = Homework; -------------------------------------------------------------------------------- /src/Lesson.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const {FatalError, EdupageError} = require("./exceptions"); 4 | const RawData = require("../lib/RawData"); 5 | const Assignment = require("./Assignment"); 6 | const Class = require("./Class"); 7 | const Classroom = require("./Classroom"); 8 | const Edupage = require("./Edupage"); 9 | const {ENDPOINT} = require("./enums"); 10 | const Period = require("./Period"); 11 | const Student = require("./Student"); 12 | const Subject = require("./Subject"); 13 | const Teacher = require("./Teacher"); 14 | 15 | /** 16 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 17 | */ 18 | 19 | class Lesson extends RawData { 20 | /** 21 | * Creates an instance of Lesson. 22 | * @param {RawDataObject} [data={}] 23 | * @param {Edupage} [edupage=null] 24 | * @memberof Lesson 25 | */ 26 | constructor(data = {}, edupage = null) { 27 | super(data); 28 | 29 | /** 30 | * Edupage instance associated to this object 31 | * @type {Edupage} 32 | */ 33 | this.edupage = edupage; 34 | 35 | /** 36 | * @type {string} 37 | */ 38 | this.id = data.flags.dp0.id; 39 | 40 | /** 41 | * @type {string} 42 | */ 43 | this.lid = data.flags.dp0.lid; 44 | 45 | /** 46 | * @type {Date} 47 | */ 48 | this.date = new Date(data.flags.dp0.date); 49 | 50 | /** 51 | * @type {string} 52 | */ 53 | this.homeworkNote = data.flags.dp0.note_homework; 54 | 55 | /** 56 | * @type {string} 57 | */ 58 | this.absentNote = data.flags.dp0.note_student_absent; 59 | 60 | /** 61 | * @type {string} 62 | */ 63 | this.curriculum = data.flags.dp0.note_wd || null; 64 | 65 | /** 66 | * @type {string} 67 | */ 68 | this.onlineLessonURL = data.flags.dp0.ol_url || null; 69 | 70 | /** 71 | * @type {boolean} 72 | */ 73 | this.isOnlineLesson = !!this.onlineLessonURL; 74 | 75 | 76 | /** 77 | * @type {Period} 78 | */ 79 | this.period = null; 80 | 81 | /** 82 | * @type {Subject} 83 | */ 84 | this.subject = null; 85 | 86 | /** 87 | * @type {Class[]} 88 | */ 89 | this.classes = []; 90 | 91 | /** 92 | * @type {Classroom[]} 93 | */ 94 | this.classrooms = []; 95 | 96 | /** 97 | * @type {Student[]} 98 | */ 99 | this.students = []; 100 | 101 | /** 102 | * @type {Teacher[]} 103 | */ 104 | this.teachers = []; 105 | 106 | /** 107 | * @type {Assignment[]} 108 | */ 109 | this.assignments = []; 110 | 111 | if(this.edupage) Lesson.prototype.init.call(this); 112 | } 113 | 114 | /** 115 | * 116 | * @param {Edupage} [edupage=null] 117 | * @memberof Lesson 118 | * @returns {void} 119 | */ 120 | init(edupage = null) { 121 | if(edupage) this.edupage = edupage; 122 | 123 | this.period = this.edupage.periods.find(e => e.id == this._data.flags.dp0.period); 124 | this.subject = this.edupage.subjects.find(e => e.id == this._data.flags.dp0.subjectid); 125 | this.classes = this._data.flags.dp0.classids.map(id => this.edupage.classes.find(e => e.id == id)); 126 | this.classrooms = this._data.flags.dp0.classroomids.map(id => this.edupage.classrooms.find(e => e.id == id)); 127 | this.students = this._data.flags.dp0.studentids.map(id => this.edupage.students.find(e => e.id == id)); 128 | this.teachers = this._data.flags.dp0.teacherids.map(id => this.edupage.teachers.find(e => e.id == id)); 129 | this.assignments = this._data.flags.dp0.homeworkids.map(id => 130 | this.edupage.assignments.find(e => e.hwkid && e.hwkid.includes(id.split(":")[1] || id)) 131 | ); 132 | 133 | //In case the period is not specified 134 | if(!this.period) { 135 | const periodId = this._data.flags.dp0.period; 136 | 137 | //There is a period id, but it's not in the periods list 138 | if(periodId) return FatalError.warn(new ReferenceError("Failed to find period for lesson"), { 139 | query: periodId, 140 | periods: this.edupage.periods, 141 | _data_lesson: this._data, 142 | _data_edupage: this.edupage._data.dbi?.periods 143 | }); 144 | 145 | //There is no period id specified, so use time at least 146 | this.period = Period.getInvalid({ 147 | startTime: this._data.flags.dp0.starttime, 148 | endTime: this._data.flags.dp0.endtime, 149 | }); 150 | } 151 | 152 | //Set the lesson start time 153 | if(this.period.startTime) { 154 | const d = this.period.startTime.split(":"); 155 | this.date.setHours(+d[0], +d[1]); 156 | } 157 | } 158 | 159 | /** 160 | * 161 | * @return {Promise} 162 | * @memberof Lesson 163 | */ 164 | async signIntoLesson() { 165 | return new Promise((resolve, reject) => { 166 | if(!this.isOnlineLesson) return reject(new Error(`Cannot sign into this lesson`)); 167 | const tryFetch = async _count => { 168 | 169 | const payload = { 170 | "__args": [ 171 | null, 172 | { 173 | "click": true, 174 | "date": this.date.toISOString().slice(0, 10), 175 | "ol_url": this.onlineLessonURL, 176 | "subjectid": this.subject.id 177 | } 178 | ], 179 | "__gsh": this.edupage.ASC.gsecHash 180 | }; 181 | 182 | this.edupage.api({ 183 | url: ENDPOINT.DASHBOARD_SIGN_ONLINE_LESSON, 184 | data: payload, 185 | encodeBody: false 186 | }).then(res => { 187 | resolve(res.reload != true); 188 | }).catch(err => { 189 | if(err.retry) { 190 | debug(`[Timetable] Got retry signal, retrying...`); 191 | tryFetch(err.count + 1); 192 | } else { 193 | error(`[Timetable] Could not fetch timetables`, err); 194 | reject(new EdupageError("Failed to fetch timetables: " + err.message)); 195 | } 196 | }); 197 | }; 198 | tryFetch(-1); 199 | }); 200 | } 201 | } 202 | 203 | module.exports = Lesson; -------------------------------------------------------------------------------- /src/Message.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const {iterate} = require("../lib/utils"); 5 | const Attachment = require("./Attachment"); 6 | const Class = require("./Class"); 7 | const Edupage = require("./Edupage"); 8 | const {ENDPOINT, ENTITY_TYPE, API_STATUS, TIMELINE_ITEM_TYPE} = require("./enums"); 9 | const {APIError, EdupageError, MessageError} = require("./exceptions"); 10 | const Parent = require("./Parent"); 11 | const Plan = require("./Plan"); 12 | const Student = require("./Student"); 13 | const Teacher = require("./Teacher"); 14 | const User = require("./User"); 15 | const Assignment = require("./Assignment"); 16 | 17 | debug.log = console.log.bind(console); 18 | 19 | /** 20 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 21 | */ 22 | 23 | class Message extends RawData { 24 | /** 25 | * Creates an instance of Message. 26 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 27 | * @param {Edupage} [edupage=null] Edupage instance to use. 28 | * @memberof Message 29 | */ 30 | constructor(data = {}, edupage = null) { 31 | super(data); 32 | 33 | if(!data.data) data.data = {}; 34 | if(typeof data.data === "string") data.data = JSON.parse(data.data) || {}; 35 | 36 | /** 37 | * Edupage instance associated to this object. 38 | * @type {Edupage} 39 | */ 40 | this.edupage = edupage; 41 | 42 | 43 | /** 44 | * Timeline item id 45 | * @type {string} 46 | */ 47 | this.id = data.timelineid; 48 | 49 | /** 50 | * Timeline item type 51 | * @type {string} 52 | */ 53 | this.type = data.typ; 54 | 55 | /** 56 | * Date when the message was created 57 | * @type {Date} 58 | */ 59 | this.creationDate = data.cas_pridania ? new Date(data.cas_pridania) : null; 60 | 61 | /** 62 | * Date when the message was last updated 63 | * @type {Date} 64 | */ 65 | this.timelineDate = data.timestamp ? new Date(data.timestamp) : null; 66 | 67 | /** 68 | * ID of the user who created the message 69 | * @type {string} 70 | */ 71 | this.otherId = data.otherId || null; 72 | 73 | /** 74 | * Number of replies to the message 75 | * @type {number} 76 | */ 77 | this.repliesCount = +data.pocet_reakcii || 0; 78 | 79 | /** 80 | * Date of the last reply to the message 81 | * @type {Date} 82 | */ 83 | this.lastReplyDate = data.posledna_reakcia ? new Date(data.posledna_reakcia) : null; 84 | 85 | /** 86 | * Flag indicating whether the message was removed 87 | * @type {boolean} 88 | */ 89 | this.isRemoved = !!+data.removed; 90 | 91 | /** 92 | * Flag indicating whether the message is reply 93 | * @type {boolean} 94 | */ 95 | this.isReply = !!this._data.data.textReply; 96 | 97 | /** 98 | * Flag indicating whether the message is sent as important 99 | * @type {boolean} 100 | */ 101 | this.isImportant = !!+data.data.receipt || !!+data.data.importantReply; 102 | 103 | /** 104 | * Date when the currently logged in user read the message. If the message is not important, this is same as `Message.creationDate`. 105 | * @type {Date} 106 | */ 107 | this.seenDate = this.isImportant ? (data.data.myConfirmations?.receipt ? new Date(data.data.myConfirmations?.receipt) : null) : this.creationDate; 108 | 109 | /** 110 | * Flag indicating whether the message was seen. If the message is not important, this will be always `true`. 111 | * @type {boolean} 112 | */ 113 | this.isSeen = !!this.seenDate; 114 | 115 | /** 116 | * Date when the currently logged in user liked the message. 117 | * @type {Date} 118 | */ 119 | this.likedDate = data.data.myConfirmations?.like ? new Date(data.data.myConfirmations?.like) : null; 120 | 121 | /** 122 | * Flag indicating whether the message was liked. 123 | * @type {boolean} 124 | */ 125 | this.isLiked = !!this.likedDate; 126 | 127 | /** 128 | * Date when the currently logged in user marked this message as done. 129 | * @type {Date} 130 | */ 131 | this.doneDate = null; 132 | 133 | /** 134 | * Flag indicating whether the message was marked as done. 135 | * @type {boolean} 136 | */ 137 | this.isDone = false; 138 | 139 | /** 140 | * Flag indicating whether the message was starred. 141 | * @type {boolean} 142 | */ 143 | this.isStarred = false; 144 | 145 | /** 146 | * Number of likes the message has. 147 | * @type {number} 148 | */ 149 | this.likes = data.data.confirmations?.like || 0; 150 | 151 | /** 152 | * The textual content of the message. 153 | * @type {string} 154 | */ 155 | this.text = data.data.messageContent || data.text; 156 | 157 | /** 158 | * Title of the message. 159 | * @type {string} 160 | */ 161 | this.title = data.user_meno; 162 | 163 | 164 | /** 165 | * Number of participants in the message. 166 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 167 | * @type {number} 168 | */ 169 | this.participantsCount = null; 170 | 171 | /** 172 | * List of participants in the message. 173 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 174 | * @type {(User | Teacher | Student | Parent)[]} 175 | */ 176 | this.participants = []; 177 | 178 | /** 179 | * List of users who liked the message. 180 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 181 | * @type {({user: User | Teacher | Student | Parent, date: Date})[]} 182 | */ 183 | this.likedBy = []; 184 | 185 | /** 186 | * List of users who have seen the message. 187 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 188 | * @type {({user: User | Teacher | Student | Parent, date: Date})[]} 189 | */ 190 | this.seenBy = []; 191 | 192 | 193 | /** 194 | * Author of the message. 195 | * @type {User | Teacher | Student | Parent} 196 | */ 197 | this.owner = null; 198 | 199 | /** 200 | * Recipient of the message. 201 | * @type {User | Teacher | Student | Parent | Plan | Class} 202 | */ 203 | this.recipient = null; 204 | 205 | /** 206 | * Recipient of the message as user string. This can be used, when the exact recipient is not known (e.g. when the recipient is everyone, this will be '*'). 207 | * @type {string} 208 | */ 209 | this.recipientUserString = null; 210 | 211 | /** 212 | * Flag indicating whether the message has no exact recipient (e.g. userstring contains '*'). 213 | * @type {boolean} 214 | */ 215 | this.isWildcardRecipient = false; 216 | 217 | /** 218 | * Root message of the reply. 219 | * @type {Message} 220 | */ 221 | this.replyOf = null; 222 | 223 | 224 | /** 225 | * Resources attached to the message. 226 | * @type {Attachment[]} 227 | */ 228 | this.attachments = []; 229 | 230 | /** 231 | * List of replies to the message. 232 | * @type {Message[]} 233 | */ 234 | this.replies = []; 235 | 236 | /** 237 | * Assignment attached to the message. 238 | * @type {Assignment} 239 | */ 240 | this.assignment = null; 241 | 242 | if(this.edupage) Message.prototype.init.call(this); 243 | } 244 | 245 | /** 246 | * Initializes instance. 247 | * @param {Edupage} [edupage=null] Edupage instance to use. 248 | * @memberof Message 249 | */ 250 | init(edupage = null) { 251 | if(edupage) this.edupage = edupage; 252 | 253 | //Update attributes 254 | if("item" in this._data) { 255 | const data = this._data.item; 256 | 257 | this.timelineDate = data.timestamp ? new Date(data.timestamp) : null; 258 | this.repliesCount = +data.pocet_reakcii || 0; 259 | this.lastReplyDate = data.posledna_reakcia ? new Date(data.posledna_reakcia) : null; 260 | this.isRemoved = !!+data.removed; 261 | this.seenDate = this.isImportant ? (this._data.reakcie[0]?.data?.myConfirmations?.receipt ? new Date(this._data.reakcie[0]?.data?.myConfirmations?.receipt) : null) : this.creationDate; 262 | this.isSeen = !!this.seenDate; 263 | this.likedDate = this._data.reakcie[0]?.data?.myConfirmations?.like ? new Date(this._data.reakcie[0]?.data?.myConfirmations?.like) : null; 264 | this.isLiked = !!this.likedDate; 265 | this.likes = data.data.confirmations?.like || 0; 266 | } 267 | 268 | //Add more data 269 | if("numParticipants" in this._data) this.participantsCount = this._data.numParticipants; 270 | if("participants" in this._data) this.participants = iterate(this._data.participants).map(([i, user, data]) => this.edupage.getUserByUserString(user) || User.from(user, data, this.edupage)); 271 | if("reakcie" in this._data) { 272 | this._data.reakcie 273 | .forEach(e => { 274 | if(e.pomocny_zaznam) return; 275 | if(e.timelineid == this._data.item?.timelineid) return; 276 | if(e.cas_pridania == this._data.cas_pridania) return; 277 | if(e.typ == TIMELINE_ITEM_TYPE.CONFIRMATION) { 278 | const data = Message.parseUsername(e.vlastnik_meno); 279 | const user = this.edupage.getUserByUserString(e.vlastnik) || User.from(e.vlastnik, data, this.edupage); 280 | 281 | if(e.data.like) this.likedBy.push({user, date: new Date(e.data.like)}); 282 | if(this.isImportant) this.seenBy.push({user, date: new Date(e.cas_pridania)}); 283 | } else { 284 | if(this.replies.some(t => t.id == e.timelineid)) return; 285 | 286 | //After init it should get pushed automatically 287 | const msg = new Message(e, this.edupage); 288 | 289 | //If it wasn't pushed into replies, push it manually 290 | if(!this.replies.includes(msg)) this.replies.push(msg); 291 | } 292 | }); 293 | 294 | this.repliesCount = this.replies.length; 295 | } 296 | 297 | //Setup flags 298 | const props = this._data.userProps || this.edupage._data.timelineUserProps[this.id] || {}; 299 | this.doneDate = props.doneMaxCas ? new Date(props.doneMaxCas) : null; 300 | this.isDone = !!this.doneDate; 301 | this.isStarred = !!+props.starred; 302 | 303 | //Add owner object 304 | if(this._data.vlastnik) { 305 | //Parse name of the owner 306 | const data = Message.parseUsername(this._data.vlastnik_meno); 307 | 308 | //Assign owner object 309 | this.owner = this.edupage.getUserByUserString(this._data.vlastnik) || 310 | User.from(this._data.vlastnik, data, this.edupage); 311 | } 312 | 313 | //Add recipient object 314 | if(this._data.user) { 315 | const {recipient, wildcard} = this.getRecipient(this._data.user); 316 | 317 | this.recipient = recipient; 318 | this.isWildcardRecipient = wildcard; 319 | this.recipientUserString = this._data.user; 320 | } 321 | 322 | //Add reply 323 | if(this._data.reakcia_na && this._data.typ != TIMELINE_ITEM_TYPE.CONFIRMATION) { 324 | //Find root message 325 | const message = this.edupage.timelineItems.find(e => e.id == this._data.reakcia_na); 326 | 327 | if(message) { 328 | message.replies.push(this); 329 | this.replyOf = message; 330 | } else { 331 | //Cannot find root message 332 | } 333 | } 334 | 335 | //Add attachments 336 | if(this._data.data.attachements) { 337 | const attchs = this._data.data.attachements; 338 | this.attachments = Object.keys(attchs).map(e => new Attachment({src: e, name: attchs[e]}, this.edupage)); 339 | } 340 | 341 | //Add homework 342 | if(this._data.data.superid) { 343 | this.assignment = this.edupage.assignments.find(e => e.superId == this._data.data.superid); 344 | } 345 | 346 | //TODO: implement this._data.participantGroups = { 347 | //TODO: hasParticipants: 1 348 | //TODO: id: "CustPlan3610" 349 | //TODO: name: "Telesná a športová výchova · I.BP - I" 350 | //TODO: spec: "Rodičia a žiaci" 351 | //TODO: type: "group" 352 | //TODO: }[] 353 | 354 | } 355 | 356 | /** 357 | * @typedef {Object} MessageReplyOptions 358 | * @prop {string} text 359 | * @prop {User | Teacher | Student | Parent} [recipient=null] 360 | * @prop {boolean} [parents=false] 361 | * @prop {Attachment[]} [attachments=[]] 362 | */ 363 | 364 | /** 365 | * Creates a new reply to the message 366 | * @param {MessageReplyOptions} options 367 | * @memberof Message 368 | */ 369 | async reply(options) { 370 | const { 371 | text, 372 | recipient = null, 373 | parents = false, 374 | attachments = [] 375 | } = options; 376 | 377 | const res = await this.edupage.api({ 378 | url: ENDPOINT.TIMELINE_CREATE_REPLY, 379 | data: { 380 | "groupid": this.id, 381 | "recipient": this.isReply 382 | ? this.owner.getUserString(parents) 383 | : (recipient ? recipient.getUserString(parents) : ""), 384 | "text": text, 385 | "moredata": JSON.stringify({attachements: attachments}) 386 | } 387 | }); 388 | 389 | //Request failed 390 | if(res.status !== API_STATUS.OK) { 391 | error(`[Reply] Received invalid status from the server '${res.status}'`); 392 | throw new APIError(`Failed to send message: Invalid status received '${res.status}'`, res); 393 | } 394 | 395 | //Add reply to root message 396 | const _len = this.replies.length; 397 | this._data.reakcie = res.data.reakcie; 398 | Message.prototype.init.call(this); 399 | 400 | //Check for successful creation of the reply 401 | if(this.replies.length > _len) { 402 | if(this.replies.length > _len + 1) debug(`[Reply] Message contains multiple new replies (${_len} -> ${this.replies.length})`); 403 | return this.replies[this.replies.length - 1]; 404 | } else { 405 | error(`[Reply] Message had ${_len} replies, and now has ${this.replies.length}`, res, this.replies); 406 | throw new MessageError(`Failed to get sent message: No new replies were created`); 407 | } 408 | } 409 | 410 | /** 411 | * Marks the message as liked 412 | * @param {boolean} [state=true] State to use (like/unlike) 413 | * @return {Promise} 414 | * @memberof Message 415 | */ 416 | async markAsLiked(state = true) { 417 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 418 | 419 | //Load data 420 | const res = await this.edupage.api({ 421 | url: ENDPOINT.TIMELINE_CREATE_CONFIRMATION, 422 | data: { 423 | "groupid": this.id, 424 | "confirmType": "like", 425 | "val": (+state).toString() 426 | } 427 | }); 428 | 429 | await this.refresh(res); 430 | 431 | return state; 432 | } 433 | 434 | /** 435 | * Marks the message as seen 436 | * @return {Promise} 437 | * @memberof Message 438 | */ 439 | async markAsSeen() { 440 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 441 | 442 | //Load data 443 | const res = await this.edupage.api({ 444 | url: ENDPOINT.TIMELINE_CREATE_CONFIRMATION, 445 | data: { 446 | "groupid": this.id, 447 | "confirmType": "receipt", 448 | "val": "" 449 | } 450 | }); 451 | 452 | await this.refresh(res); 453 | } 454 | 455 | /** 456 | * Marks the message as done 457 | * @param {boolean} [state=true] State to use (done/not done) 458 | * @return {Promise} 459 | * @memberof Message 460 | */ 461 | async markAsDone(state = true) { 462 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 463 | 464 | //Load data 465 | const res = await this.edupage.api({ 466 | url: ENDPOINT.TIMELINE_FLAG_HOMEWORK, 467 | data: { 468 | "homeworkid": "timeline:" + this.id, 469 | "flag": "done", 470 | "value": (+state).toString() 471 | } 472 | }); 473 | 474 | if(!("timelineUserProps" in res)) throw new APIError(`Failed to refresh message: Invalid status received '${res.status}'`, this, res); 475 | 476 | this._data.userProps = null; 477 | this.edupage._data.timelineUserProps = res.timelineUserProps; 478 | Message.prototype.init.call(this); 479 | 480 | return state; 481 | } 482 | 483 | /** 484 | * Marks the message as starred 485 | * @param {boolean} [state=true] State to use (starred/not starred) 486 | * @return {Promise} 487 | * @memberof Message 488 | */ 489 | async markAsStarred(state = true) { 490 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 491 | 492 | //Load data 493 | const res = await this.edupage.api({ 494 | url: ENDPOINT.TIMELINE_FLAG_HOMEWORK, 495 | data: { 496 | "homeworkid": "timeline:" + this.id, 497 | "flag": "important", 498 | "value": (+state).toString() 499 | } 500 | }); 501 | 502 | if(!("timelineUserProps" in res)) throw new APIError(`Failed to refresh message: Invalid status received '${res.status}'`, this, res); 503 | 504 | this._data.userProps = null; 505 | this.edupage._data.timelineUserProps = res.timelineUserProps; 506 | Message.prototype.init.call(this); 507 | 508 | return state; 509 | } 510 | 511 | /** 512 | * Refreshes message replies and some other fields 513 | * @param {RawDataObject} [data=null] Raw data to use instead of requesting from the server 514 | * @memberof Message 515 | */ 516 | async refresh(data = null) { 517 | if(!this.edupage) throw new EdupageError(`Message does not have assigned Edupage instance yet`); 518 | 519 | //Load data 520 | const res = data || await this.edupage.api({ 521 | url: ENDPOINT.TIMELINE_GET_REPLIES, 522 | data: { 523 | groupid: this.id, 524 | lastsync: "" 525 | } 526 | }); 527 | 528 | //Invalid response 529 | if(res.status !== API_STATUS.OK) throw new APIError(`Failed to refresh message: Invalid status received '${res.status}'`, this, res); 530 | 531 | //Parse message data of each reply 532 | res.data.reakcie.forEach(e => { 533 | if(typeof e.data === "string") e.data = JSON.parse(e.data); 534 | }); 535 | 536 | //Assign data 537 | this._data = {...this._data, ...res.data}; 538 | 539 | //Init Message object 540 | Message.prototype.init.call(this); 541 | } 542 | 543 | /** 544 | * Parses name of the user 545 | * @static 546 | * @param {string} name Name to parse 547 | * @return {{firstname: string, lastname: string}} Parsed name 548 | * @memberof Message 549 | */ 550 | static parseUsername(name) { 551 | const {firstname = "", lastname = ""} = name.match(/(?.*?)\s?(?\S+)(?:\s\(|$)/)?.groups || {}; 552 | 553 | return {firstname, lastname}; 554 | } 555 | 556 | /** 557 | * Searches for the user object from userstring. If user is not found, the parsed userstring is returned. 558 | * @private 559 | * @param {string} userString Userstring to search for 560 | * @return {{recipient: (User | Teacher | Student | Parent | Plan | Class), wildcard: boolean}} User object and wildcard flag 561 | * @memberof Message 562 | */ 563 | getRecipient(userString) { 564 | //Ignore parents just for now 565 | userString = userString.replace("Only", ""); 566 | 567 | //Parse userString 568 | const {type, id, wildcard} = User.parseUserString(userString); 569 | let recipient = null; 570 | 571 | if(type == ENTITY_TYPE.TEACHER) recipient = this.edupage.teachers.find(e => e.id == id); 572 | if(type == ENTITY_TYPE.STUDENT) recipient = this.edupage.students.find(e => e.id == id); 573 | if(type == ENTITY_TYPE.PARENT) recipient = this.edupage.parents.find(e => e.id == id); 574 | if(type == ENTITY_TYPE.STUD_PLAN || type == ENTITY_TYPE.CUST_PLAN) recipient = this.edupage.plans.find(e => e.id == id); 575 | if(type == ENTITY_TYPE.STUD_CLASS) recipient = this.edupage.classes.find(e => e.id == id); 576 | if(type == ENTITY_TYPE.CLASS) recipient = this.edupage.plans.find(e => e.customClassId == id); 577 | 578 | return {recipient, wildcard}; 579 | } 580 | } 581 | 582 | module.exports = Message; -------------------------------------------------------------------------------- /src/Parent.js: -------------------------------------------------------------------------------- 1 | const Edupage = require("./Edupage"); 2 | const {GENDER, ENTITY_TYPE} = require("./enums"); 3 | const User = require("./User"); 4 | 5 | /** 6 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 7 | */ 8 | 9 | class Parent extends User { 10 | /** 11 | * Creates an instance of Parent. 12 | * @param {RawDataObject} [data={}] 13 | * @param {Edupage} [edupage=null] 14 | * @memberof Parent 15 | */ 16 | constructor(data = {}, edupage = null) { 17 | super(data, edupage); 18 | 19 | /** 20 | * Edupage instance associated to this object 21 | * @type {Edupage} 22 | */ 23 | this.edupage = edupage; 24 | 25 | /** 26 | * @type {string} 27 | */ 28 | this.firstname = data.firstname || null; 29 | 30 | /** 31 | * @type {string} 32 | */ 33 | this.lastname = data.lastname || null; 34 | 35 | /** 36 | * @type {GENDER} 37 | */ 38 | this.gender = data.gender || null; 39 | 40 | /** 41 | * @type {string} 42 | */ 43 | this.id = data.id; 44 | 45 | /** 46 | * @type {string} 47 | */ 48 | this.userString = ENTITY_TYPE.PARENT + this.id; 49 | } 50 | 51 | /** 52 | * 53 | * @param {Edupage} [edupage=null] 54 | * @memberof Parent 55 | */ 56 | init(edupage = null) { 57 | if(edupage) this.edupage = edupage; 58 | } 59 | } 60 | 61 | module.exports = Parent; -------------------------------------------------------------------------------- /src/Period.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | 3 | /** 4 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 5 | */ 6 | 7 | class Period extends RawData { 8 | /** 9 | * Creates an instance of Period. 10 | * @param {RawDataObject} [data=null] Raw data to initialize the instance with. 11 | * @memberof Period 12 | */ 13 | constructor(data = null) { 14 | super(data); 15 | 16 | /** 17 | * Period id 18 | * @type {string} 19 | */ 20 | this.id = data.id; 21 | 22 | /** 23 | * Period name 24 | * @type {string} 25 | */ 26 | this.name = data.name; 27 | 28 | /** 29 | * Period short name 30 | * @type {string} 31 | */ 32 | this.short = data.short; 33 | 34 | /** 35 | * Period start time in format HH:MM 36 | * @type {string} 37 | */ 38 | this.startTime = data.starttime; 39 | 40 | /** 41 | * Period end time in format HH:MM 42 | * @type {string} 43 | */ 44 | this.endTime = data.endtime; 45 | } 46 | 47 | // eslint-disable-next-line valid-jsdoc 48 | /** 49 | * Creates new invalid Period 50 | * @static 51 | * @param {{id?: string?, name?: string, short?: string, startTime?: string, endTime?: string}} [data=null] Data to be used to create the Period (if there's any) 52 | * @return {Period} Created Period 53 | * @memberof Period 54 | */ 55 | static getInvalid(data = null) { 56 | const { 57 | id = null, 58 | name = "Unknown Period", 59 | short = "?", 60 | startTime = "00:00", 61 | endTime = "00:00" 62 | } = data || {}; 63 | 64 | return new Period({ 65 | id, 66 | name, 67 | short, 68 | startTime, 69 | endTime 70 | }); 71 | } 72 | } 73 | 74 | module.exports = Period; -------------------------------------------------------------------------------- /src/Plan.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | const Class = require("./Class"); 3 | const Edupage = require("./Edupage"); 4 | const Season = require("./Season"); 5 | const Student = require("./Student"); 6 | const Subject = require("./Subject"); 7 | const Teacher = require("./Teacher"); 8 | 9 | /** 10 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 11 | */ 12 | 13 | class Plan extends RawData { 14 | /** 15 | * Creates an instance of Plan. 16 | * @param {RawDataObject} [data={}] 17 | * @param {Edupage} [edupage=null] 18 | * @memberof Plan 19 | */ 20 | constructor(data = {}, edupage = null) { 21 | super(data); 22 | 23 | if(!data.settings) data.settings = {}; 24 | if(typeof data.settings === "string") data.settings = JSON.parse(data.settings) || {}; 25 | 26 | /** 27 | * Edupage instance associated to this object 28 | * @type {Edupage} 29 | */ 30 | this.edupage = edupage; 31 | 32 | 33 | /** 34 | * @type {string} 35 | */ 36 | this.id = data.planid; 37 | 38 | /** 39 | * @type {string} 40 | */ 41 | this.subjectId = data.predmetid; 42 | 43 | /** 44 | * @type {string} 45 | */ 46 | this.customClassId = data.triedaid; 47 | 48 | /** 49 | * @type {string} 50 | */ 51 | this.customName = data.nazov; 52 | 53 | /** 54 | * @type {Date} 55 | */ 56 | this.changedDate = data.cas_zmeny || data.lastChange ? new Date(data.cas_zmeny || data.lastChange) : null; 57 | 58 | /** 59 | * @type {number} 60 | */ 61 | this.year = +data.rok; 62 | 63 | /** 64 | * @type {Object} 65 | */ 66 | this.settings = data.settings; 67 | 68 | /** 69 | * @type {boolean} 70 | */ 71 | this.isPublic = !!+data.zverejnit_studentom; 72 | 73 | /** 74 | * @type {string} 75 | * @example "o" 76 | */ 77 | this.state = data.stav; 78 | 79 | /** 80 | * @type {boolean} 81 | */ 82 | this.isValid = !!+data.valid; 83 | 84 | /** 85 | * @type {Date} 86 | */ 87 | this.approvedDate = data.approved ? new Date(data.approved) : null; 88 | 89 | /** 90 | * @type {boolean} 91 | */ 92 | this.isApproved = !!this.approvedDate; 93 | 94 | /** 95 | * @type {string} 96 | */ 97 | this.otherId = data.ineid; 98 | 99 | /** 100 | * @type {number} 101 | */ 102 | this.topicsCount = data.countTopics; 103 | 104 | /** 105 | * @type {number} 106 | */ 107 | this.taughtCount = data.countTaught; 108 | 109 | /** 110 | * @type {number} 111 | */ 112 | this.standardsCount = data.countStandards; 113 | 114 | /** 115 | * @type {string} 116 | */ 117 | this.timetableGroup = data.rozvrhy_skupinaMeno || null; 118 | 119 | /** 120 | * @type {Season} 121 | */ 122 | this.season = null; 123 | 124 | /** 125 | * @type {string} 126 | */ 127 | this.name = data.nazovPlanu; 128 | 129 | /** 130 | * @type {number} 131 | */ 132 | this.classOrdering = data.triedaOrdering; 133 | 134 | /** 135 | * @type {boolean} 136 | */ 137 | this.isEntireClass = !!data.entireClass; 138 | 139 | 140 | /** 141 | * @type {Subject} 142 | */ 143 | this.subject = null; 144 | 145 | /** 146 | * @type {Class[]} 147 | */ 148 | this.classes = []; 149 | 150 | /** 151 | * @type {Teacher} 152 | */ 153 | this.teacher = null; 154 | 155 | /** 156 | * @type {Teacher[]} 157 | */ 158 | this.teachers = []; 159 | 160 | /** 161 | * @type {Student[]} 162 | */ 163 | this.students = []; 164 | 165 | 166 | if(this.edupage) Plan.prototype.init.call(this); 167 | } 168 | 169 | /** 170 | * 171 | * @param {Edupage} [edupage=null] 172 | * @memberof Plan 173 | */ 174 | init(edupage = null) { 175 | if(edupage) this.edupage = edupage; 176 | 177 | if(this._data.obdobie) this.season = this.edupage.seasons.find(e => e.id == this._data.obdobie); 178 | 179 | this.subject = this.edupage.subjects.find(e => e.id == this._data.predmetid); 180 | this.teacher = this.edupage.teachers.find(e => e.id == this._data.ucitelid); 181 | 182 | this.classes = this._data.triedy.map(e => this.edupage.classes.find(t => t.id == e.toString())); 183 | this.teachers = this._data.ucitelids.map(e => this.edupage.teachers.find(t => t.id == e)); 184 | this.students = this._data.students.map(e => this.edupage.students.find(t => t.id == e)); 185 | } 186 | } 187 | 188 | module.exports = Plan; -------------------------------------------------------------------------------- /src/Season.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const Edupage = require("./Edupage"); 5 | 6 | debug.log = console.log.bind(console); 7 | 8 | /** 9 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 10 | */ 11 | 12 | class Season extends RawData { 13 | /** 14 | * Creates an instance of Season. 15 | * @param {RawDataObject} [data={}] 16 | * @param {Edupage} [edupage=null] 17 | * @memberof Season 18 | */ 19 | constructor(data = {}, edupage = null) { 20 | super(data); 21 | 22 | /** 23 | * @type {Edupage} 24 | */ 25 | this.edupage = edupage; 26 | 27 | /** 28 | * @type {string} 29 | */ 30 | this.id = data.id; 31 | 32 | /** 33 | * @type {Date} 34 | */ 35 | this.fromDate = null; 36 | 37 | /** 38 | * @type {Date} 39 | */ 40 | this.toDate = null; 41 | 42 | /** 43 | * @type {string} 44 | */ 45 | this.name = data.nazov; 46 | 47 | /** 48 | * @type {number} 49 | */ 50 | this.halfYear = +data.polrok; 51 | 52 | /** 53 | * @type {number} 54 | */ 55 | this.index = +data.poradie; 56 | 57 | /** 58 | * @type {Season} 59 | */ 60 | this.supSeason = null; 61 | 62 | /** 63 | * @type {string[]} 64 | */ 65 | this.types = data.typy; 66 | 67 | /** 68 | * @type {Season} 69 | */ 70 | this.classificationSeason = null; 71 | 72 | /** 73 | * @type {boolean} 74 | */ 75 | this.isClassification = !!data.klasifikacny; 76 | 77 | if(this.edupage) Season.prototype.init.call(this); 78 | } 79 | 80 | /** 81 | * 82 | * @param {Edupage} [edupage=null] 83 | * @memberof Message 84 | */ 85 | init(edupage = null) { 86 | if(edupage) this.edupage = edupage; 87 | 88 | const year = this.edupage.year; 89 | 90 | this.fromDate = new Date(this._data.od.replace("Y0", year).replace("Y1", year + 1)); 91 | this.toDate = new Date(this._data.do.replace("Y0", year).replace("Y1", year + 1)); 92 | 93 | this.supSeason = this.edupage.seasons.find(e => e.id == this._data.nadobdobie) || null; 94 | if(this.isClassification) this.classificationSeason = this.edupage.seasons.find(e => e.id == this._data.klasifikacny) || null; 95 | } 96 | } 97 | 98 | module.exports = Season; -------------------------------------------------------------------------------- /src/Student.js: -------------------------------------------------------------------------------- 1 | const Class = require("./Class"); 2 | const Edupage = require("./Edupage"); 3 | const {ENTITY_TYPE} = require("./enums"); 4 | const Parent = require("./Parent"); 5 | const User = require("./User"); 6 | 7 | /** 8 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 9 | */ 10 | 11 | class Student extends User { 12 | /** 13 | * Creates an instance of Student. 14 | * @param {RawDataObject} [data={}] 15 | * @param {Edupage} [edupage=null] 16 | * @memberof Student 17 | */ 18 | constructor(data = {}, edupage = null) { 19 | super(data, edupage); 20 | 21 | /** 22 | * Edupage instance associated to this object 23 | * @type {Edupage} 24 | */ 25 | this.edupage = edupage; 26 | 27 | /** 28 | * @type {number} 29 | */ 30 | this.number = +data.number; 31 | 32 | /** 33 | * @type {number} 34 | */ 35 | this.numberInClass = +data.numberinclass; 36 | 37 | /** 38 | * @type {string} 39 | */ 40 | this.parent1Id = data.parent1id; 41 | 42 | /** 43 | * @type {string} 44 | */ 45 | this.parent2Id = data.parent2id; 46 | 47 | /** 48 | * @type {string} 49 | */ 50 | this.parent3Id = data.parent3id; 51 | 52 | /** 53 | * @type {string} 54 | */ 55 | this.userString = ENTITY_TYPE.STUDENT + this.id; 56 | 57 | 58 | /** 59 | * @type {Parent} 60 | */ 61 | this.parent1 = data.parent1id ? new Parent({id: data.parent1id}, edupage) : null; 62 | 63 | /** 64 | * @type {Parent} 65 | */ 66 | this.parent2 = data.parent2id ? new Parent({id: data.parent2id}, edupage) : null; 67 | 68 | /** 69 | * @type {Parent} 70 | */ 71 | this.parent3 = data.parent3id ? new Parent({id: data.parent3id}, edupage) : null; 72 | 73 | 74 | /** 75 | * @type {Class} 76 | */ 77 | this.class = null; 78 | 79 | if(this.edupage) Student.prototype.init.call(this); 80 | } 81 | 82 | /** 83 | * 84 | * @param {Edupage} [edupage=null] 85 | * @memberof Parent 86 | */ 87 | init(edupage = null) { 88 | if(edupage) this.edupage = edupage; 89 | 90 | this.class = this.edupage.classes.find(e => e.id == this._data.classid); 91 | this.parent1 = this.parent1 ? this.edupage.parents.find(e => e.id == this.parent1.id) : this.parent1; 92 | this.parent2 = this.parent2 ? this.edupage.parents.find(e => e.id == this.parent2.id) : this.parent2; 93 | this.parent3 = this.parent3 ? this.edupage.parents.find(e => e.id == this.parent3.id) : this.parent3; 94 | } 95 | 96 | /** 97 | * 98 | * @param {boolean} [parents=false] 99 | * @return {string} 100 | * @memberof Student 101 | */ 102 | getUserString(parents = false) { 103 | return parents ? 104 | this.userString.split(/(?<=[a-z])(?=[0-9])/).join("Only") : 105 | this.userString; 106 | } 107 | } 108 | 109 | module.exports = Student; -------------------------------------------------------------------------------- /src/Subject.js: -------------------------------------------------------------------------------- 1 | const RawData = require("../lib/RawData"); 2 | 3 | class Subject extends RawData { 4 | constructor(data = null) { 5 | super(data); 6 | 7 | /** 8 | * @type {string} 9 | */ 10 | this.id = data.id; 11 | 12 | /** 13 | * @type {string} 14 | */ 15 | this.name = data.name; 16 | 17 | /** 18 | * @type {string} 19 | */ 20 | this.short = data.short; 21 | } 22 | } 23 | 24 | module.exports = Subject; -------------------------------------------------------------------------------- /src/Teacher.js: -------------------------------------------------------------------------------- 1 | const Classroom = require("./Classroom"); 2 | const Edupage = require("./Edupage"); 3 | const {ENTITY_TYPE} = require("./enums"); 4 | const User = require("./User"); 5 | 6 | class Teacher extends User { 7 | /** 8 | * Creates an instance of Teacher. 9 | * @param {Object} [data={}] 10 | * @param {Edupage} [edupage=null] 11 | * @memberof Teacher 12 | */ 13 | constructor(data = {}, edupage = null) { 14 | super(data, edupage); 15 | 16 | /** 17 | * Edupage instance associated to this object 18 | * @type {Edupage} 19 | */ 20 | this.edupage = edupage; 21 | 22 | /** 23 | * @type {number} 24 | */ 25 | this.cb_hidden = data.cb_hidden; 26 | 27 | /** 28 | * @type {string} 29 | */ 30 | this.short = data.short; 31 | 32 | /** 33 | * @type {string} 34 | */ 35 | this.userString = ENTITY_TYPE.TEACHER + this.id; 36 | 37 | 38 | /** 39 | * @type {Classroom} 40 | */ 41 | this.classroom = data.classroom; 42 | 43 | if(this.edupage) Teacher.prototype.init.call(this); 44 | } 45 | 46 | /** 47 | * 48 | * @param {Edupage} [edupage=null] 49 | * @memberof Parent 50 | */ 51 | init(edupage = null) { 52 | if(edupage) this.edupage = edupage; 53 | 54 | this.classroom = this.edupage.classrooms.find(e => e.id == this._data.classroomid); 55 | } 56 | } 57 | 58 | module.exports = Teacher; -------------------------------------------------------------------------------- /src/Test.js: -------------------------------------------------------------------------------- 1 | const Assignment = require("./Assignment"); 2 | const Edupage = require("./Edupage"); 3 | 4 | /** 5 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 6 | */ 7 | 8 | class Test extends Assignment { 9 | /** 10 | * Creates an instance of Test. 11 | * @param {RawDataObject} [data={}] 12 | * @param {Edupage} [edupage=null] 13 | * @memberof Test 14 | */ 15 | constructor(data = {}, edupage = null) { 16 | super(data, edupage); 17 | } 18 | } 19 | 20 | module.exports = Test; -------------------------------------------------------------------------------- /src/Timetable.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const RawData = require("../lib/RawData"); 4 | const Edupage = require("./Edupage"); 5 | const {FatalError, ParseError} = require("./exceptions"); 6 | const Lesson = require("./Lesson"); 7 | 8 | debug.log = console.log.bind(console); 9 | 10 | /** 11 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 12 | */ 13 | 14 | class Timetable extends RawData { 15 | /** 16 | * Creates an instance of Timetable. 17 | * @param {RawDataObject} [data={}] 18 | * @param {string} [date=null] 19 | * @param {Edupage} [edupage=null] 20 | * @memberof Timetable 21 | */ 22 | constructor(data = {}, date = null, edupage = null) { 23 | super(data); 24 | 25 | /** 26 | * Edupage instance associated to this object 27 | * @type {Edupage} 28 | */ 29 | this.edupage = edupage; 30 | 31 | /** 32 | * @type {Date} 33 | */ 34 | this.date = new Date(date); 35 | 36 | /** 37 | * @type {Lesson[]} 38 | */ 39 | this.lessons = []; 40 | 41 | 42 | /** 43 | * @type {number} 44 | */ 45 | this.week = data.tt_week; 46 | 47 | if(this.edupage) Timetable.prototype.init.call(this); 48 | } 49 | 50 | /** 51 | * @param {Edupage} [edupage=null] 52 | * @memberof Timetable 53 | */ 54 | init(edupage = null) { 55 | if(edupage) this.edupage = edupage; 56 | 57 | this.lessons = this._data.plan.filter(e => e.type == "lesson").map(data => new Lesson(data, this.edupage)); 58 | } 59 | 60 | /** 61 | * 62 | * @static 63 | * @param {string} html 64 | * @return {RawDataObject} 65 | * @memberof Timetable 66 | */ 67 | static parse(html) { 68 | const match = html.match(/classbook\.fill\([\s\S]*?,\s?([\s\S]+?)(?:,\s?\[[\s\S]*?\])?\);/mi); 69 | 70 | if(!match || !match[1]) return FatalError.throw(new ParseError("Failed to parse timetable data from html"), {html, match}); 71 | 72 | try { 73 | return JSON.parse(match[1]); 74 | } catch(e) { 75 | return FatalError.throw(new ParseError("Failed to parse JSON from timetable html"), {html, match}); 76 | } 77 | } 78 | } 79 | 80 | module.exports = Timetable; -------------------------------------------------------------------------------- /src/User.js: -------------------------------------------------------------------------------- 1 | const debug = require("debug")("edupage:log"); 2 | const error = require("debug")("edupage:error"); 3 | const {default: fetch} = require("node-fetch"); 4 | const CookieJar = require("../lib/CookieJar"); 5 | const {LoginError, ParseError, EdupageError, APIError, MessageError, FatalError} = require("./exceptions"); 6 | const {GENDER, ENDPOINT, ENTITY_TYPE, API_STATUS, TIMELINE_ITEM_TYPE} = require("./enums"); 7 | const Edupage = require("./Edupage"); 8 | const RawData = require("../lib/RawData"); 9 | const Attachment = require("./Attachment"); 10 | 11 | debug.log = console.log.bind(console); 12 | 13 | /** 14 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 15 | */ 16 | 17 | /** 18 | * @typedef {import("./Teacher")} Teacher 19 | */ 20 | /** 21 | * @typedef {import("./Student")} Student 22 | */ 23 | /** 24 | * @typedef {import("./Parent")} Parent 25 | */ 26 | /** 27 | * @typedef {import("./Message")} Message 28 | */ 29 | /** 30 | * @typedef {import("./enums").EntityType} EntityType 31 | */ 32 | 33 | 34 | /** 35 | * @typedef {Object} LoginOptions 36 | * @prop {string | null | undefined} [code2FA=undefined] If the provided value is typeof `string`, it's considered as a 2FA code (should be provided after first unsuccessful login). If it's `null` the 2FA will be skipped. If omitted or `undefined` and the 2FA is requested by the Edupage, the function will resolve with `null`. 37 | * @prop {string} [user] Can be used to select a specific user in case there are more. 38 | * @prop {string} [edupage=""] The edupage subdomain (origin) to login to. Try to set this if you have trouble logging in (e.g. incorrect password error). 39 | */ 40 | 41 | 42 | class User extends RawData { 43 | /** 44 | * Creates an instance of User. 45 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 46 | * @param {Edupage} [edupage=null] Edupage instance to use. 47 | * @memberof User 48 | */ 49 | constructor(data = {}, edupage = null) { 50 | super(data); 51 | 52 | /** 53 | * Edupage instance associated to this object. 54 | * @type {Edupage} 55 | */ 56 | this.edupage = edupage; 57 | 58 | /** 59 | * Date since when the user is registered 60 | * @type {Date} 61 | */ 62 | this.dateFrom = data.datefrom ? new Date(data.datefrom) : null; 63 | 64 | /** 65 | * Date of expected leave of the user 66 | * @type {Date} 67 | */ 68 | this.dateTo = data.dateto ? new Date(data.dateto) : null; 69 | 70 | /** 71 | * Firstname of the user 72 | * @type {string} 73 | */ 74 | this.firstname = data.firstname; 75 | 76 | /** 77 | * Lastname of the user 78 | * @type {string} 79 | */ 80 | this.lastname = data.lastname; 81 | 82 | /** 83 | * Gender of the user 84 | * @type {GENDER} 85 | */ 86 | this.gender = data.gender; 87 | 88 | /** 89 | * Edupage identifier of the user in format of number 90 | * @example "845796" 91 | * @type {string} 92 | */ 93 | this.id = data.id; 94 | 95 | /** 96 | * Edupage userstring of the user 97 | * @example "Student845796" 98 | * @type {string} 99 | */ 100 | this.userString = null; 101 | 102 | /** 103 | * Flag marking if the user has left the school 104 | * @type {boolean} 105 | */ 106 | this.isOut = data.isOut; 107 | 108 | /** 109 | * Edupage origin of the user (subdomain) 110 | * @type {string} 111 | */ 112 | this.origin = null; 113 | 114 | /** 115 | * Login credentials of the user. Set if the user is logged in or has attempted to log in. 116 | * @type {{username: string, password: string} | null} 117 | */ 118 | this.credentials = null; 119 | 120 | /** 121 | * CookieJar object storing current session cookies for logged in user 122 | * @type {CookieJar} 123 | */ 124 | this.cookies = null; 125 | 126 | /** 127 | * Flag telling if the user is logged in 128 | * @type {boolean} 129 | */ 130 | this.isLoggedIn = false; 131 | 132 | /** 133 | * Email address of the logged in user 134 | * @type {string} 135 | */ 136 | this.email = null; 137 | 138 | if(this.edupage) User.prototype.init.call(this); 139 | } 140 | 141 | /** 142 | * Initializes instance with raw data 143 | * @param {Edupage} [edupage=null] 144 | * @memberof Class 145 | */ 146 | init(edupage = null) { 147 | if(edupage) this.edupage = edupage; 148 | 149 | this.origin = this.edupage.user.origin; 150 | } 151 | 152 | /** 153 | * @typedef {Object} PollOption 154 | * @prop {string} text Text of the option. 155 | * @prop {string} [id] Id of the option. If not provided, a new one will be generated. 156 | */ 157 | 158 | /** 159 | * @typedef {Object} PollOptions 160 | * @prop {PollOption[]} options Options to be added to the poll. 161 | * @prop {boolean} [multiple=false] If `true` multiple choices can be selected. 162 | */ 163 | 164 | /** 165 | * @typedef {Object} MessageOptions 166 | * @prop {string} text Text of the message. 167 | * @prop {boolean} [important=false] If `true` the message will be marked as important. You will also be able to track who has read the message. 168 | * @prop {boolean} [parents=false] If `true` the message will be sent to student as well as their parents. 169 | * @prop {boolean} [allowReplies=true] Allows to disable replies to the message. 170 | * @prop {boolean} [repliesToAuthorOnly=false] Allows to reply only to the author of the message. By default, replies are sent to all users. 171 | * @prop {Attachment[]} [attachments=[]] Attachments to be added to the message. 172 | * @prop {PollOptions} [poll] Poll to be added to the message. 173 | */ 174 | 175 | /** 176 | * Sends a message to the user 177 | * @param {MessageOptions} options Message options 178 | * @this {User | Teacher | Student | Parent} 179 | * @memberof User 180 | */ 181 | async sendMessage(options) { 182 | const { 183 | text = "", 184 | important = false, 185 | parents = false, 186 | allowReplies = true, 187 | repliesToAuthorOnly = false, 188 | attachments = [], 189 | poll = null 190 | } = options; 191 | 192 | if(!this.edupage) throw new EdupageError(`User does not have assigned Edupage instance yet`); 193 | 194 | //Post message 195 | const hasPoll = poll && poll.options && poll.options; 196 | const res = await this.edupage.api({ 197 | url: ENDPOINT.TIMELINE_CREATE_ITEM, 198 | data: { 199 | attachements: JSON.stringify(attachments.reduce((a, b) => ({...a, ...b.toJSON()}), {})), 200 | receipt: important ? "1" : "0", 201 | repliesDisabled: !allowReplies ? "1" : "0", 202 | repliesToAllDisabled: !allowReplies || repliesToAuthorOnly ? "1" : "0", 203 | selectedUser: this.getUserString(parents), 204 | text: text, 205 | typ: TIMELINE_ITEM_TYPE.MESSAGE, 206 | votingParams: hasPoll ? JSON.stringify({ 207 | "answers": poll.options.map(e => ({ 208 | text: e.text, 209 | id: e.id || Math.random().toString(16).slice(2) 210 | })), 211 | "multiple": false 212 | }) : undefined 213 | } 214 | }); 215 | 216 | //Request failed 217 | if(res.status !== API_STATUS.OK) { 218 | error(`Received invalid status from the server '${res.status}'`); 219 | throw new APIError(`Failed to send message: Invalid status received '${res.status}'`, res); 220 | } 221 | 222 | //No changes 223 | if(!res.changes?.length) { 224 | error(`Failed to send message (no changes made) (${res.changes})`); 225 | throw new MessageError(`Failed to send message: No changes were made`, res); 226 | } 227 | 228 | //Debug 229 | if(res.changes.length > 1) debug(`[Message] Multiple changes after posting single message`, res.changes); 230 | 231 | //Return Message object 232 | return new (require("./Message"))(res.changes[0], this.edupage); 233 | } 234 | 235 | /** 236 | * @typedef {import("../lib/ResponseTypings").MAuthResponse} MAuthResponse 237 | */ 238 | 239 | /** 240 | * Logs in the user. Provide third parameter as login options if you have troubles logging in. 241 | * @param {string} username Username of the user 242 | * @param {string} password Password of the user 243 | * @param {LoginOptions} [options] Login options 244 | * @return {Promise} Returns a promise that resolves with the `User` object if successful. If the 2FA is requested by the Edupage, the promise will resolve with `null`. 245 | * @memberof User 246 | */ 247 | async login(username, password, options = {}) { 248 | if(!username || !password) throw new LoginError(`Invalid credentials`); 249 | 250 | //Setup options 251 | if(!options) options = {}; 252 | if(!options.code2FA) options.code2FA = undefined; 253 | if(!options.user) options.user = undefined; 254 | 255 | //Create a new CookieJar instance to store required cookies 256 | this.cookies = new CookieJar(); 257 | 258 | return new Promise((resolve, reject) => { 259 | debug(`[Login] Logging in as ${username}...`); 260 | 261 | const payload = { 262 | "m": username, 263 | "h": password, 264 | "edupage": options.edupage || "", 265 | "plgc": null, 266 | "ajheslo": "1", 267 | "hasujheslo": "1", 268 | "ajportal": "1", 269 | "ajportallogin": "1", 270 | "mobileLogin": "1", 271 | "version": "2020.0.18", 272 | "fromEdupage": options.edupage || "", 273 | "device_name": null, 274 | "device_id": null, 275 | "device_key": "", 276 | "os": null, 277 | "murl": null, 278 | "edid": "" 279 | }; 280 | 281 | const loginServer = options.edupage || "login1"; 282 | const skip2Fa = options.code2FA === null; 283 | if(typeof options.code2FA === "string") payload.t2fasec = options.code2FA; 284 | 285 | fetch(`https://${loginServer}.edupage.org/login/mauth`, { 286 | "headers": { 287 | "accept": "application/json, text/javascript, */*; q=0.01", 288 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8" 289 | }, 290 | "method": "POST", 291 | "body": new URLSearchParams(payload).toString() 292 | }).then(res => { 293 | debug(`[Login] Saving received cookies...`); 294 | this.cookies.setCookie(res); 295 | return res.json(); 296 | }).then(async (/**@type {MAuthResponse}*/json) => { 297 | let selectedUser = null; 298 | 299 | //Error handling 300 | if(!json.users.length) { 301 | if(json.needEdupage) { 302 | reject(new LoginError(`Failed to login: Incorrect username. (If you are sure that the username is correct, try providing 'edupage' option)`)); 303 | } else { 304 | reject(new LoginError(`Failed to login: Incorrect password. (If you are sure that the password is correct, try providing 'edupage' option)`)); 305 | } 306 | return; 307 | } 308 | 309 | //Process response 310 | if(json.users.length == 1) { 311 | if(json.users[0].need2fa == "1" && !skip2Fa) { 312 | if(json.t2fasec) { 313 | debug(`[Login] 2FA code is invalid`); 314 | return reject(new LoginError(`Invalid 2FA code`)); 315 | } else { 316 | debug(`[Login] 2FA was requested by the Edupage`); 317 | return resolve(null); 318 | } 319 | } else { 320 | debug(`[Login] Successfully logged in`); 321 | selectedUser = json.users[0]; 322 | } 323 | } else { 324 | debug(`[Login] Found multiple users with the same username`); 325 | if(options.user) { 326 | const user = json.users.find(user => user.userid == options.user); 327 | 328 | if(user) { 329 | debug(`[Login] Selected user by 'user' option`); 330 | selectedUser = user; 331 | } 332 | } 333 | if(!selectedUser) { 334 | error(`[Login] No user selected`); 335 | return reject(new LoginError(`Multiple users found: ${json.users.map(user => `${user.userid} (${user.firstname} ${user.lastname})`).join(", ")}. Please, pass the selected user as 'user' option to login options.`)); 336 | } 337 | } 338 | 339 | //Update values 340 | this.cookies.setCookie("PHPSESSID", selectedUser.esid); 341 | this.origin = selectedUser.edupage; 342 | this.isLoggedIn = true; 343 | 344 | this.credentials = { 345 | username, 346 | password 347 | }; 348 | 349 | debug(`[Login] Login successful`); 350 | resolve(this); 351 | }).catch(err => { 352 | error(`[Login] Failed to login user:`, err); 353 | reject(err); 354 | }); 355 | }); 356 | } 357 | 358 | /** 359 | * Retruns Edupage's representation of the user id. 360 | * @return {string} Userstring 361 | * @memberof User 362 | */ 363 | getUserString() { 364 | return this.userString; 365 | } 366 | 367 | /** 368 | * Creates an instance of Student or Teacher from user data. 369 | * @static 370 | * @param {string} userString Userstring to create an instance of 371 | * @param {RawDataObject} [data={}] Raw data to use for the instance 372 | * @param {Edupage} [edupage=null] Edupage instance to use for the instance 373 | * @return {User | Teacher | Student | Parent} User instance 374 | * @memberof User 375 | */ 376 | static from(userString, data = {}, edupage = null) { 377 | const {id, type} = this.parseUserString(userString); 378 | 379 | data.id = id || null; 380 | 381 | if(type == ENTITY_TYPE.TEACHER) return new (require("./Teacher"))(data, edupage); 382 | if(type == ENTITY_TYPE.STUDENT) return new (require("./Student"))(data, edupage); 383 | if(type == ENTITY_TYPE.PARENT) return new (require("./Parent"))(data, edupage); 384 | 385 | const user = new User(data, edupage); 386 | user.userString = userString; 387 | 388 | return user; 389 | } 390 | 391 | /** 392 | * Parses the user string to provide some useful information 393 | * @static 394 | * @param {string} userString Userstring to parse 395 | * @return {{id: string, type: EntityType, wildcard: boolean}} Parsed userstring 396 | * @memberof User 397 | */ 398 | static parseUserString(userString) { 399 | const id = (userString.match(/-?\d+/) || [])[0]; 400 | const type = ((userString.match(/[a-z]+/i) || [])[0] || ""); 401 | const wildcard = userString.indexOf("*") > -1; 402 | 403 | return {id, type, wildcard}; 404 | } 405 | } 406 | 407 | module.exports = User; -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | const SESSION_PING_INTERVAL_MS = 10 * 60 * 1000; //10 minutes 2 | 3 | module.exports = { 4 | SESSION_PING_INTERVAL_MS, 5 | }; -------------------------------------------------------------------------------- /src/enums.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Edupage API endpoint 3 | * @typedef {number} APIEndpoint 4 | */ 5 | 6 | /** 7 | * Gender type 8 | * @typedef {string} Gender 9 | */ 10 | 11 | /** 12 | * Edupage primitive enitity type 13 | * @typedef {string} EntityType 14 | */ 15 | 16 | /** 17 | * Edupage API resporse status 18 | * @typedef {string} APIStatus 19 | */ 20 | 21 | /** 22 | * Assignment type 23 | * @typedef {string} AssignmentType 24 | */ 25 | 26 | /** 27 | * Assignment group 28 | * @typedef {AssignmentType[]} AssignmentGroup 29 | */ 30 | 31 | /** 32 | * Timeline item type 33 | * @typedef {string} TimelineItemType 34 | */ 35 | 36 | 37 | 38 | /** 39 | * @enum {APIEndpoint} 40 | */ 41 | const ENDPOINT = { 42 | DASHBOARD_GET_USER: 1, 43 | DASHBOARD_GET_CLASSBOOK: 2, 44 | DASHBOARD_GCALL: 3, 45 | DASHBOARD_SIGN_ONLINE_LESSON: 4, 46 | TIMELINE_GET_DATA: 5, 47 | TIMELINE_GET_REPLIES: 6, 48 | TIMELINE_GET_CREATED_ITEMS: 7, 49 | TIMELINE_CREATE_ITEM: 8, 50 | TIMELINE_CREATE_CONFIRMATION: 9, 51 | TIMELINE_CREATE_REPLY: 10, 52 | TIMELINE_FLAG_HOMEWORK: 11, 53 | TIMELINE_UPLOAD_ATTACHMENT: 12, 54 | ELEARNING_TEST_DATA: 13, 55 | ELEARNING_TEST_RESULTS: 14, 56 | ELEARNING_CARDS_DATA: 15, 57 | GRADES_DATA: 16, 58 | SESSION_PING: 17 59 | }; 60 | 61 | /** 62 | * @enum {Gender} 63 | */ 64 | const GENDER = { 65 | MALE: "M", 66 | FEMALE: "F" 67 | }; 68 | 69 | /** 70 | * @enum {EntityType} 71 | */ 72 | const ENTITY_TYPE = { 73 | STUD_PLAN: "StudPlan", 74 | STUDENT: "Student", 75 | CUST_PLAN: "CustPlan", 76 | STUDENT_ONLY: "StudentOnly", 77 | STUD_CLASS: "StudTrieda", 78 | TEACHER: "Ucitel", 79 | ALL: "*", 80 | CLASS: "Trieda", 81 | STUDENT_ALL: "Student*", 82 | STUDENTONLY_ALL: "StudentOnly*", 83 | TEACHER_ALL: "Ucitel*", 84 | ADMIN: "Admin", 85 | PARENT: "Parent" 86 | }; 87 | 88 | /** 89 | * @enum {APIStatus} 90 | */ 91 | const API_STATUS = { 92 | OK: "ok", 93 | FAIL: "fail" 94 | }; 95 | 96 | /** 97 | * @enum {AssignmentType} 98 | */ 99 | const ASSIGNMENT_TYPE = { 100 | HOMEWORK: "hw", 101 | ETEST_HOMEWORK: "etesthw", 102 | BIG_EXAM: "bexam", 103 | EXAM: "exam", 104 | SMALL_EXAM: "sexam", 105 | ORAL_EXAM: "oexam", 106 | REPORT_EXAM: "rexam", 107 | TESTING: "testing", 108 | TEST: "test", 109 | PROJECT_EXAM: "pexam", 110 | ETEST: "etest", 111 | ETEST_PRINT: "etestprint", 112 | ETEST_LESSON: "etestlesson", 113 | LESSON: "lekcia", 114 | PROJECT: "projekt", 115 | RESULT: "result", 116 | CURRICULUM: "ucivo", 117 | TIMELINE: "timeline" 118 | }; 119 | 120 | /** 121 | * @enum {AssignmentGroup} 122 | */ 123 | const ASSIGNMENT_GROUP = { 124 | HOMEWORK: ["hw", "etesthw"], 125 | EXAM: ["bexam", "sexam", "oexam", "rexam", "testing"], 126 | TEST: ["test", "etest", "etestprint"], 127 | PROJECT: ["pexam", "projekt"], 128 | PRESENTATION: ["etestlesson", "lekcia"], 129 | OTHER: ["result", "ucivo", "timeline"] 130 | }; 131 | 132 | /** 133 | * @enum {TimelineItemType} 134 | */ 135 | const TIMELINE_ITEM_TYPE = { 136 | MESSAGE: "sprava", 137 | MESSAGE_TO_SUBTITUTER: "spravasuplujucemu", 138 | NOTICEBOARD: "nastenka", 139 | GRADE_ANNOUNCEMENT: "nastenka", 140 | GRADE: "znamka", 141 | NOTE: "vcelicka", 142 | HOMEWORK: "homework", 143 | HOMEWORK_STUDENT_STATE: "homework", 144 | ABSENCE_NOTE: "ospravedlnenka", 145 | ABSENCE_NOTE_REMINDER: "ospravedlnenka_reminder", 146 | PROCESS: "process", 147 | PROCESS_ADMIN: "processadmin", 148 | STUDENT_ABSENT: "student_absent", 149 | ACCIDENT: "accident", 150 | EVENT: "event", 151 | TIMETABLE: "timetable", 152 | SUBSTITUTION: "substitution", 153 | CANTEEN_MENU: "stravamenu", 154 | CANTEEN_CREDIT: "strava_kredit", 155 | CANTEEN_SUSPEND_REINSTATE_ORDERS: "strava_prerusObnovObj", 156 | CANTEEN_OPENING: "strava_vydaj", 157 | SURVEY: "anketa", 158 | PLAN: "plan", 159 | SETTINGS: "settings", 160 | ALBUM: "album", 161 | NEWS: "news", 162 | TEST_ASSIGNMENT: "testpridelenie", 163 | TEST_RESULT: "testvysledok", 164 | CHAT: "chat", 165 | CHECK_IN: "pipnutie", 166 | CONSULTATION_MESSAGE: "konzultaciemsg", 167 | CONSULTATION: "konzultacie", 168 | PAYMENTS: "payments", 169 | SIGN_IN: "signin", 170 | CURRICULUM: "ucivo", 171 | CURRICULUM_REMINDER: "ucivo_reminder", 172 | BLACKBOARD: "bb", 173 | STUDENT_PICKUP: "odchadzka", 174 | TIMETABLE_CLOUD_GENERATE: "ttcloudgen", 175 | CONFIRMATION: "confirmation", 176 | CONTEST: "contest" 177 | }; 178 | 179 | 180 | module.exports = { 181 | GENDER, 182 | ENDPOINT, 183 | ENTITY_TYPE, 184 | API_STATUS, 185 | ASSIGNMENT_TYPE, 186 | ASSIGNMENT_GROUP, 187 | TIMELINE_ITEM_TYPE 188 | }; -------------------------------------------------------------------------------- /src/exceptions.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | /** 5 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 6 | */ 7 | 8 | class LoginError extends Error { 9 | constructor(message) { 10 | super(message); 11 | 12 | this.name = "LoginError"; 13 | } 14 | } 15 | 16 | class ParseError extends Error { 17 | constructor(message) { 18 | super(message); 19 | 20 | this.name = "ParseError"; 21 | } 22 | } 23 | 24 | class EdupageError extends Error { 25 | constructor(message) { 26 | super(message); 27 | 28 | this.name = "EdupageError"; 29 | } 30 | } 31 | 32 | class APIError extends Error { 33 | constructor(message, ...data) { 34 | super(message); 35 | 36 | this.name = "APIError"; 37 | this.data = data; 38 | } 39 | } 40 | 41 | class MessageError extends Error { 42 | constructor(message, ...data) { 43 | super(message); 44 | 45 | this.name = "MessageError"; 46 | this.data = data; 47 | } 48 | } 49 | 50 | class AttachmentError extends Error { 51 | constructor(message, ...data) { 52 | super(message); 53 | 54 | this.name = "AttachmentError"; 55 | this.data = data; 56 | } 57 | } 58 | 59 | class FatalError extends Error { 60 | static warningsEnabled = !process.argv.includes("--edu-disable-warnings=true"); 61 | 62 | /** 63 | * @static 64 | * @param {Error} error 65 | * @param {RawDataObject} data 66 | * @returns {any} 67 | * @memberof FatalError 68 | */ 69 | static throw(error, data) { 70 | const date = new Date(); 71 | const filename = `fatal_error_${date.toISOString().replace(/:/g, "-").replace(/\..\d*/, "")}.json`; 72 | const dirname = path.resolve(__dirname, "../logs"); 73 | const file = path.join(dirname, filename); 74 | const err = Object.getOwnPropertyNames(error).reduce((obj, prop) => ((obj[prop] = error[prop]), obj), {}); //To convert `Error` object to Object literal 75 | 76 | //Create logs direcory and save error log 77 | if(!fs.existsSync(dirname)) fs.mkdirSync(dirname); 78 | fs.writeFileSync(file, JSON.stringify({error: err, data}, null, "\t")); 79 | 80 | const message = ` 81 | # 82 | # A fatal error has occurred! 83 | # 84 | # This is probably not your fault! 85 | # If this happens often, please consider reporting a bug. 86 | # 87 | # Error thrown: 88 | # ${error.stack.split("\n").join("\n# ")} 89 | # 90 | # Summary: 91 | # Time: ${date.toString()} 92 | # EdupageAPI version: v${require("../package.json").version} 93 | # Node.js version: ${process.version} 94 | # Error log: ${file} 95 | # 96 | # If you would like to submit an issue, please visit: 97 | # > https://github.com/loumadev/EdupageAPI/issues 98 | # (Please, include this error message + error log) 99 | # 100 | `; 101 | //Log error to stderr and exit process 102 | console.error(message); 103 | process.exitCode = 1; 104 | 105 | //To comply jslint 106 | return undefined; 107 | } 108 | 109 | /** 110 | * @static 111 | * @param {Error} error 112 | * @param {RawDataObject} data 113 | * @returns {any} 114 | * @memberof FatalError 115 | */ 116 | static warn(error, data) { 117 | if(!this.warningsEnabled) return; 118 | 119 | const date = new Date(); 120 | const filename = `warning_${date.toISOString().replace(/:/g, "-").replace(/\..\d*/, "")}.json`; 121 | const dirname = path.resolve(__dirname, "../logs"); 122 | const file = path.join(dirname, filename); 123 | const err = Object.getOwnPropertyNames(error).reduce((obj, prop) => ((obj[prop] = error[prop]), obj), {}); //To convert `Error` object to Object literal 124 | 125 | //Create logs direcory and save error log 126 | if(!fs.existsSync(dirname)) fs.mkdirSync(dirname); 127 | fs.writeFileSync(file, JSON.stringify({error: err, data}, null, "\t")); 128 | 129 | const message = ` 130 | # 131 | # An error has occurred! 132 | # 133 | # This is probably not your fault! 134 | # It should not cause the program to crash, but some functionality can be limited. 135 | # If this happens often, please consider reporting a bug. 136 | # You can disable the warnings by including "--edu-disable-warnings=true" as a CLI argument. 137 | # 138 | # Error thrown: 139 | # ${error.stack.split("\n").join("\n# ")} 140 | # 141 | # Summary: 142 | # Time: ${date.toString()} 143 | # EdupageAPI version: v${require("../package.json").version} 144 | # Node.js version: ${process.version} 145 | # Error log: ${file} 146 | # 147 | # If you would like to submit an issue, please visit: 148 | # > https://github.com/loumadev/EdupageAPI/issues 149 | # (Please, include this error message + error log) 150 | # 151 | `; 152 | //Log warning to stderr 153 | console.warn(message); 154 | 155 | //To comply jslint 156 | return undefined; 157 | } 158 | } 159 | 160 | module.exports = { 161 | LoginError, 162 | ParseError, 163 | EdupageError, 164 | APIError, 165 | MessageError, 166 | AttachmentError, 167 | FatalError 168 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "**/*.js" 4 | ], 5 | "exclude": [ 6 | "**/test/*" 7 | ], 8 | "compilerOptions": { 9 | "declaration": true, 10 | "emitDeclarationOnly": true, 11 | "allowJs": true, 12 | "target": "es5", 13 | "module": "commonjs", 14 | "sourceMap": true, 15 | "outDir": "./typings/" 16 | } 17 | } -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | import ASC = require("./src/ASC"); 2 | import Application = require("./src/Application"); 3 | import Assignment = require("./src/Assignment"); 4 | import Attachment = require("./src/Attachment"); 5 | import Class = require("./src/Class"); 6 | import Classroom = require("./src/Classroom"); 7 | import Edupage = require("./src/Edupage"); 8 | import Grade = require("./src/Grade"); 9 | import Homework = require("./src/Homework"); 10 | import Lesson = require("./src/Lesson"); 11 | import Message = require("./src/Message"); 12 | import Parent = require("./src/Parent"); 13 | import Period = require("./src/Period"); 14 | import Plan = require("./src/Plan"); 15 | import Season = require("./src/Season"); 16 | import Student = require("./src/Student"); 17 | import Subject = require("./src/Subject"); 18 | import Teacher = require("./src/Teacher"); 19 | import Test = require("./src/Test"); 20 | import Timetable = require("./src/Timetable"); 21 | import User = require("./src/User"); 22 | import { GENDER } from "./src/enums"; 23 | import { ENDPOINT } from "./src/enums"; 24 | import { ENTITY_TYPE } from "./src/enums"; 25 | import { API_STATUS } from "./src/enums"; 26 | import { ASSIGNMENT_TYPE } from "./src/enums"; 27 | import { ASSIGNMENT_GROUP } from "./src/enums"; 28 | import { TIMELINE_ITEM_TYPE } from "./src/enums"; 29 | import { LoginError } from "./src/exceptions"; 30 | import { ParseError } from "./src/exceptions"; 31 | import { EdupageError } from "./src/exceptions"; 32 | import { APIError } from "./src/exceptions"; 33 | import { MessageError } from "./src/exceptions"; 34 | import { AttachmentError } from "./src/exceptions"; 35 | import { FatalError } from "./src/exceptions"; 36 | export { ASC, Application, Assignment, Attachment, Class, Classroom, Edupage, Grade, Homework, Lesson, Message, Parent, Period, Plan, Season, Student, Subject, Teacher, Test, Timetable, User, GENDER, ENDPOINT, ENTITY_TYPE, API_STATUS, ASSIGNMENT_TYPE, ASSIGNMENT_GROUP, TIMELINE_ITEM_TYPE, LoginError, ParseError, EdupageError, APIError, MessageError, AttachmentError, FatalError }; 37 | -------------------------------------------------------------------------------- /typings/lib/CookieJar.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export = CookieJar; 3 | declare class CookieJar { 4 | /** 5 | * Creates an instance of CookieJar. 6 | * @memberof CookieJar 7 | */ 8 | constructor(...args: any[]); 9 | /** 10 | * @type {CookieJar.Cookie[]} 11 | */ 12 | cookies: (typeof Cookie)[]; 13 | /** 14 | * Adds cookie to the Jar 15 | * @param {string | CookieJar.Cookie | http.ServerResponse | http.IncomingMessage | fetch.Response} cookie Cookie name (requires second parameter), Cookie String, CookieJar.Cookie object, ServerResponseLike object 16 | * @param {string} [value=undefined] 17 | * @param {Object} [options={}] 18 | * @return {CookieJar} 19 | * @memberof CookieJar 20 | */ 21 | setCookie(cookie: string | typeof Cookie | http.ServerResponse | http.IncomingMessage | fetch.Response, value?: string, options?: { 22 | [x: string]: any; 23 | }): CookieJar; 24 | /** 25 | * Retrns cookie object found by name 26 | * @param {string} name Cookie name 27 | * @return {CookieJar.Cookie} Cookie object if found, otherwise undefined 28 | * @memberof CookieJar 29 | */ 30 | getCookie(name: string): typeof Cookie; 31 | /** 32 | * Removes cookie from the Jar 33 | * @param {string | CookieJar.Cookie} cookie 34 | * @return {CookieJar.Cookie} Deleted cookie 35 | * @memberof CookieJar 36 | */ 37 | deleteCookie(cookie: string | typeof Cookie): typeof Cookie; 38 | /** 39 | * Sends header with cookies 40 | * @param {http.ServerResponse} response Server response object 41 | * @param {boolean} [full=true] Include cookie properties and flags 42 | * @return {CookieJar} 43 | * @memberof CookieJar 44 | */ 45 | sendCookies(response: http.ServerResponse, full?: boolean): CookieJar; 46 | /** 47 | * Converts Cookie object to cookie string 48 | * @param {boolean} [full=true] Include cookie properties and flags 49 | * @return {string} Cookie String 50 | * @memberof CookieJar 51 | */ 52 | toString(full?: boolean): string; 53 | /** 54 | * Checks if the Jar is empty 55 | * @return {boolean} true if Jar is empty, otherwise false 56 | * @memberof CookieJar 57 | */ 58 | isEmpty(): boolean; 59 | /** 60 | * Checks if the Jar contains cookie with certain name 61 | * @param {string} name Cookie name 62 | * @return {boolean} true if Jar contians cookie with certain name, otherwise false 63 | * @memberof CookieJar 64 | */ 65 | includes(name: string): boolean; 66 | /** 67 | * Adds cookies to the Jar 68 | * @param {CookieJar.Cookie[]} cookies 69 | * @memberof CookieJar 70 | */ 71 | _addCookiesToJar(...cookies: (typeof Cookie)[]): void; 72 | /** 73 | * Removes expired cookies from the Jar 74 | * @memberof CookieJar 75 | */ 76 | _removeExpiredCookies(): void; 77 | } 78 | declare namespace CookieJar { 79 | export { Cookie }; 80 | } 81 | declare class Cookie { 82 | static keywords: string[]; 83 | static formatKeyword(key: any): string | false; 84 | static parse(cookieStringArray: any): any; 85 | name: string; 86 | value: string; 87 | /** 88 | * @type {CookieProperties} 89 | */ 90 | props: { 91 | /** 92 | * The maximum lifetime of the cookie as an HTTP-date timestamp. 93 | */ 94 | Expires?: string; 95 | /** 96 | * Number of seconds until the cookie expires. A zero or negative number will expire the cookie immediately. 97 | */ 98 | "Max-Age"?: string; 99 | /** 100 | * Host to which the cookie will be sent. 101 | */ 102 | Domain?: string; 103 | /** 104 | * A path that must exist in the requested URL, or the browser won't send the `Cookie` header. 105 | */ 106 | Path?: string; 107 | /** 108 | * Controls whether a cookie is sent with cross-origin requests, providing some protection against cross-site request forgery attacks (CSRF). 109 | */ 110 | SameSite?: string; 111 | }; 112 | /** 113 | * @type {Array<"Secure" | "HttpOnly">} 114 | */ 115 | flags: Array<"Secure" | "HttpOnly">; 116 | /** 117 | * Convert cookie to cookie string 118 | * @override 119 | * @param {boolean} [full=true] Include cookie properties and flags 120 | * @return {string} Cookie String 121 | */ 122 | override toString(full?: boolean): string; 123 | } 124 | import http = require("http"); 125 | -------------------------------------------------------------------------------- /typings/lib/RawData.d.ts: -------------------------------------------------------------------------------- 1 | export = RawData; 2 | /** 3 | * Holds raw data received from server 4 | * @typedef {Object} RawDataObject 5 | */ 6 | declare class RawData { 7 | /** 8 | * Creates an instance of RawData. 9 | * @param {RawDataObject} [_data=null] 10 | * @memberof RawData 11 | */ 12 | constructor(_data?: RawDataObject); 13 | /** 14 | * Holds raw data received from server 15 | * @type {RawDataObject} 16 | */ 17 | _data: RawDataObject; 18 | } 19 | declare namespace RawData { 20 | export { RawDataObject }; 21 | } 22 | /** 23 | * Holds raw data received from server 24 | */ 25 | type RawDataObject = { 26 | [x: string]: any; 27 | }; 28 | -------------------------------------------------------------------------------- /typings/lib/ResponseTypings.d.ts: -------------------------------------------------------------------------------- 1 | declare const _exports: any; 2 | export = _exports; 3 | -------------------------------------------------------------------------------- /typings/lib/utils.d.ts: -------------------------------------------------------------------------------- 1 | export function btoa(data: any): string; 2 | export function atob(data: any): string; 3 | /** 4 | * Create iterable key-value pairs. 5 | * @param {any} iterable Iterable Object, Array or any other value. 6 | * @returns {([index: number, key: string, value: any] | [index: number, value: any])[]} Iterator 7 | * @example 8 | * iterate([4, 5, 6]); //[[0, 4], [1, 5], [2, 6]] 9 | * iterate([]); //[] 10 | * iterate({a: 4, b: 5, c: 6}); //[[0, "a", 4], [1, "b", 5], [2, "c", 6]] 11 | * iterate({}); //[] 12 | * iterate("bar"); //[[0, "b"], [1, "a"], [2, "r"]] 13 | * iterate(11); //[[0, 11]] 14 | * iterate(true); //[[0, true]] 15 | * iterate(false); //[[0, false]] 16 | * iterate(null); //[] 17 | * iterate(undefined); //[] 18 | */ 19 | export function iterate(iterable: any): ([index: number, key: string, value: any] | [index: number, value: any])[]; 20 | -------------------------------------------------------------------------------- /typings/src/ASC.d.ts: -------------------------------------------------------------------------------- 1 | export = ASC; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class ASC extends RawData { 6 | /** 7 | * Parses the HTML page and returns raw ASC data. 8 | * @static 9 | * @param {string} html HTML page to parse. 10 | * @return {RawDataObject} Parsed data. 11 | * @memberof ASC 12 | */ 13 | static parse(html: string): RawDataObject; 14 | /** 15 | * Creates an instance of ASC. 16 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 17 | * @param {Edupage} [edupage=null] Edupage instance to use. 18 | * @memberof ASC 19 | */ 20 | constructor(data?: RawDataObject, edupage?: Edupage); 21 | /** 22 | * Edupage instance associated to this object. 23 | * @type {Edupage | string} 24 | */ 25 | edupage: Edupage | string; 26 | /** 27 | * Userstring of the currently logged in user. 28 | * @type {string} 29 | */ 30 | loggedUser: string; 31 | /** 32 | * Rights of the currently logged in user. 33 | * @type {any[]} 34 | */ 35 | loggedUserRights: any[]; 36 | /** 37 | * Timezone for the school. 38 | * @type {string} 39 | */ 40 | timezone: string; 41 | /** 42 | * Weekend days for the school. 43 | * @type {number[]} 44 | */ 45 | weekendDays: number[]; 46 | /** 47 | * Edupage server. 48 | * @type {string} 49 | * @example "edupage61" 50 | */ 51 | server: string; 52 | /** 53 | * Full name of the school. 54 | * @type {string} 55 | */ 56 | schoolName: string; 57 | /** 58 | * Language code of the currently logged in user. 59 | * @type {string} 60 | */ 61 | lang: string; 62 | /** 63 | * Country code of the school. 64 | * @type {string} 65 | */ 66 | schoolCountry: string; 67 | /** 68 | * Turnover of the school in format "MM-DD" 69 | * @type {string} 70 | * @example "08-01" 71 | */ 72 | schoolyearTurnover: string; 73 | /** 74 | * Secure hash used for API calls 75 | * @type {string} 76 | * @example "94c3f4d3" 77 | */ 78 | gsecHash: string; 79 | /** 80 | * Id used for API calls 81 | * @type {string} 82 | */ 83 | gpid: string; 84 | /** 85 | * List of currently available gpid ids. 86 | * @type {string[]} 87 | */ 88 | gpids: string[]; 89 | /** 90 | * First day of the week (0 = Sunday, 1 = Monday, ...). 91 | * @type {number} 92 | */ 93 | firstDayOfWeek: number; 94 | } 95 | declare namespace ASC { 96 | export { RawDataObject }; 97 | } 98 | import RawData = require("../lib/RawData"); 99 | import Edupage = require("./Edupage"); 100 | type RawDataObject = import("../lib/RawData").RawDataObject; 101 | -------------------------------------------------------------------------------- /typings/src/Application.d.ts: -------------------------------------------------------------------------------- 1 | export = Application; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | /** 6 | * @typedef {import("./enums").EntityType} EntityType 7 | */ 8 | /** 9 | * @experimental 10 | * @class Application 11 | * @extends {RawData} 12 | */ 13 | declare class Application extends RawData { 14 | /** 15 | * Creates an instance of Application. 16 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 17 | * @param {Edupage} [edupage=null] Edupage instance to use. 18 | * @memberof Application 19 | */ 20 | constructor(data?: RawDataObject, edupage?: Edupage); 21 | /** 22 | * Edupage instance associated to this object. 23 | * @type {Edupage} 24 | */ 25 | edupage: Edupage; 26 | /** 27 | * Id of the application. 28 | * @type {string} 29 | */ 30 | id: string; 31 | /** 32 | * Date from which the application is valid. 33 | * @type {Date} 34 | */ 35 | dateFrom: Date; 36 | /** 37 | * Date until which the application is valid. 38 | * @type {Date} 39 | */ 40 | dateTo: Date; 41 | /** 42 | * Name of the application. 43 | * @type {string} 44 | */ 45 | name: string; 46 | /** 47 | * Array of parameter names to be provided to `Application.post(...)` call (e.g. date, price...) 48 | * @type {string[]} 49 | */ 50 | parameters: string[]; 51 | /** 52 | * Type of entities this application is available for. 53 | * @type {EntityType} 54 | */ 55 | availableFor: EntityType; 56 | /** 57 | * Flag indicating whether the application is enabled. 58 | * @type {boolean} 59 | */ 60 | isEnabled: boolean; 61 | /** 62 | * Unknown property 63 | * @type {boolean} 64 | */ 65 | isTextOptional: boolean; 66 | /** 67 | * Unknown property 68 | * @type {boolean} 69 | */ 70 | isAdvancedWorkflow: boolean; 71 | /** 72 | * Unknown property 73 | * @type {boolean} 74 | */ 75 | isSimpleWorkflow: boolean; 76 | /** 77 | * Creates Application draft 78 | * @return {Promise} Created draft id 79 | * @memberof Application 80 | */ 81 | createDraft(): Promise; 82 | /** 83 | * Posts the application with input parameters 84 | * @experimental 85 | * @param {RawDataObject} [parameters={}] Object of parameters from `Application.parameters` 86 | * @param {string} [draftId=null] Your custom created draft ID. If the paramater is not specified (or provided value is falsy), new draft is created internally. 87 | * @return {Promise} `true` if the method was successful, otherwise `false` 88 | * @memberof Application 89 | */ 90 | post(parameters?: RawDataObject, draftId?: string): Promise; 91 | /** 92 | * Initializes instance. 93 | * @param {Edupage} [edupage=null] Edupage instance to use. 94 | * @memberof Application 95 | */ 96 | init(edupage?: Edupage): void; 97 | } 98 | declare namespace Application { 99 | export { RawDataObject, EntityType }; 100 | } 101 | import RawData = require("../lib/RawData"); 102 | import Edupage = require("./Edupage"); 103 | type EntityType = import("./enums").EntityType; 104 | type RawDataObject = import("../lib/RawData").RawDataObject; 105 | -------------------------------------------------------------------------------- /typings/src/Assignment.d.ts: -------------------------------------------------------------------------------- 1 | export = Assignment; 2 | /** 3 | * @typedef {import("./Homework")} Homework 4 | */ 5 | /** 6 | * @typedef {import("./Test")} Test 7 | */ 8 | /** 9 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 10 | */ 11 | declare class Assignment extends RawData { 12 | /** 13 | * Creates an instance of Homework or Test from homework data. 14 | * @static 15 | * @param {RawDataObject} [data={}] 16 | * @param {Edupage} [edupage=null] 17 | * @return {Assignment | Homework | Test} 18 | * @memberof Assignment 19 | */ 20 | static from(data?: RawDataObject, edupage?: Edupage): Assignment | Homework | Test; 21 | /** 22 | * Creates an instance of Assignment. 23 | * @param {RawDataObject} [data={}] Initializes instance with raw data. 24 | * @param {Edupage} [edupage=null] Edupage instance to use. 25 | * @memberof Assignment 26 | */ 27 | constructor(data?: RawDataObject, edupage?: Edupage); 28 | /** 29 | * Edupage instance associated to this object. 30 | * @type {Edupage} 31 | */ 32 | edupage: Edupage; 33 | /** 34 | * Homework id of this assignment. 35 | * @type {string} 36 | * @example "superid:16647" 37 | */ 38 | id: string; 39 | /** 40 | * Super assignment id. 41 | * @type {string} 42 | */ 43 | superId: string; 44 | /** 45 | * Author of the assignment. 46 | * @type {User | Teacher} 47 | */ 48 | owner: User | Teacher; 49 | /** 50 | * Subject associated to this assignment. 51 | * @type {Subject} 52 | */ 53 | subject: Subject; 54 | /** 55 | * Title of the assignment. 56 | * @type {string} 57 | */ 58 | title: string; 59 | /** 60 | * Description of the assignment. 61 | * @type {string} 62 | */ 63 | details: string; 64 | /** 65 | * Date when the assignment was created. 66 | * @type {Date} 67 | */ 68 | creationDate: Date; 69 | /** 70 | * Date from which the assignment is available for the students. 71 | * @type {Date} 72 | */ 73 | fromDate: Date; 74 | /** 75 | * Date by which the assignment is available to students. 76 | * @type {Date} 77 | */ 78 | toDate: Date; 79 | /** 80 | * Time to complete the assignment in seconds 81 | * @type {number} 82 | */ 83 | duration: number; 84 | /** 85 | * Period when the assignment is available. 86 | * @type {Period} 87 | */ 88 | period: Period; 89 | /** 90 | * Id of the test. 91 | * @type {string} 92 | */ 93 | testId: string; 94 | /** 95 | * Type of the assignment. 96 | * @type {ASSIGNMENT_TYPE} 97 | */ 98 | type: ASSIGNMENT_TYPE; 99 | /** 100 | * Homework id 2. 101 | * @type {string} 102 | * @example "subid:4B1340557B68DE71" 103 | */ 104 | hwkid: string; 105 | /** 106 | * Number of cards in the material. 107 | * @type {number} 108 | */ 109 | cardsCount: number; 110 | /** 111 | * Number of answer cards in the material. 112 | * @type {number} 113 | */ 114 | answerCardsCount: number; 115 | /** 116 | * The evaluation state of the assignment. 117 | * @type {string} 118 | */ 119 | state: string; 120 | /** 121 | * Flag indicating if the assignment is new. 122 | * @type {boolean} 123 | */ 124 | isSeen: boolean; 125 | /** 126 | * Comment of the assignment. 127 | * @type {string} 128 | */ 129 | comment: string; 130 | /** 131 | * Result of the assignment. 132 | * @type {string} 133 | */ 134 | result: string; 135 | /** 136 | * Flag indicating if the assignment is finished. 137 | * @type {boolean} 138 | */ 139 | isFinished: boolean; 140 | /** 141 | * Date when the assignment was last updated. 142 | * @type {Date} 143 | */ 144 | stateUpdatedDate: Date; 145 | /** 146 | * User who last updated the assignment. 147 | * @type {User | Teacher} 148 | */ 149 | stateUpdatedBy: User | Teacher; 150 | /** 151 | * List of grades associated to this assignment. 152 | * @type {Grade[]} 153 | */ 154 | grades: Grade[]; 155 | /** 156 | * Initializes the object. 157 | * @param {Edupage} [edupage=null] Edupage instance to use. 158 | * @memberof Assignment 159 | */ 160 | init(edupage?: Edupage): void; 161 | /** 162 | * Fetches results + material data for assignment 163 | * Usually the structure is: `{resultsData: {...}, materialData: {...}, ...}` 164 | * Fetched data are cached into `this._data._resultsData` 165 | * @return {Promise} Non-parsed raw data object 166 | * @memberof Assignment 167 | */ 168 | getData(): Promise; 169 | } 170 | declare namespace Assignment { 171 | export { Homework, Test, RawDataObject }; 172 | } 173 | import RawData = require("../lib/RawData"); 174 | import Edupage = require("./Edupage"); 175 | import User = require("./User"); 176 | import Teacher = require("./Teacher"); 177 | import Subject = require("./Subject"); 178 | import Period = require("./Period"); 179 | import { ASSIGNMENT_TYPE } from "./enums"; 180 | import Grade = require("./Grade"); 181 | type RawDataObject = import("../lib/RawData").RawDataObject; 182 | type Homework = import("./Homework"); 183 | type Test = import("./Test"); 184 | -------------------------------------------------------------------------------- /typings/src/Attachment.d.ts: -------------------------------------------------------------------------------- 1 | export = Attachment; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Attachment extends RawData { 6 | /** 7 | * Creates an instance of Attachment. 8 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 9 | * @param {Edupage} [edupage=null] Edupage instance to use. 10 | * @memberof Attachment 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * Edupage instance associated to this object 15 | * @type {Edupage} 16 | */ 17 | edupage: Edupage; 18 | /** 19 | * Name of the attachment file 20 | * @type {string} 21 | */ 22 | name: string; 23 | /** 24 | * Absolute URL path to the attachment file uploaded on Edupage cloud server 25 | * @type {string} 26 | */ 27 | src: string; 28 | /** 29 | * Initializes instance. 30 | * @param {Edupage} [edupage=null] 31 | * @memberof Attachment 32 | */ 33 | init(edupage?: Edupage): void; 34 | /** 35 | * Converts the `Attachment` object to JSON object 36 | * @return {Object} JSON object contianing the attachment name as key and the attachment URL as value 37 | * @memberof Attachment 38 | */ 39 | toJSON(): { 40 | [x: string]: string; 41 | }; 42 | } 43 | declare namespace Attachment { 44 | export { formBoundary, RawDataObject }; 45 | } 46 | import RawData = require("../lib/RawData"); 47 | import Edupage = require("./Edupage"); 48 | type RawDataObject = import("../lib/RawData").RawDataObject; 49 | declare var formBoundary: string; 50 | -------------------------------------------------------------------------------- /typings/src/Class.d.ts: -------------------------------------------------------------------------------- 1 | export = Class; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Class extends RawData { 6 | /** 7 | * Creates an instance of Class. 8 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 9 | * @param {Edupage} [edupage=null] Edupage instance to use. 10 | * @memberof Class 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * Edupage instance associated to this object 15 | * @type {Edupage} 16 | */ 17 | edupage: Edupage; 18 | /** 19 | * Grade of the class 20 | * @type {number} 21 | */ 22 | grade: number; 23 | /** 24 | * ID of the class 25 | * @type {string} 26 | */ 27 | id: string; 28 | /** 29 | * Name of the class 30 | * @type {string} 31 | */ 32 | name: string; 33 | /** 34 | * Short name of the class 35 | * @type {string} 36 | */ 37 | short: string; 38 | /** 39 | * Classroom associated to this class 40 | * @type {Classroom} 41 | */ 42 | classroom: Classroom; 43 | /** 44 | * Teacher associated to this class 45 | * @type {Teacher} 46 | */ 47 | teacher: Teacher; 48 | /** 49 | * Teacher 2 associated to this class 50 | * @type {Teacher} 51 | */ 52 | teacher2: Teacher; 53 | /** 54 | * Initializes instance. 55 | * @param {Edupage} [edupage=null] Edupage instance to use. 56 | * @memberof Class 57 | */ 58 | init(edupage?: Edupage): void; 59 | } 60 | declare namespace Class { 61 | export { RawDataObject }; 62 | } 63 | import RawData = require("../lib/RawData"); 64 | import Edupage = require("./Edupage"); 65 | import Classroom = require("./Classroom"); 66 | import Teacher = require("./Teacher"); 67 | type RawDataObject = import("../lib/RawData").RawDataObject; 68 | -------------------------------------------------------------------------------- /typings/src/Classroom.d.ts: -------------------------------------------------------------------------------- 1 | export = Classroom; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Classroom extends RawData { 6 | /** 7 | * Creates an instance of Classroom. 8 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 9 | * @param {Edupage} [edupage=null] Edupage instance to use. 10 | * @memberof Classroom 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * Edupage instance associated to this object. 15 | * @type {Edupage} 16 | */ 17 | edupage: Edupage; 18 | /** 19 | * Unknown property 20 | * @type {boolean} 21 | */ 22 | cb_hidden: boolean; 23 | /** 24 | * Classroom id 25 | * @type {string} 26 | */ 27 | id: string; 28 | /** 29 | * Classroom name 30 | * @type {string} 31 | */ 32 | name: string; 33 | /** 34 | * Classroom short name 35 | * @type {string} 36 | */ 37 | short: string; 38 | /** 39 | * Initializes instance. 40 | * @param {Edupage} [edupage=null] Edupage instance to use. 41 | * @memberof Classroom 42 | */ 43 | init(edupage?: Edupage): void; 44 | } 45 | declare namespace Classroom { 46 | export { RawDataObject }; 47 | } 48 | import RawData = require("../lib/RawData"); 49 | import Edupage = require("./Edupage"); 50 | type RawDataObject = import("../lib/RawData").RawDataObject; 51 | -------------------------------------------------------------------------------- /typings/src/Edupage.d.ts: -------------------------------------------------------------------------------- 1 | export = Edupage; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | /** 6 | * @typedef {import("./enums").APIEndpoint} APIEndpoint 7 | */ 8 | declare class Edupage extends RawData { 9 | /** 10 | * 11 | * @param {Date | number | string} date1 12 | * @param {Date | number | string} date2 13 | * @return {boolean} true if day of the dates is same, otherwise false 14 | * @memberof Edupage 15 | */ 16 | static compareDay(date1: Date | number | string, date2: Date | number | string): boolean; 17 | /** 18 | * 19 | * @param {Date} date 20 | * @return {string} string representation of the date 21 | * @memberof Edupage 22 | */ 23 | static dateToString(date: Date): string; 24 | /** 25 | * Parses raw JSON data from html 26 | * @private 27 | * @param {string} html 28 | * @returns {RawDataObject} 29 | */ 30 | private static parse; 31 | /** 32 | * Creates an instance of Edupage. 33 | * @memberof Edupage 34 | */ 35 | constructor(); 36 | /** 37 | * Instance of currently logged in user. 38 | * All the information in the edupage instance is related to this user. 39 | * This user is used to make all requests to the Edupage internal APIs. 40 | * @type {User | Teacher | Student} 41 | */ 42 | user: User | Teacher | Student; 43 | /** 44 | * List of all seasons or semesters. 45 | * @type {Season[]} 46 | */ 47 | seasons: Season[]; 48 | /** 49 | * List of all students in the school (if the user is a teacher, otherwise the list contains classmates only). 50 | * @type {Student[]} 51 | */ 52 | students: Student[]; 53 | /** 54 | * List of all teachers in the school. 55 | * @type {Teacher[]} 56 | */ 57 | teachers: Teacher[]; 58 | /** 59 | * List of all classes in the school. 60 | * @type {Class[]} 61 | */ 62 | classes: Class[]; 63 | /** 64 | * List of all classrooms in the school. 65 | * @type {Classroom[]} 66 | */ 67 | classrooms: Classroom[]; 68 | /** 69 | * List of all parents for owned class (if the user is a teacher, otherwise the list contains parents of the student). 70 | * @type {Parent[]} 71 | */ 72 | parents: Parent[]; 73 | /** 74 | * List of all subjects in the school. 75 | * @type {Subject[]} 76 | */ 77 | subjects: Subject[]; 78 | /** 79 | * List of all periods in the school. 80 | * @type {Period[]} 81 | */ 82 | periods: Period[]; 83 | /** 84 | * List of all timetables currently fetched. 85 | * There are always timetables for 2 - 4 days (current + next, if friday + weekend). 86 | * This list is used as a cache to avoid fetching the same timetable multiple times. 87 | * @type {Timetable[]} 88 | */ 89 | timetables: Timetable[]; 90 | /** 91 | * List of all message items on the timeline for currently logged in user. 92 | * Contains visible messages as well as hidden confirmations and helper records 93 | * @type {Message[]} 94 | */ 95 | timelineItems: Message[]; 96 | /** 97 | * List of all visible timeline items on the timeline for currently logged in user. 98 | * @type {Message[]} 99 | */ 100 | timeline: Message[]; 101 | /** 102 | * List of all plans for currently logged in user. 103 | * @type {Plan[]} 104 | */ 105 | plans: Plan[]; 106 | /** 107 | * List of all assignments for currently logged in user. 108 | * @type {Assignment[]} 109 | */ 110 | assignments: Assignment[]; 111 | /** 112 | * List of all assignments type of homework for currently logged in user. 113 | * @type {Homework[]} 114 | */ 115 | homeworks: Homework[]; 116 | /** 117 | * List of all assignments type of test for currently logged in user. 118 | * @type {Test[]} 119 | */ 120 | tests: Test[]; 121 | /** 122 | * List of all applications in the school. 123 | * @experimental 124 | * @type {Application[]} 125 | */ 126 | applications: Application[]; 127 | /** 128 | * Instance of an ASC object. 129 | * Can be used to access some general school data. 130 | * @type {ASC} 131 | */ 132 | ASC: ASC; 133 | /** 134 | * Current school year. 135 | * If the current school year is "2020/2021", then this value is `2020`. 136 | * @type {number} 137 | */ 138 | year: number; 139 | /** 140 | * Base edupage URL. 141 | * @example "https://example.edupage.org" 142 | * @type {string} 143 | */ 144 | baseUrl: string; 145 | /** 146 | * @typedef {import("./User").LoginOptions} LoginOptions 147 | */ 148 | /** 149 | * Logs user in for this instance 150 | * 151 | * @param {string} [username=this.user.credentials.username] Username of the user 152 | * @param {string} [password=this.user.credentials.password] Password of the user 153 | * @param {LoginOptions} [options] Login options 154 | * @return {Promise} Returns a promise that resolves with the `User` object if successful. If the 2FA is requested by the Edupage, the promise will resolve with `null`. 155 | * @memberof Edupage 156 | */ 157 | login(username?: string, password?: string, options?: User.LoginOptions): Promise; 158 | /** 159 | * Refreshes all fields in `Edupage` instance 160 | * @memberof Edupage 161 | */ 162 | refresh(): Promise; 163 | /** 164 | * Fetches global Edupage data (such as teachers, classes, classrooms, subjects...) 165 | * and updates internal values. 166 | * This includes ASC refresh. 167 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 168 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 169 | * have to recalculate internal values every time and save some performance. 170 | * @memberof Edupage 171 | */ 172 | refreshEdupage(_update?: boolean): Promise; 173 | /** 174 | * Fetches timeline data (messages, notifications...) 175 | * and updates internal values. 176 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 177 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 178 | * have to recalculate internal values every time and save some performance. 179 | * @memberof Edupage 180 | */ 181 | refreshTimeline(_update?: boolean): Promise; 182 | /** 183 | * Fetches timeline items data created by currently 184 | * logged user and updates internal values. 185 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 186 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 187 | * have to recalculate internal values every time and save some performance. 188 | * @memberof Edupage 189 | */ 190 | refreshCreatedItems(_update?: boolean): Promise; 191 | /** 192 | * Fetches grades of currently logged 193 | * user and updates internal values. 194 | * @param {boolean} [_update=true] Tells whether to update internal values or not. 195 | * Can be tweaked if are calling multiple "refresh" methods at once, so you don't 196 | * have to recalculate internal values every time and save some performance. 197 | * @memberof Edupage 198 | */ 199 | refreshGrades(_update?: boolean): Promise; 200 | /** 201 | * Internal method to update all the fields in Edupage instance. Should be called 202 | * after any of "refresh" methods (in case `_update` argument is set to `false`) 203 | * @memberof Edupage 204 | */ 205 | _updateInternalValues(): void; 206 | grades: Grade[]; 207 | /** 208 | * 209 | * @param {string} id 210 | * @return {User | Teacher | Student | Parent | undefined} 211 | * @memberof Edupage 212 | */ 213 | getUserById(id: string): User | Teacher | Student | Parent | undefined; 214 | /** 215 | * 216 | * @param {string} userString 217 | * @return {string} 218 | * @memberof Edupage 219 | */ 220 | getUserIdByUserString(userString: string): string; 221 | /** 222 | * 223 | * @param {string} userString 224 | * @return {User | Teacher | Student | Parent | undefined} 225 | * @memberof Edupage 226 | */ 227 | getUserByUserString(userString: string): User | Teacher | Student | Parent | undefined; 228 | /** 229 | * 230 | * @param {boolean} time 231 | * @return {string} 232 | * @memberof Edupage 233 | */ 234 | getYearStart(time?: boolean): string; 235 | /** 236 | * 237 | * @param {Date} date 238 | * @return {Promise} 239 | * @memberof Edupage 240 | */ 241 | getTimetableForDate(date: Date): Promise; 242 | /** 243 | * @param {Date} fromDate 244 | * @param {Date} toDate 245 | * @return {Promise} 246 | * @memberof Edupage 247 | */ 248 | fetchTimetablesForDates(fromDate: Date, toDate: Date): Promise; 249 | /** 250 | * 251 | * @param {string} filepath 252 | * @returns {Promise} 253 | */ 254 | uploadAttachment(filepath: string): Promise; 255 | /** 256 | * @typedef {Object} APIOptions 257 | * @prop {string | ENDPOINT} url 258 | * @prop {Object | stream.Readable | Buffer | string} [data={}] 259 | * @prop {Object} [headers={}] 260 | * @prop {string} [method="POST"] 261 | * @prop {boolean} [encodeBody=true] 262 | * @prop {"json" | "text"} [type="json"] 263 | * @prop {boolean} [autoLogin=true] 264 | */ 265 | /** 266 | * 267 | * @static 268 | * @param {APIOptions} options 269 | * @param {number} [_count=0] 270 | * @return {Promise} Resolves: Response body, Rejects: Error or retry object in case of successful invalid gsecHash error resolution 271 | * @memberof Edupage 272 | */ 273 | api(options: { 274 | url: string | ENDPOINT; 275 | data?: { 276 | [x: string]: any; 277 | } | stream.Readable | Buffer | string; 278 | headers?: { 279 | [x: string]: any; 280 | }; 281 | method?: string; 282 | encodeBody?: boolean; 283 | type?: "json" | "text"; 284 | autoLogin?: boolean; 285 | }, _count?: number): Promise; 289 | /** 290 | * Sends a session ping request 291 | * @returns {Promise} Whether the ping was successful 292 | */ 293 | pingSession(): Promise; 294 | /** 295 | * Schedules a session ping request 296 | */ 297 | scheduleSessionPing(): void; 298 | _sessionPingTimeout: NodeJS.Timeout; 299 | /** 300 | * Stops internal timers to prevent process from hanging infinitely. 301 | */ 302 | exit(): void; 303 | /** 304 | * Converts Object to form body 305 | * @private 306 | * @param {Object} data 307 | * @return {string} Form body 308 | */ 309 | private encodeRequestBody; 310 | /** 311 | * Returns endpoint URL 312 | * @private 313 | * @param {APIEndpoint} endpoint 314 | * @return {string} Endpoint URL 315 | */ 316 | private buildRequestUrl; 317 | } 318 | declare namespace Edupage { 319 | export { RawDataObject, APIEndpoint }; 320 | } 321 | import RawData = require("../lib/RawData"); 322 | import User = require("./User"); 323 | import Teacher = require("./Teacher"); 324 | import Student = require("./Student"); 325 | import Season = require("./Season"); 326 | import Class = require("./Class"); 327 | import Classroom = require("./Classroom"); 328 | import Parent = require("./Parent"); 329 | import Subject = require("./Subject"); 330 | import Period = require("./Period"); 331 | import Timetable = require("./Timetable"); 332 | import Message = require("./Message"); 333 | import Plan = require("./Plan"); 334 | import Assignment = require("./Assignment"); 335 | import Homework = require("./Homework"); 336 | import Test = require("./Test"); 337 | import Application = require("./Application"); 338 | import ASC = require("./ASC"); 339 | import Grade = require("./Grade"); 340 | import Attachment = require("./Attachment"); 341 | import { ENDPOINT } from "./enums"; 342 | import stream = require("stream"); 343 | type RawDataObject = import("../lib/RawData").RawDataObject; 344 | type APIEndpoint = import("./enums").APIEndpoint; 345 | -------------------------------------------------------------------------------- /typings/src/Grade.d.ts: -------------------------------------------------------------------------------- 1 | export = Grade; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Grade extends RawData { 6 | /** 7 | * 8 | * @static 9 | * @param {string} html 10 | * @return {{settings: RawDataObject, data: RawDataObject}} 11 | * @memberof Grade 12 | */ 13 | static parse(html: string): { 14 | settings: RawDataObject; 15 | data: RawDataObject; 16 | }; 17 | /** 18 | * Creates an instance of Grade. 19 | * @param {RawDataObject} [data={}] 20 | * @param {Edupage} [edupage=null] 21 | * @memberof Grade 22 | */ 23 | constructor(data?: RawDataObject, edupage?: Edupage); 24 | /** 25 | * @type {Edupage} 26 | */ 27 | edupage: Edupage; 28 | /** 29 | * @type {string} 30 | */ 31 | value: string; 32 | /** 33 | * @type {Date} 34 | */ 35 | creationDate: Date; 36 | /** 37 | * @type {Season} 38 | */ 39 | season: Season; 40 | /** 41 | * @type {Date} 42 | */ 43 | signedDate: Date; 44 | /** 45 | * @type {Date} 46 | */ 47 | signedByParentDate: Date; 48 | /** 49 | * @type {boolean} 50 | */ 51 | isSigned: boolean; 52 | /** 53 | * @type {Subject} 54 | */ 55 | subject: Subject; 56 | /** 57 | * @type {string} 58 | */ 59 | state: string; 60 | /** 61 | * @type {Student} 62 | */ 63 | student: Student; 64 | /** 65 | * @type {Teacher} 66 | */ 67 | teacher: Teacher; 68 | /** 69 | * @type {string} 70 | */ 71 | id: string; 72 | /** 73 | * @type {string} 74 | */ 75 | eventId: string; 76 | /** 77 | * @type {Class} 78 | */ 79 | class: Class; 80 | /** 81 | * @type {Class[]} 82 | */ 83 | classes: Class[]; 84 | /** 85 | * @type {string} 86 | */ 87 | title: string; 88 | /** 89 | * @type {string} 90 | */ 91 | short: string; 92 | /** 93 | * @type {string} 94 | * @example "31.3.2021" 95 | */ 96 | date: string; 97 | /** 98 | * @type {string} 99 | */ 100 | type: string; 101 | /** 102 | * @type {number} 103 | */ 104 | weight: number; 105 | /** 106 | * @type {number} 107 | */ 108 | maxPoints: number; 109 | /** 110 | * @type {number} 111 | */ 112 | points: number; 113 | /** 114 | * @type {number} 115 | */ 116 | percentage: number; 117 | /** 118 | * @type {Plan} 119 | */ 120 | plan: Plan; 121 | /** 122 | * @type {string} 123 | */ 124 | average: string; 125 | /** 126 | * @type {string} 127 | */ 128 | provider: string; 129 | /** 130 | * @type {string} 131 | */ 132 | superId: string; 133 | /** 134 | * @type {boolean} 135 | */ 136 | isClassified: boolean; 137 | /** 138 | * @type {Assignment} 139 | */ 140 | assignment: Assignment; 141 | /** 142 | * 143 | * @param {Edupage} [edupage=null] 144 | * @returns {any} 145 | * @memberof Grade 146 | */ 147 | init(edupage?: Edupage): any; 148 | } 149 | declare namespace Grade { 150 | export { RawDataObject }; 151 | } 152 | import RawData = require("../lib/RawData"); 153 | import Edupage = require("./Edupage"); 154 | import Season = require("./Season"); 155 | import Subject = require("./Subject"); 156 | import Student = require("./Student"); 157 | import Teacher = require("./Teacher"); 158 | import Class = require("./Class"); 159 | import Plan = require("./Plan"); 160 | import Assignment = require("./Assignment"); 161 | type RawDataObject = import("../lib/RawData").RawDataObject; 162 | -------------------------------------------------------------------------------- /typings/src/Homework.d.ts: -------------------------------------------------------------------------------- 1 | export = Homework; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Homework extends Assignment { 6 | } 7 | declare namespace Homework { 8 | export { RawDataObject }; 9 | } 10 | import Assignment = require("./Assignment"); 11 | type RawDataObject = import("../lib/RawData").RawDataObject; 12 | -------------------------------------------------------------------------------- /typings/src/Lesson.d.ts: -------------------------------------------------------------------------------- 1 | export = Lesson; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Lesson extends RawData { 6 | /** 7 | * Creates an instance of Lesson. 8 | * @param {RawDataObject} [data={}] 9 | * @param {Edupage} [edupage=null] 10 | * @memberof Lesson 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * Edupage instance associated to this object 15 | * @type {Edupage} 16 | */ 17 | edupage: Edupage; 18 | /** 19 | * @type {string} 20 | */ 21 | id: string; 22 | /** 23 | * @type {string} 24 | */ 25 | lid: string; 26 | /** 27 | * @type {Date} 28 | */ 29 | date: Date; 30 | /** 31 | * @type {string} 32 | */ 33 | homeworkNote: string; 34 | /** 35 | * @type {string} 36 | */ 37 | absentNote: string; 38 | /** 39 | * @type {string} 40 | */ 41 | curriculum: string; 42 | /** 43 | * @type {string} 44 | */ 45 | onlineLessonURL: string; 46 | /** 47 | * @type {boolean} 48 | */ 49 | isOnlineLesson: boolean; 50 | /** 51 | * @type {Period} 52 | */ 53 | period: Period; 54 | /** 55 | * @type {Subject} 56 | */ 57 | subject: Subject; 58 | /** 59 | * @type {Class[]} 60 | */ 61 | classes: Class[]; 62 | /** 63 | * @type {Classroom[]} 64 | */ 65 | classrooms: Classroom[]; 66 | /** 67 | * @type {Student[]} 68 | */ 69 | students: Student[]; 70 | /** 71 | * @type {Teacher[]} 72 | */ 73 | teachers: Teacher[]; 74 | /** 75 | * @type {Assignment[]} 76 | */ 77 | assignments: Assignment[]; 78 | /** 79 | * 80 | * @param {Edupage} [edupage=null] 81 | * @memberof Lesson 82 | * @returns {void} 83 | */ 84 | init(edupage?: Edupage): void; 85 | /** 86 | * 87 | * @return {Promise} 88 | * @memberof Lesson 89 | */ 90 | signIntoLesson(): Promise; 91 | } 92 | declare namespace Lesson { 93 | export { RawDataObject }; 94 | } 95 | import RawData = require("../lib/RawData"); 96 | import Edupage = require("./Edupage"); 97 | import Period = require("./Period"); 98 | import Subject = require("./Subject"); 99 | import Class = require("./Class"); 100 | import Classroom = require("./Classroom"); 101 | import Student = require("./Student"); 102 | import Teacher = require("./Teacher"); 103 | import Assignment = require("./Assignment"); 104 | type RawDataObject = import("../lib/RawData").RawDataObject; 105 | -------------------------------------------------------------------------------- /typings/src/Message.d.ts: -------------------------------------------------------------------------------- 1 | export = Message; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Message extends RawData { 6 | /** 7 | * Parses name of the user 8 | * @static 9 | * @param {string} name Name to parse 10 | * @return {{firstname: string, lastname: string}} Parsed name 11 | * @memberof Message 12 | */ 13 | static parseUsername(name: string): { 14 | firstname: string; 15 | lastname: string; 16 | }; 17 | /** 18 | * Creates an instance of Message. 19 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 20 | * @param {Edupage} [edupage=null] Edupage instance to use. 21 | * @memberof Message 22 | */ 23 | constructor(data?: RawDataObject, edupage?: Edupage); 24 | /** 25 | * Edupage instance associated to this object. 26 | * @type {Edupage} 27 | */ 28 | edupage: Edupage; 29 | /** 30 | * Timeline item id 31 | * @type {string} 32 | */ 33 | id: string; 34 | /** 35 | * Timeline item type 36 | * @type {string} 37 | */ 38 | type: string; 39 | /** 40 | * Date when the message was created 41 | * @type {Date} 42 | */ 43 | creationDate: Date; 44 | /** 45 | * Date when the message was last updated 46 | * @type {Date} 47 | */ 48 | timelineDate: Date; 49 | /** 50 | * ID of the user who created the message 51 | * @type {string} 52 | */ 53 | otherId: string; 54 | /** 55 | * Number of replies to the message 56 | * @type {number} 57 | */ 58 | repliesCount: number; 59 | /** 60 | * Date of the last reply to the message 61 | * @type {Date} 62 | */ 63 | lastReplyDate: Date; 64 | /** 65 | * Flag indicating whether the message was removed 66 | * @type {boolean} 67 | */ 68 | isRemoved: boolean; 69 | /** 70 | * Flag indicating whether the message is reply 71 | * @type {boolean} 72 | */ 73 | isReply: boolean; 74 | /** 75 | * Flag indicating whether the message is sent as important 76 | * @type {boolean} 77 | */ 78 | isImportant: boolean; 79 | /** 80 | * Date when the currently logged in user read the message. If the message is not important, this is same as `Message.creationDate`. 81 | * @type {Date} 82 | */ 83 | seenDate: Date; 84 | /** 85 | * Flag indicating whether the message was seen. If the message is not important, this will be always `true`. 86 | * @type {boolean} 87 | */ 88 | isSeen: boolean; 89 | /** 90 | * Date when the currently logged in user liked the message. 91 | * @type {Date} 92 | */ 93 | likedDate: Date; 94 | /** 95 | * Flag indicating whether the message was liked. 96 | * @type {boolean} 97 | */ 98 | isLiked: boolean; 99 | /** 100 | * Date when the currently logged in user marked this message as done. 101 | * @type {Date} 102 | */ 103 | doneDate: Date; 104 | /** 105 | * Flag indicating whether the message was marked as done. 106 | * @type {boolean} 107 | */ 108 | isDone: boolean; 109 | /** 110 | * Flag indicating whether the message was starred. 111 | * @type {boolean} 112 | */ 113 | isStarred: boolean; 114 | /** 115 | * Number of likes the message has. 116 | * @type {number} 117 | */ 118 | likes: number; 119 | /** 120 | * The textual content of the message. 121 | * @type {string} 122 | */ 123 | text: string; 124 | /** 125 | * Title of the message. 126 | * @type {string} 127 | */ 128 | title: string; 129 | /** 130 | * Number of participants in the message. 131 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 132 | * @type {number} 133 | */ 134 | participantsCount: number; 135 | /** 136 | * List of participants in the message. 137 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 138 | * @type {(User | Teacher | Student | Parent)[]} 139 | */ 140 | participants: (User | Teacher | Student | Parent)[]; 141 | /** 142 | * List of users who liked the message. 143 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 144 | * @type {({user: User | Teacher | Student | Parent, date: Date})[]} 145 | */ 146 | likedBy: { 147 | user: User | Teacher | Student | Parent; 148 | date: Date; 149 | }[]; 150 | /** 151 | * List of users who have seen the message. 152 | * ! WARNING: This property is only accessible after calling `message.refresh()`! 153 | * @type {({user: User | Teacher | Student | Parent, date: Date})[]} 154 | */ 155 | seenBy: { 156 | user: User | Teacher | Student | Parent; 157 | date: Date; 158 | }[]; 159 | /** 160 | * Author of the message. 161 | * @type {User | Teacher | Student | Parent} 162 | */ 163 | owner: User | Teacher | Student | Parent; 164 | /** 165 | * Recipient of the message. 166 | * @type {User | Teacher | Student | Parent | Plan | Class} 167 | */ 168 | recipient: User | Teacher | Student | Parent | Plan | Class; 169 | /** 170 | * Recipient of the message as user string. This can be used, when the exact recipient is not known (e.g. when the recipient is everyone, this will be '*'). 171 | * @type {string} 172 | */ 173 | recipientUserString: string; 174 | /** 175 | * Flag indicating whether the message has no exact recipient (e.g. userstring contains '*'). 176 | * @type {boolean} 177 | */ 178 | isWildcardRecipient: boolean; 179 | /** 180 | * Root message of the reply. 181 | * @type {Message} 182 | */ 183 | replyOf: Message; 184 | /** 185 | * Resources attached to the message. 186 | * @type {Attachment[]} 187 | */ 188 | attachments: Attachment[]; 189 | /** 190 | * List of replies to the message. 191 | * @type {Message[]} 192 | */ 193 | replies: Message[]; 194 | /** 195 | * Assignment attached to the message. 196 | * @type {Assignment} 197 | */ 198 | assignment: Assignment; 199 | /** 200 | * Initializes instance. 201 | * @param {Edupage} [edupage=null] Edupage instance to use. 202 | * @memberof Message 203 | */ 204 | init(edupage?: Edupage): void; 205 | /** 206 | * @typedef {Object} MessageReplyOptions 207 | * @prop {string} text 208 | * @prop {User | Teacher | Student | Parent} [recipient=null] 209 | * @prop {boolean} [parents=false] 210 | * @prop {Attachment[]} [attachments=[]] 211 | */ 212 | /** 213 | * Creates a new reply to the message 214 | * @param {MessageReplyOptions} options 215 | * @memberof Message 216 | */ 217 | reply(options: { 218 | text: string; 219 | recipient?: User | Teacher | Student | Parent; 220 | parents?: boolean; 221 | attachments?: Attachment[]; 222 | }): Promise; 223 | /** 224 | * Marks the message as liked 225 | * @param {boolean} [state=true] State to use (like/unlike) 226 | * @return {Promise} 227 | * @memberof Message 228 | */ 229 | markAsLiked(state?: boolean): Promise; 230 | /** 231 | * Marks the message as seen 232 | * @return {Promise} 233 | * @memberof Message 234 | */ 235 | markAsSeen(): Promise; 236 | /** 237 | * Marks the message as done 238 | * @param {boolean} [state=true] State to use (done/not done) 239 | * @return {Promise} 240 | * @memberof Message 241 | */ 242 | markAsDone(state?: boolean): Promise; 243 | /** 244 | * Marks the message as starred 245 | * @param {boolean} [state=true] State to use (starred/not starred) 246 | * @return {Promise} 247 | * @memberof Message 248 | */ 249 | markAsStarred(state?: boolean): Promise; 250 | /** 251 | * Refreshes message replies and some other fields 252 | * @param {RawDataObject} [data=null] Raw data to use instead of requesting from the server 253 | * @memberof Message 254 | */ 255 | refresh(data?: RawDataObject): Promise; 256 | /** 257 | * Searches for the user object from userstring. If user is not found, the parsed userstring is returned. 258 | * @private 259 | * @param {string} userString Userstring to search for 260 | * @return {{recipient: (User | Teacher | Student | Parent | Plan | Class), wildcard: boolean}} User object and wildcard flag 261 | * @memberof Message 262 | */ 263 | private getRecipient; 264 | } 265 | declare namespace Message { 266 | export { RawDataObject }; 267 | } 268 | import RawData = require("../lib/RawData"); 269 | import Edupage = require("./Edupage"); 270 | import User = require("./User"); 271 | import Teacher = require("./Teacher"); 272 | import Student = require("./Student"); 273 | import Parent = require("./Parent"); 274 | import Plan = require("./Plan"); 275 | import Class = require("./Class"); 276 | import Attachment = require("./Attachment"); 277 | import Assignment = require("./Assignment"); 278 | type RawDataObject = import("../lib/RawData").RawDataObject; 279 | -------------------------------------------------------------------------------- /typings/src/Parent.d.ts: -------------------------------------------------------------------------------- 1 | export = Parent; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Parent extends User { 6 | } 7 | declare namespace Parent { 8 | export { RawDataObject }; 9 | } 10 | import User = require("./User"); 11 | type RawDataObject = import("../lib/RawData").RawDataObject; 12 | -------------------------------------------------------------------------------- /typings/src/Period.d.ts: -------------------------------------------------------------------------------- 1 | export = Period; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Period extends RawData { 6 | /** 7 | * Creates new invalid Period 8 | * @static 9 | * @param {{id?: string?, name?: string, short?: string, startTime?: string, endTime?: string}} [data=null] Data to be used to create the Period (if there's any) 10 | * @return {Period} Created Period 11 | * @memberof Period 12 | */ 13 | static getInvalid(data?: { 14 | id?: string | null; 15 | name?: string; 16 | short?: string; 17 | startTime?: string; 18 | endTime?: string; 19 | }): Period; 20 | /** 21 | * Period id 22 | * @type {string} 23 | */ 24 | id: string; 25 | /** 26 | * Period name 27 | * @type {string} 28 | */ 29 | name: string; 30 | /** 31 | * Period short name 32 | * @type {string} 33 | */ 34 | short: string; 35 | /** 36 | * Period start time in format HH:MM 37 | * @type {string} 38 | */ 39 | startTime: string; 40 | /** 41 | * Period end time in format HH:MM 42 | * @type {string} 43 | */ 44 | endTime: string; 45 | } 46 | declare namespace Period { 47 | export { RawDataObject }; 48 | } 49 | import RawData = require("../lib/RawData"); 50 | type RawDataObject = import("../lib/RawData").RawDataObject; 51 | -------------------------------------------------------------------------------- /typings/src/Plan.d.ts: -------------------------------------------------------------------------------- 1 | export = Plan; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Plan extends RawData { 6 | /** 7 | * Creates an instance of Plan. 8 | * @param {RawDataObject} [data={}] 9 | * @param {Edupage} [edupage=null] 10 | * @memberof Plan 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * Edupage instance associated to this object 15 | * @type {Edupage} 16 | */ 17 | edupage: Edupage; 18 | /** 19 | * @type {string} 20 | */ 21 | id: string; 22 | /** 23 | * @type {string} 24 | */ 25 | subjectId: string; 26 | /** 27 | * @type {string} 28 | */ 29 | customClassId: string; 30 | /** 31 | * @type {string} 32 | */ 33 | customName: string; 34 | /** 35 | * @type {Date} 36 | */ 37 | changedDate: Date; 38 | /** 39 | * @type {number} 40 | */ 41 | year: number; 42 | /** 43 | * @type {Object} 44 | */ 45 | settings: { 46 | [x: string]: any; 47 | }; 48 | /** 49 | * @type {boolean} 50 | */ 51 | isPublic: boolean; 52 | /** 53 | * @type {string} 54 | * @example "o" 55 | */ 56 | state: string; 57 | /** 58 | * @type {boolean} 59 | */ 60 | isValid: boolean; 61 | /** 62 | * @type {Date} 63 | */ 64 | approvedDate: Date; 65 | /** 66 | * @type {boolean} 67 | */ 68 | isApproved: boolean; 69 | /** 70 | * @type {string} 71 | */ 72 | otherId: string; 73 | /** 74 | * @type {number} 75 | */ 76 | topicsCount: number; 77 | /** 78 | * @type {number} 79 | */ 80 | taughtCount: number; 81 | /** 82 | * @type {number} 83 | */ 84 | standardsCount: number; 85 | /** 86 | * @type {string} 87 | */ 88 | timetableGroup: string; 89 | /** 90 | * @type {Season} 91 | */ 92 | season: Season; 93 | /** 94 | * @type {string} 95 | */ 96 | name: string; 97 | /** 98 | * @type {number} 99 | */ 100 | classOrdering: number; 101 | /** 102 | * @type {boolean} 103 | */ 104 | isEntireClass: boolean; 105 | /** 106 | * @type {Subject} 107 | */ 108 | subject: Subject; 109 | /** 110 | * @type {Class[]} 111 | */ 112 | classes: Class[]; 113 | /** 114 | * @type {Teacher} 115 | */ 116 | teacher: Teacher; 117 | /** 118 | * @type {Teacher[]} 119 | */ 120 | teachers: Teacher[]; 121 | /** 122 | * @type {Student[]} 123 | */ 124 | students: Student[]; 125 | /** 126 | * 127 | * @param {Edupage} [edupage=null] 128 | * @memberof Plan 129 | */ 130 | init(edupage?: Edupage): void; 131 | } 132 | declare namespace Plan { 133 | export { RawDataObject }; 134 | } 135 | import RawData = require("../lib/RawData"); 136 | import Edupage = require("./Edupage"); 137 | import Season = require("./Season"); 138 | import Subject = require("./Subject"); 139 | import Class = require("./Class"); 140 | import Teacher = require("./Teacher"); 141 | import Student = require("./Student"); 142 | type RawDataObject = import("../lib/RawData").RawDataObject; 143 | -------------------------------------------------------------------------------- /typings/src/Season.d.ts: -------------------------------------------------------------------------------- 1 | export = Season; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Season extends RawData { 6 | /** 7 | * Creates an instance of Season. 8 | * @param {RawDataObject} [data={}] 9 | * @param {Edupage} [edupage=null] 10 | * @memberof Season 11 | */ 12 | constructor(data?: RawDataObject, edupage?: Edupage); 13 | /** 14 | * @type {Edupage} 15 | */ 16 | edupage: Edupage; 17 | /** 18 | * @type {string} 19 | */ 20 | id: string; 21 | /** 22 | * @type {Date} 23 | */ 24 | fromDate: Date; 25 | /** 26 | * @type {Date} 27 | */ 28 | toDate: Date; 29 | /** 30 | * @type {string} 31 | */ 32 | name: string; 33 | /** 34 | * @type {number} 35 | */ 36 | halfYear: number; 37 | /** 38 | * @type {number} 39 | */ 40 | index: number; 41 | /** 42 | * @type {Season} 43 | */ 44 | supSeason: Season; 45 | /** 46 | * @type {string[]} 47 | */ 48 | types: string[]; 49 | /** 50 | * @type {Season} 51 | */ 52 | classificationSeason: Season; 53 | /** 54 | * @type {boolean} 55 | */ 56 | isClassification: boolean; 57 | /** 58 | * 59 | * @param {Edupage} [edupage=null] 60 | * @memberof Message 61 | */ 62 | init(edupage?: Edupage): void; 63 | } 64 | declare namespace Season { 65 | export { RawDataObject }; 66 | } 67 | import RawData = require("../lib/RawData"); 68 | import Edupage = require("./Edupage"); 69 | type RawDataObject = import("../lib/RawData").RawDataObject; 70 | -------------------------------------------------------------------------------- /typings/src/Student.d.ts: -------------------------------------------------------------------------------- 1 | export = Student; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Student extends User { 6 | /** 7 | * @type {number} 8 | */ 9 | number: number; 10 | /** 11 | * @type {number} 12 | */ 13 | numberInClass: number; 14 | /** 15 | * @type {string} 16 | */ 17 | parent1Id: string; 18 | /** 19 | * @type {string} 20 | */ 21 | parent2Id: string; 22 | /** 23 | * @type {string} 24 | */ 25 | parent3Id: string; 26 | /** 27 | * @type {Parent} 28 | */ 29 | parent1: Parent; 30 | /** 31 | * @type {Parent} 32 | */ 33 | parent2: Parent; 34 | /** 35 | * @type {Parent} 36 | */ 37 | parent3: Parent; 38 | /** 39 | * @type {Class} 40 | */ 41 | class: Class; 42 | } 43 | declare namespace Student { 44 | export { RawDataObject }; 45 | } 46 | import User = require("./User"); 47 | import Parent = require("./Parent"); 48 | import Class = require("./Class"); 49 | type RawDataObject = import("../lib/RawData").RawDataObject; 50 | -------------------------------------------------------------------------------- /typings/src/Subject.d.ts: -------------------------------------------------------------------------------- 1 | export = Subject; 2 | declare class Subject extends RawData { 3 | constructor(data?: any); 4 | /** 5 | * @type {string} 6 | */ 7 | id: string; 8 | /** 9 | * @type {string} 10 | */ 11 | name: string; 12 | /** 13 | * @type {string} 14 | */ 15 | short: string; 16 | } 17 | import RawData = require("../lib/RawData"); 18 | -------------------------------------------------------------------------------- /typings/src/Teacher.d.ts: -------------------------------------------------------------------------------- 1 | export = Teacher; 2 | declare class Teacher extends User { 3 | /** 4 | * @type {number} 5 | */ 6 | cb_hidden: number; 7 | /** 8 | * @type {string} 9 | */ 10 | short: string; 11 | /** 12 | * @type {Classroom} 13 | */ 14 | classroom: Classroom; 15 | } 16 | import User = require("./User"); 17 | import Classroom = require("./Classroom"); 18 | -------------------------------------------------------------------------------- /typings/src/Test.d.ts: -------------------------------------------------------------------------------- 1 | export = Test; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Test extends Assignment { 6 | } 7 | declare namespace Test { 8 | export { RawDataObject }; 9 | } 10 | import Assignment = require("./Assignment"); 11 | type RawDataObject = import("../lib/RawData").RawDataObject; 12 | -------------------------------------------------------------------------------- /typings/src/Timetable.d.ts: -------------------------------------------------------------------------------- 1 | export = Timetable; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | declare class Timetable extends RawData { 6 | /** 7 | * 8 | * @static 9 | * @param {string} html 10 | * @return {RawDataObject} 11 | * @memberof Timetable 12 | */ 13 | static parse(html: string): RawDataObject; 14 | /** 15 | * Creates an instance of Timetable. 16 | * @param {RawDataObject} [data={}] 17 | * @param {string} [date=null] 18 | * @param {Edupage} [edupage=null] 19 | * @memberof Timetable 20 | */ 21 | constructor(data?: RawDataObject, date?: string, edupage?: Edupage); 22 | /** 23 | * Edupage instance associated to this object 24 | * @type {Edupage} 25 | */ 26 | edupage: Edupage; 27 | /** 28 | * @type {Date} 29 | */ 30 | date: Date; 31 | /** 32 | * @type {Lesson[]} 33 | */ 34 | lessons: Lesson[]; 35 | /** 36 | * @type {number} 37 | */ 38 | week: number; 39 | /** 40 | * @param {Edupage} [edupage=null] 41 | * @memberof Timetable 42 | */ 43 | init(edupage?: Edupage): void; 44 | } 45 | declare namespace Timetable { 46 | export { RawDataObject }; 47 | } 48 | import RawData = require("../lib/RawData"); 49 | import Edupage = require("./Edupage"); 50 | import Lesson = require("./Lesson"); 51 | type RawDataObject = import("../lib/RawData").RawDataObject; 52 | -------------------------------------------------------------------------------- /typings/src/User.d.ts: -------------------------------------------------------------------------------- 1 | export = User; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | /** 6 | * @typedef {import("./Teacher")} Teacher 7 | */ 8 | /** 9 | * @typedef {import("./Student")} Student 10 | */ 11 | /** 12 | * @typedef {import("./Parent")} Parent 13 | */ 14 | /** 15 | * @typedef {import("./Message")} Message 16 | */ 17 | /** 18 | * @typedef {import("./enums").EntityType} EntityType 19 | */ 20 | /** 21 | * @typedef {Object} LoginOptions 22 | * @prop {string | null | undefined} [code2FA=undefined] If the provided value is typeof `string`, it's considired as a 2FA code (should be provided after first unsuccessful login). If it's `null` the 2FA will be skipped. If omitted or `undefined` and the 2FA is requested by the Edupage, the function will resolve with `null`. 23 | * @prop {string} [user] Can be used to select a specific user in case there are more. 24 | * @prop {string} [edupage=""] The edupage subdomain (origin) to login to. Try to set this if you have trouble logging in (e.g. incorrect password error). 25 | */ 26 | declare class User extends RawData { 27 | /** 28 | * Creates an instance of Student or Teacher from user data. 29 | * @static 30 | * @param {string} userString Userstring to create an instance of 31 | * @param {RawDataObject} [data={}] Raw data to use for the instance 32 | * @param {Edupage} [edupage=null] Edupage instance to use for the instance 33 | * @return {User | Teacher | Student | Parent} User instance 34 | * @memberof User 35 | */ 36 | static from(userString: string, data?: RawDataObject, edupage?: Edupage): User | Teacher | Student | Parent; 37 | /** 38 | * Parses the user string to provide some useful information 39 | * @static 40 | * @param {string} userString Userstring to parse 41 | * @return {{id: string, type: EntityType, wildcard: boolean}} Parsed userstring 42 | * @memberof User 43 | */ 44 | static parseUserString(userString: string): { 45 | id: string; 46 | type: EntityType; 47 | wildcard: boolean; 48 | }; 49 | /** 50 | * Creates an instance of User. 51 | * @param {RawDataObject} [data={}] Raw data to initialize the instance with. 52 | * @param {Edupage} [edupage=null] Edupage instance to use. 53 | * @memberof User 54 | */ 55 | constructor(data?: RawDataObject, edupage?: Edupage); 56 | /** 57 | * Edupage instance associated to this object. 58 | * @type {Edupage} 59 | */ 60 | edupage: Edupage; 61 | /** 62 | * Date since when the user is registered 63 | * @type {Date} 64 | */ 65 | dateFrom: Date; 66 | /** 67 | * Date of expected leave of the user 68 | * @type {Date} 69 | */ 70 | dateTo: Date; 71 | /** 72 | * Firstname of the user 73 | * @type {string} 74 | */ 75 | firstname: string; 76 | /** 77 | * Lastname of the user 78 | * @type {string} 79 | */ 80 | lastname: string; 81 | /** 82 | * Gender of the user 83 | * @type {GENDER} 84 | */ 85 | gender: GENDER; 86 | /** 87 | * Edupage identifier of the user in format of number 88 | * @example "845796" 89 | * @type {string} 90 | */ 91 | id: string; 92 | /** 93 | * Edupage userstring of the user 94 | * @example "Student845796" 95 | * @type {string} 96 | */ 97 | userString: string; 98 | /** 99 | * Flag marking if the user has left the school 100 | * @type {boolean} 101 | */ 102 | isOut: boolean; 103 | /** 104 | * Edupage origin of the user (subdomain) 105 | * @type {string} 106 | */ 107 | origin: string; 108 | /** 109 | * Login credentials of the user. Set if the user is logged in or has attempted to log in. 110 | * @type {{username: string, password: string} | null} 111 | */ 112 | credentials: { 113 | username: string; 114 | password: string; 115 | }; 116 | /** 117 | * CookieJar object storing current session cookies for logged in user 118 | * @type {CookieJar} 119 | */ 120 | cookies: CookieJar; 121 | /** 122 | * Flag telling if the user is logged in 123 | * @type {boolean} 124 | */ 125 | isLoggedIn: boolean; 126 | /** 127 | * Email address of the logged in user 128 | * @type {string} 129 | */ 130 | email: string; 131 | /** 132 | * Initializes instance with raw data 133 | * @param {Edupage} [edupage=null] 134 | * @memberof Class 135 | */ 136 | init(edupage?: Edupage): void; 137 | /** 138 | * @typedef {Object} PollOption 139 | * @prop {string} text Text of the option. 140 | * @prop {string} [id] Id of the option. If not provided, a new one will be generated. 141 | */ 142 | /** 143 | * @typedef {Object} PollOptions 144 | * @prop {PollOption[]} options Options to be added to the poll. 145 | * @prop {boolean} [multiple=false] If `true` multiple choices can be selected. 146 | */ 147 | /** 148 | * @typedef {Object} MessageOptions 149 | * @prop {string} text Text of the message. 150 | * @prop {boolean} [important=false] If `true` the message will be marked as important. You will also be able to track who has read the message. 151 | * @prop {boolean} [parents=false] If `true` the message will be sent to student as well as their parents. 152 | * @prop {Attachment[]} [attachments=[]] Attachments to be added to the message. 153 | * @prop {PollOptions} [poll] Poll to be added to the message. 154 | */ 155 | /** 156 | * Sends a message to the user 157 | * @param {MessageOptions} options Message options 158 | * @this {User | Teacher | Student | Parent} 159 | * @memberof User 160 | */ 161 | sendMessage(options: { 162 | /** 163 | * Text of the message. 164 | */ 165 | text: string; 166 | /** 167 | * If `true` the message will be marked as important. You will also be able to track who has read the message. 168 | */ 169 | important?: boolean; 170 | /** 171 | * If `true` the message will be sent to student as well as their parents. 172 | */ 173 | parents?: boolean; 174 | /** 175 | * Attachments to be added to the message. 176 | */ 177 | attachments?: Attachment[]; 178 | /** 179 | * Poll to be added to the message. 180 | */ 181 | poll?: { 182 | /** 183 | * Options to be added to the poll. 184 | */ 185 | options: { 186 | /** 187 | * Text of the option. 188 | */ 189 | text: string; 190 | /** 191 | * Id of the option. If not provided, a new one will be generated. 192 | */ 193 | id?: string; 194 | }[]; 195 | /** 196 | * If `true` multiple choices can be selected. 197 | */ 198 | multiple?: boolean; 199 | }; 200 | }): Promise; 201 | /** 202 | * @typedef {import("../lib/ResponseTypings").MAuthResponse} MAuthResponse 203 | */ 204 | /** 205 | * Logs in the user. Provide third parameter as login options if you have troubles logging in. 206 | * @param {string} username Username of the user 207 | * @param {string} password Password of the user 208 | * @param {LoginOptions} [options] Login options 209 | * @return {Promise} Returns a promise that resolves with the `User` object if successful. If the 2FA is requested by the Edupage, the promise will resolve with `null`. 210 | * @memberof User 211 | */ 212 | login(username: string, password: string, options?: LoginOptions): Promise; 213 | /** 214 | * Retruns Edupage's representation of the user id. 215 | * @return {string} Userstring 216 | * @memberof User 217 | */ 218 | getUserString(): string; 219 | } 220 | declare namespace User { 221 | export { RawDataObject, Teacher, Student, Parent, Message, EntityType, LoginOptions }; 222 | } 223 | import RawData = require("../lib/RawData"); 224 | import Edupage = require("./Edupage"); 225 | import { GENDER } from "./enums"; 226 | import CookieJar = require("../lib/CookieJar"); 227 | import Attachment = require("./Attachment"); 228 | type LoginOptions = { 229 | /** 230 | * If the provided value is typeof `string`, it's considired as a 2FA code (should be provided after first unsuccessful login). If it's `null` the 2FA will be skipped. If omitted or `undefined` and the 2FA is requested by the Edupage, the function will resolve with `null`. 231 | */ 232 | code2FA?: string | null | undefined; 233 | /** 234 | * Can be used to select a specific user in case there are more. 235 | */ 236 | user?: string; 237 | /** 238 | * The edupage subdomain (origin) to login to. Try to set this if you have trouble logging in (e.g. incorrect password error). 239 | */ 240 | edupage?: string; 241 | }; 242 | type RawDataObject = import("../lib/RawData").RawDataObject; 243 | type Teacher = import("./Teacher"); 244 | type Student = import("./Student"); 245 | type Parent = import("./Parent"); 246 | type EntityType = import("./enums").EntityType; 247 | type Message = import("./Message"); 248 | -------------------------------------------------------------------------------- /typings/src/constants.d.ts: -------------------------------------------------------------------------------- 1 | export const SESSION_PING_INTERVAL_MS: number; 2 | -------------------------------------------------------------------------------- /typings/src/enums.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Edupage API endpoint 3 | */ 4 | export type APIEndpoint = number; 5 | /** 6 | * Gender type 7 | */ 8 | export type Gender = string; 9 | /** 10 | * Edupage primitive enitity type 11 | */ 12 | export type EntityType = string; 13 | /** 14 | * Edupage API resporse status 15 | */ 16 | export type APIStatus = string; 17 | /** 18 | * Assignment type 19 | */ 20 | export type AssignmentType = string; 21 | /** 22 | * Assignment group 23 | */ 24 | export type AssignmentGroup = AssignmentType[]; 25 | /** 26 | * Timeline item type 27 | */ 28 | export type TimelineItemType = string; 29 | export type GENDER = Gender; 30 | export namespace GENDER { 31 | const MALE: string; 32 | const FEMALE: string; 33 | } 34 | export type ENDPOINT = APIEndpoint; 35 | export namespace ENDPOINT { 36 | const DASHBOARD_GET_USER: number; 37 | const DASHBOARD_GET_CLASSBOOK: number; 38 | const DASHBOARD_GCALL: number; 39 | const DASHBOARD_SIGN_ONLINE_LESSON: number; 40 | const TIMELINE_GET_DATA: number; 41 | const TIMELINE_GET_REPLIES: number; 42 | const TIMELINE_GET_CREATED_ITEMS: number; 43 | const TIMELINE_CREATE_ITEM: number; 44 | const TIMELINE_CREATE_CONFIRMATION: number; 45 | const TIMELINE_CREATE_REPLY: number; 46 | const TIMELINE_FLAG_HOMEWORK: number; 47 | const TIMELINE_UPLOAD_ATTACHMENT: number; 48 | const ELEARNING_TEST_DATA: number; 49 | const ELEARNING_TEST_RESULTS: number; 50 | const ELEARNING_CARDS_DATA: number; 51 | const GRADES_DATA: number; 52 | const SESSION_PING: number; 53 | } 54 | export type ENTITY_TYPE = EntityType; 55 | export namespace ENTITY_TYPE { 56 | const STUD_PLAN: string; 57 | const STUDENT: string; 58 | const CUST_PLAN: string; 59 | const STUDENT_ONLY: string; 60 | const STUD_CLASS: string; 61 | const TEACHER: string; 62 | const ALL: string; 63 | const CLASS: string; 64 | const STUDENT_ALL: string; 65 | const STUDENTONLY_ALL: string; 66 | const TEACHER_ALL: string; 67 | const ADMIN: string; 68 | const PARENT: string; 69 | } 70 | export type API_STATUS = APIStatus; 71 | export namespace API_STATUS { 72 | const OK: string; 73 | const FAIL: string; 74 | } 75 | export type ASSIGNMENT_TYPE = AssignmentType; 76 | export namespace ASSIGNMENT_TYPE { 77 | const HOMEWORK: string; 78 | const ETEST_HOMEWORK: string; 79 | const BIG_EXAM: string; 80 | const EXAM: string; 81 | const SMALL_EXAM: string; 82 | const ORAL_EXAM: string; 83 | const REPORT_EXAM: string; 84 | const TESTING: string; 85 | const TEST: string; 86 | const PROJECT_EXAM: string; 87 | const ETEST: string; 88 | const ETEST_PRINT: string; 89 | const ETEST_LESSON: string; 90 | const LESSON: string; 91 | const PROJECT: string; 92 | const RESULT: string; 93 | const CURRICULUM: string; 94 | const TIMELINE: string; 95 | } 96 | export type ASSIGNMENT_GROUP = string[]; 97 | export namespace ASSIGNMENT_GROUP { 98 | const HOMEWORK_1: string[]; 99 | export { HOMEWORK_1 as HOMEWORK }; 100 | const EXAM_1: string[]; 101 | export { EXAM_1 as EXAM }; 102 | const TEST_1: string[]; 103 | export { TEST_1 as TEST }; 104 | const PROJECT_1: string[]; 105 | export { PROJECT_1 as PROJECT }; 106 | export const PRESENTATION: string[]; 107 | export const OTHER: string[]; 108 | } 109 | export type TIMELINE_ITEM_TYPE = TimelineItemType; 110 | export namespace TIMELINE_ITEM_TYPE { 111 | export const MESSAGE: string; 112 | export const MESSAGE_TO_SUBTITUTER: string; 113 | export const NOTICEBOARD: string; 114 | export const GRADE_ANNOUNCEMENT: string; 115 | export const GRADE: string; 116 | export const NOTE: string; 117 | const HOMEWORK_2: string; 118 | export { HOMEWORK_2 as HOMEWORK }; 119 | export const HOMEWORK_STUDENT_STATE: string; 120 | export const ABSENCE_NOTE: string; 121 | export const ABSENCE_NOTE_REMINDER: string; 122 | export const PROCESS: string; 123 | export const PROCESS_ADMIN: string; 124 | export const STUDENT_ABSENT: string; 125 | export const ACCIDENT: string; 126 | export const EVENT: string; 127 | export const TIMETABLE: string; 128 | export const SUBSTITUTION: string; 129 | export const CANTEEN_MENU: string; 130 | export const CANTEEN_CREDIT: string; 131 | export const CANTEEN_SUSPEND_REINSTATE_ORDERS: string; 132 | export const CANTEEN_OPENING: string; 133 | export const SURVEY: string; 134 | export const PLAN: string; 135 | export const SETTINGS: string; 136 | export const ALBUM: string; 137 | export const NEWS: string; 138 | export const TEST_ASSIGNMENT: string; 139 | export const TEST_RESULT: string; 140 | export const CHAT: string; 141 | export const CHECK_IN: string; 142 | export const CONSULTATION_MESSAGE: string; 143 | export const CONSULTATION: string; 144 | export const PAYMENTS: string; 145 | export const SIGN_IN: string; 146 | const CURRICULUM_1: string; 147 | export { CURRICULUM_1 as CURRICULUM }; 148 | export const CURRICULUM_REMINDER: string; 149 | export const BLACKBOARD: string; 150 | export const STUDENT_PICKUP: string; 151 | export const TIMETABLE_CLOUD_GENERATE: string; 152 | export const CONFIRMATION: string; 153 | export const CONTEST: string; 154 | } 155 | -------------------------------------------------------------------------------- /typings/src/exceptions.d.ts: -------------------------------------------------------------------------------- 1 | export type RawDataObject = import("../lib/RawData").RawDataObject; 2 | /** 3 | * @typedef {import("../lib/RawData").RawDataObject} RawDataObject 4 | */ 5 | export class LoginError extends Error { 6 | constructor(message: any); 7 | } 8 | export class ParseError extends Error { 9 | constructor(message: any); 10 | } 11 | export class EdupageError extends Error { 12 | constructor(message: any); 13 | } 14 | export class APIError extends Error { 15 | constructor(message: any, ...data: any[]); 16 | data: any[]; 17 | } 18 | export class MessageError extends Error { 19 | constructor(message: any, ...data: any[]); 20 | data: any[]; 21 | } 22 | export class AttachmentError extends Error { 23 | constructor(message: any, ...data: any[]); 24 | data: any[]; 25 | } 26 | export class FatalError extends Error { 27 | static warningsEnabled: boolean; 28 | /** 29 | * @static 30 | * @param {Error} error 31 | * @param {RawDataObject} data 32 | * @returns {any} 33 | * @memberof FatalError 34 | */ 35 | static throw(error: Error, data: RawDataObject): any; 36 | /** 37 | * @static 38 | * @param {Error} error 39 | * @param {RawDataObject} data 40 | * @returns {any} 41 | * @memberof FatalError 42 | */ 43 | static warn(error: Error, data: RawDataObject): any; 44 | } 45 | --------------------------------------------------------------------------------