propojení profilů, větší podpora pro učitele

This commit is contained in:
Daniel Bulant 2021-10-24 12:54:50 +02:00
parent b1b82bfa5e
commit 78554b24d6
8 changed files with 224 additions and 15 deletions

146
commands/ssps/cmd.js Normal file
View file

@ -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 <email> [--discord <id>] - Connects discord and user profile
teacher-connect <discord id> <email>- Connects discord and teacher profile
set-teacher-room <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 <discord id> <email>
`);
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 <id> 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 <email> [--discord <id>]
`);
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 <id>.");
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");
}
};

View file

@ -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);
}

View file

@ -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",

View file

@ -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 = [

View file

@ -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)*?)</)?.[1];
let komise = text.match(/Komise(?:.|\n)*?"value">((?:.|\n)*?)</)?.[1];
if(!room && !komise) continue;
room = room.trim();
komise = komise.trim();
console.log(room, komise);
await Room.create({
id: room
}, { ignoreDuplicates: true });
teacher.roomId = room;
teacher.komise = komise;
await teacher.save();
}
}
console.log("Profesni sit synced");

View file

@ -27,6 +27,10 @@ const Person = sequelize.define(
discord: {
type: DataTypes.BIGINT
},
type: {
type: DataTypes.ENUM,
values: ["student", "teacher"]
},
flags: {
type: DataTypes.INTEGER
}

View file

@ -13,6 +13,9 @@ const Teacher = sequelize.define(
},
name: {
type: DataTypes.STRING
},
komise: {
type: DataTypes.STRING
}
}, {
indexes: [{

View file

@ -51,4 +51,9 @@ Timetable.belongsTo(Room);
Person.hasOne(Student);
Person.hasOne(Teacher);
sequelize.sync({ force: global.config.mysql.forceUpdate });
Teacher.belongsTo(Room);
sequelize.sync({
force: global.config.mysql.forceUpdate,
alter: global.config.mysql.update
});