mirror of
https://github.com/danbulant/ssps-bot
synced 2026-05-19 12:08:57 +00:00
288 lines
7.5 KiB
JavaScript
288 lines
7.5 KiB
JavaScript
const { DateTime } = require("luxon");
|
|
const fetch = require("node-fetch");
|
|
|
|
async function request(endpoint, body) {
|
|
const res = await fetch(endpoint.startsWith("https://") ? endpoint : "https://www.ssps.cz/" + endpoint, {
|
|
method: body ? "POST" : "GET",
|
|
body,
|
|
headers: {
|
|
accept: "application/json",
|
|
"user-agent": "Discord Bot (https://danbulant.eu Daniel Bulant bulant.da.2021@ssps.cz)",
|
|
"x-notes": "Zobrazeni informaci o skole v ramci Discordu"
|
|
}
|
|
});
|
|
return await res.json();
|
|
}
|
|
|
|
/**
|
|
* @typedef AbsentClass
|
|
* @property {ClassEntity} Entity
|
|
* @property {(null | "ODP")[]} Reasons
|
|
*/
|
|
/**
|
|
* @typedef ClassEntity
|
|
* @property {string} Id
|
|
* @property {string} Abbrev
|
|
*/
|
|
/**
|
|
* @typedef ChangedClass
|
|
* @property {ClassEntity} Class
|
|
* @property {ChangedLesson[]} ChangedLessons
|
|
* @property {ChangedLesson[]} CancelledLessons
|
|
* @property {string[]} ChangedGroups
|
|
*/
|
|
/**
|
|
* @typedef ChangedLesson
|
|
* @property {"supluje" | "přesun >>" | "přesun <<" | "spojí"} ChgType1
|
|
* @property {string} ChgType2
|
|
* @property {string} Hour Číslo ale ve stringu
|
|
* @property {string} Subject 3 letter code in uppercase
|
|
* @property {string} Group Empty if all
|
|
* @property {string} [Room] Room number
|
|
* @property {string} [Teacher] Teacher code (Abbrev)
|
|
*/
|
|
|
|
class Supplementations {
|
|
constructor(data) {
|
|
/** @type {{ AbsentClasses: AbsentClass[], ChangesForClasses: ChangedClass[]}} */
|
|
this.data = data;
|
|
}
|
|
|
|
getByClassName(name) {
|
|
const absent = this.data.AbsentClasses.filter(t => t.Entity.Abbrev === name);
|
|
const changed = this.data.ChangesForClasses.filter(t => t.Class.Abbrev === name);
|
|
return { absent, changed };
|
|
}
|
|
|
|
getByTeacher(abbrev) {
|
|
return this.data.ChangesForTeachers.filter(t => t.Teacher.Abbrev === abbrev || t.Teacher.Name === abbrev);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef Atom
|
|
* @property {string} Id
|
|
* @property {string} Abbrev
|
|
* @property {string} Name
|
|
*/
|
|
/**
|
|
* @typedef CellAtom
|
|
* @property {Atom} Class
|
|
* @property {Atom} Group
|
|
* @property {Atom} Subject
|
|
* @property {Atom} Teacher
|
|
* @property {Atom} Room
|
|
* @property {Atom[]} Cycles
|
|
* @property {Atom[]} Stamps
|
|
*/
|
|
|
|
class Schedule {
|
|
/** @type {(CellAtom | CellAtom[])[][]} */
|
|
schedule = [];
|
|
constructor(data) {
|
|
this.data = data;
|
|
for(var cell of data.Cells) {
|
|
if(!this.schedule[cell.DayIndex]) this.schedule[cell.DayIndex] = [];
|
|
this.schedule[cell.DayIndex][cell.HourIndex] = cell.Atoms;
|
|
}
|
|
}
|
|
|
|
get type() {
|
|
return this.data.Type;
|
|
}
|
|
|
|
get id() {
|
|
return this.data.TargetId;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @typedef WPPost
|
|
* @property {number} id
|
|
* @property {string} date ISO date
|
|
* @property {string} date_gmt
|
|
* @property {{ rendered: string }} guild
|
|
* @property {string} modified ISO date
|
|
* @property {string} modified_gmt
|
|
* @property {string} slug Used in pretty URLs
|
|
* @property {string} status Always publish in public API
|
|
* @property {"post"} type
|
|
* @property {string} link
|
|
* @property {{ rendered: string }} title
|
|
* @property {{ rendered: string, protected: boolean }} content
|
|
* @property {{ rendered: string, protected: boolean }} excerpt
|
|
* @property {number} author
|
|
* @property {number} featured_media
|
|
* @property {"closed" | "open"} comment_status Seems to be always closed
|
|
* @property {"closed" | "open"} ping_status Seems to be always on
|
|
* @property {boolean} sticky
|
|
* @property {string} template
|
|
* @property {string} format commonly 'standard'
|
|
* @property {[]} meta ?
|
|
* @property {number[]} categories
|
|
* @property {WPPostLinks} _links
|
|
*/
|
|
/**
|
|
* @typedef WPPostLinks
|
|
* @property {{ href: string }[]} self
|
|
* @property {{ href: string }[]} collection
|
|
* @property {{ href: string }[]} about
|
|
* @property {{ href: string, embeddable: boolean }[]} author
|
|
* @property {{ href: string, embeddable: boolean }[]} replies
|
|
* @property {{ href: string, count: number }[]} version-history
|
|
*/
|
|
|
|
/**
|
|
* @typedef ProfesniUzivatel
|
|
* @property {string|null} about
|
|
* @property {string|null} birthday
|
|
* @property {boolean} blocked
|
|
* @property {string|null} class
|
|
* @property {number} documentId
|
|
* @property {string} email
|
|
* @property {string} firstName
|
|
* @property {string} lastName
|
|
* @property {number} id
|
|
* @property {string} imagePath
|
|
* @property {number} similarity
|
|
*/
|
|
|
|
class ProfesniSitAPI {
|
|
async *getUsers() {
|
|
var errors = 0;
|
|
var id = 1;
|
|
while(errors < 15) {
|
|
/** @type {ProfesniUzivatel|null} */
|
|
const res = await request(`https://profesnisit.ssps.cz/user/get-user?id=${id}`);
|
|
id++;
|
|
console.log("Trying", id);
|
|
if(!res || Object.values(res).every(t => !t)) {
|
|
errors++;
|
|
console.log("Errored", errors, "times");
|
|
continue;
|
|
}
|
|
errors = 0;
|
|
yield res;
|
|
}
|
|
}
|
|
}
|
|
|
|
const letterMap = {
|
|
"ě": "e",
|
|
"š": "s",
|
|
"č": "c",
|
|
"ř": "r",
|
|
"ž": "z",
|
|
"ý": "y",
|
|
"á": "a",
|
|
"í": "i",
|
|
"é": "e",
|
|
"ó": "o",
|
|
"ú": "u",
|
|
"ů": "u"
|
|
};
|
|
|
|
class API {
|
|
request = request;
|
|
profesniSit = new ProfesniSitAPI;
|
|
|
|
async getSupplementations(date = new DateTime) {
|
|
const res = await request(`wp-content/themes/ssps-wordpress-theme/supplementation.php/?date=${date.toFormat("yyyyMMdd")}`);
|
|
|
|
return new Supplementations(res);
|
|
}
|
|
|
|
/**
|
|
* @returns {Promise<Schedule>}
|
|
*/
|
|
async getSchedule(className) {
|
|
const res = await request(`wp-content/themes/ssps-wordpress-theme/schedule.php/?class=${className}`);
|
|
|
|
return new Schedule(res);
|
|
}
|
|
|
|
async getNews() {
|
|
/** @type {WPPost[]} */
|
|
const res = await request(`wp-json/wp/v2/posts`);
|
|
|
|
return res;
|
|
}
|
|
|
|
formatRoom(room) {
|
|
if(!room) return "";
|
|
return room.toString().padStart(3, "0");
|
|
}
|
|
|
|
isTeacherMail(mail) {
|
|
return /[a-z.]+@ssps\.cz/.test(mail);
|
|
}
|
|
isStudentMail(mail) {
|
|
return /[a-z]+\.[a-z]{2}\.20[12][0-9]@ssps\.cz/.test(mail);
|
|
}
|
|
|
|
buildTeacherMail(name) {
|
|
if(!Array.isArray(name)) name = name.split(" ");
|
|
return `${this.removeCestina(name[1] + " " + name[0]).replace(/ /g, ".")}@ssps.cz`.toLowerCase();
|
|
}
|
|
buildStudentMail(name, year) {
|
|
if(!Array.isArray(name)) name = name.split(" ");
|
|
return `${this.removeCestina(name[1])}.${this.removeCestina(name[0]).substr(0,2)}.${year}@ssps.cz`.toLowerCase();
|
|
}
|
|
|
|
removeCestina(str) {
|
|
return str.split("").map(t => letterMap[t] || t).join("");
|
|
}
|
|
|
|
groups = [
|
|
["celá třída"],
|
|
["Skupina 1", "Skupina 2"],
|
|
["Cizí jazyk 1", "Cizí jazyk 2"]
|
|
]
|
|
|
|
map = {
|
|
"1A": "1U",
|
|
"1B": "1V",
|
|
"1C": "1W",
|
|
"1G": "1X",
|
|
"1K": "1Y",
|
|
"2A": "1P",
|
|
"2B": "1Q",
|
|
"2C": "1R",
|
|
"2L": "1S",
|
|
"2K": "1T",
|
|
"3A": "1K",
|
|
"3B": "1L",
|
|
"3C": "1M",
|
|
"3L": "1N",
|
|
"3K": "1O",
|
|
"4A": "1E",
|
|
"4B": "1F",
|
|
"4C": "1G",
|
|
"4L": "1I",
|
|
"4K": "1J"
|
|
}
|
|
demap = {
|
|
"1U": "1A",
|
|
"1V": "1B",
|
|
"1W": "1C",
|
|
"1X": "1G",
|
|
"1Y": "1K",
|
|
"1P": "2A",
|
|
"1Q": "2B",
|
|
"1R": "2C",
|
|
"1S": "2L",
|
|
"1T": "2K",
|
|
"1K": "3A",
|
|
"1L": "3B",
|
|
"1M": "3C",
|
|
"1N": "3L",
|
|
"1O": "3K",
|
|
"1E": "4A",
|
|
"1F": "4B",
|
|
"1G": "4C",
|
|
"1I": "4L",
|
|
"1J": "4K",
|
|
}
|
|
}
|
|
|
|
module.exports = new API;
|