diff --git a/commands/ssps/cmd.js b/commands/ssps/cmd.js new file mode 100644 index 0000000..55e2b96 --- /dev/null +++ b/commands/ssps/cmd.js @@ -0,0 +1,146 @@ +const commando = require("@iceprod/discord.js-commando"); +const minimist = require("minimist"); +const api = require("../../utils/api"); +const ssps = require("../../utils/ssps-server"); +const Student = require("../../utils/models/student"); +const Person = require("../../utils/models/person"); +const Teacher = require("../../utils/models/teacher"); + +module.exports = class cmd extends commando.Command { + constructor(client) { + super(client, { + name: "cmd", + memberName: "cmd", + aliases: ["run"], + group: "ssps", + description: "Runs a management command", + argsType: "multiple" + }); + } + + async run(msg, args) { + const argv = minimist(args, { + alias: { + help: "h" + }, + string: "_" + }); + + /** @param {string} str */ + function send(str) { + var lines = str.split("\n").map(t => t.trimRight()); + while(lines[0].length === 0) lines.shift(); + while(lines[lines.length - 1].length === 0) lines.pop(); + var shortestTrim = lines + .map(a => a.match(/\S/) && a.match(/^\s*/)[0].length) + .filter(a => a !== null) + .reduce((a, b) => Math.min(a, b)); + lines = lines.map(t => t.substr(shortestTrim)); + while(lines[0].length === 0) lines.shift(); + while(lines[lines.length - 1].length === 0) lines.pop(); + var sentLines = [""]; + for(const line of lines) { + if(sentLines[sentLines.length - 1] && sentLines[sentLines.length - 1].length + line.lenght > 2048) + sentLines[sentLines.length] = line; + else + sentLines[sentLines.length - 1] = (sentLines[sentLines.length - 1] || "") + "\n" + line; + } + return sentLines.map(t => msg.say("```\n" + t.substr(1) + "\n```")); + } + + switch(argv._[0]) { + case "help": + return send(` + Runs a simple management command without fancy argument parsing. + For help of a given command, run it with -h or --help flag. + + Available commands: + help - Shows this page + connect [--discord ] - Connects discord and user profile + teacher-connect - Connects discord and teacher profile + set-teacher-room [email] - Sets teacher's homeroom + `); + case "connect": + return this.connectCmd(argv, send, msg); + case "teacher-connect": + return this.teacherConnectCmd(argv, send, msg); + case "set-teacher-room": + return send("Not yet implemented"); + default: + return send(`Command ${argv._[0]} not found`); + } + } + + async teacherConnectCmd(argv, send, msg) { + if(argv.help) return send(` + Connects discord and teacher profile. + Owner only + + Format: teacher-connect + `); + if(!this.client.isOwner(msg.author)) return send("You are not in the sudoers file, this incident will be reported."); + var discord = argv._[1]; + var mail = argv._[2]; + + if(!/^[0-9]+$/.test(discord)) return send("Špatný discord účet"); + var user = await this.client.users.fetch(discord).catch((e) => console.warn(e)); + if(!user) return send("Uživatel musí být v SSPŠ discordu."); + + if(!api.isTeacherMail(mail)) return send("Email má špatný formát."); + + const person = await Person.findOne({ + where: { + mail + } + }); + if(!person) return send("Učitel nenalezen"); + const teacher = await Teacher.findOne({ + where: { + personId: person.id + } + }); + if(!teacher) return send("Nastala neznámá chyba při hledání učitele"); + person.discord = discord; + await person.save(); + return send("Učitel propojen"); + } + + async connectCmd(argv, send, msg) { + if(argv.help) return send(` + Connects discord and user profile. + Defaults to connecting this account, use --discord to connect another account. + Only bot owner can connect other discord accounts. + If there's already a connected account, it's disconnected first. + + Format: connect [--discord ] + `); + var discord = argv.discord || msg.author.id; + if(!/^[0-9]+$/.test(discord)) return send("Špatný discord účet"); + var user = await this.client.users.fetch(discord).catch(() => null); + if(!user) return send("Uživatel musí být v SSPŠ discordu."); + if(argv.discord && !this.client.isOwner(msg.author)) return send("Pouze vlastník může použít --discord ."); + const email = argv._[1]; + if(!email) return send("Chybí email"); + if(!api.isStudentMail(email)) return send("Špatný email. Použijte školní email bez skola."); + + const student = await Student.findOne({ + where: { + id: email.split("@")[0] + } + }); + if(!student) return send("Student nenalezen. Momentálně je vyžadováno aby byl student registrovaný na profesní síti."); + const userClass = ssps.getClass(msg.author); + const classID = api.map[userClass]; + if(student.classId !== classID) return send(`Třída neodpovídá roli na SSPŠ serveru`); + if(!student.personId) return send("Není vytvoření uživatelský profil - BUG"); + const person = await Person.findOne({ + where: { + id: student.personId + } + }); + if(person.mail !== email) return send("Student nenalezen."); + person.discord = discord; + await person.save(); + return send("Propojeno"); + } +}; \ No newline at end of file diff --git a/commands/ssps/profil.js b/commands/ssps/profil.js index 69bf58d..6541e3d 100644 --- a/commands/ssps/profil.js +++ b/commands/ssps/profil.js @@ -3,6 +3,8 @@ const { MessageEmbed, APIMessage } = require("discord.js"); const { DateTime } = require("luxon"); const Person = require("../../utils/models/person"); const api = require("../../utils/api"); +const Teacher = require("../../utils/models/teacher"); +const Student = require("../../utils/models/student"); module.exports = class profil extends commando.Command { constructor(client) { @@ -32,11 +34,25 @@ module.exports = class profil extends commando.Command { return msg.say(embed); } embed.setTitle(person.name); - if(person.avatar) embed.setThumbnail(person.avatar); - embed.setDescription(person.about); + embed.setThumbnail(person.avatar || user.displayAvatarURL()); + if(person.about) embed.setDescription(person.about); if(person.birthday) embed.addField("Narozeniny", DateTime.fromFormat(person.birthday, "yyyy-MM-dd").toFormat("dd. MM. yyyy")); - embed.addField("Email", person.mail); - embed.addField("Typ", api.isTeacherMail(person.mail) ? "Učitel" : api.isStudentMail(person.mail) ? "Student" : "Neprestižní"); + embed.addField("Email", person.mail, true); + embed.addField("Typ", person.type === "teacher" ? "Učitel" : person.type === "student" ? "Student" : "Neprestižní", true); + if(person.discord) embed.addField("Discord", "<@" + person.discord + ">", true) + if(person.type === "teacher") { + const teacher = await Teacher.findOne({ + where: { personId: person.id } + }); + embed.addField("Zkratka", teacher.abbrev, true); + if(teacher.roomId) embed.addField("Místnost", teacher.roomId, true); + if(teacher.komise) embed.addField("Komise", teacher.komise, true); + } else if(person.type === "student") { + const student = await Student.findOne({ + where: { personId: person.id } + }); + embed.addField("Třída", api.demap[student.class]); + } return msg.say(embed); } diff --git a/package.json b/package.json index 1c643ea..82b6755 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "js-yaml": "^4.1.0", "luxon": "^2.0.2", "mariadb": "^2.5.5", + "minimist": "^1.2.5", "node-fetch": "2", "sequelize": "^6.7.0", "sqlite": "^4.0.23", diff --git a/utils/api.js b/utils/api.js index cd27eaf..74502b6 100644 --- a/utils/api.js +++ b/utils/api.js @@ -156,8 +156,9 @@ class ProfesniSitAPI { 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 === null)) { + if(!res || Object.values(res).every(t => !t)) { errors++; + console.log("Errored", errors, "times"); continue; } errors = 0; @@ -180,9 +181,6 @@ const letterMap = { "ú": "u", "ů": "u" }; -function removeCestina(str) { - return str.split("").map(t => letterMap[t] || t).join(""); -} class API { request = request; @@ -223,11 +221,16 @@ class API { } buildTeacherMail(name) { - return `${removeCestina(name).replace(/ /g, ".")}@ssps.cz`.toLowerCase(); + 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 `${removeCestina(name[1])}.${removeCestina(name[0]).substr(0,2)}.${year}@ssps.cz`.toLowerCase(); + 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 = [ diff --git a/utils/db/import.js b/utils/db/import.js index 601c546..7b43990 100644 --- a/utils/db/import.js +++ b/utils/db/import.js @@ -6,6 +6,7 @@ if(isMain) { global.config = config; } +const fetch = require("node-fetch"); const sequelize = require("../sequelize"); const api = require("../api"); const server = require("../ssps-server"); @@ -70,7 +71,8 @@ sequelize.afterBulkSync(async () => { await Person.create({ name: item.Teacher.Name, mail: api.buildTeacherMail(item.Teacher.Name), - flags: 1 + flags: 1, + type: "teacher" }).catch(contextErrors(item)); const person = await Person.findOne({ where: { mail: api.buildTeacherMail(item.Teacher.Name) } }); await Teacher.create({ @@ -108,16 +110,21 @@ sequelize.afterBulkSync(async () => { if(!user.email) continue; if(!user.email.endsWith("ssps.cz")) continue; if(user.email.endsWith("@skola.ssps.cz")) user.email = user.email.replace("@skola.ssps.cz", "@ssps.cz"); + user.email = user.email.toLowerCase(); console.log(user.email); - var person = await Person.findOne({ + var [person] = await Person.findOrBuild({ where: { [Op.or]: { mail: user.email, name: `${user.firstName} ${user.lastName}` } + }, + defaults: { + mail: user.email, + flags: 1, + type: api.isStudentMail(user.email) ? "student" : api.isTeacherMail(user.email) ? "teacher" : null } }); - if(!person) person = Person.build({ mail: user.email, flags: 1 }); if(user.about) person.about = user.about; if(user.birthday) person.birthday = new Date(user.birthday); person.name = `${user.firstName} ${user.lastName}`; @@ -144,7 +151,31 @@ sequelize.afterBulkSync(async () => { await student.save(); } } else if(api.isTeacherMail(user.email)) { - // synced by timetables + const res = await fetch(`https://www.ssps.cz/ucitel/${api.removeCestina(person.name.replace(/ /g, "-").toLowerCase())}`); + console.log(`Fetching https://www.ssps.cz/ucitel/${api.removeCestina(person.name.replace(/ /g, "-").toLowerCase())}`); + if(res.status !== 200) continue; + const text = await res.text(); + const teacher = await Teacher.findOne({ + where: { + personId: person.id + } + }); + if(!teacher) { + console.log("No teacher for", user.email); + continue; + } + let room = text.match(/kabinetu(?:.|\n)*?"value">((?:.|\n)*?)((?:.|\n)*?)