const debug = require("debug")("edupage:log");
const error = require("debug")("edupage:error");
const {default: fetch} = require("node-fetch");
const CookieJar = require("../lib/CookieJar");
const {LoginError, ParseError, EdupageError, APIError, MessageError, FatalError} = require("./exceptions");
const {GENDER, ENDPOINT, ENTITY_TYPE, API_STATUS, TIMELINE_ITEM_TYPE} = require("./enums");
const Edupage = require("./Edupage");
const RawData = require("../lib/RawData");
const Attachment = require("./Attachment");
debug.log = console.log.bind(console);
class User extends RawData {
constructor(data = {}, edupage = null) {
super(data);
this.edupage = edupage;
this.dateFrom = data.datefrom ? new Date(data.datefrom) : null;
this.dateTo = data.dateto ? new Date(data.dateto) : null;
this.firstname = data.firstname;
this.lastname = data.lastname;
this.gender = data.gender;
this.id = data.id;
this.userString = null;
this.isOut = data.isOut;
this.origin = null;
this.credentials = null;
this.cookies = null;
this.isLoggedIn = false;
this.email = null;
if(this.edupage) User.prototype.init.call(this);
}
init(edupage = null) {
if(edupage) this.edupage = edupage;
this.origin = this.edupage.user.origin;
}
async sendMessage(options) {
const {
text = "",
important = false,
parents = false,
allowReplies = true,
repliesToAuthorOnly = false,
attachments = [],
poll = null
} = options;
if(!this.edupage) throw new EdupageError(`User does not have assigned Edupage instance yet`);
const hasPoll = poll && poll.options && poll.options;
const res = await this.edupage.api({
url: ENDPOINT.TIMELINE_CREATE_ITEM,
data: {
attachements: JSON.stringify(attachments.reduce((a, b) => ({...a, ...b.toJSON()}), {})),
receipt: important ? "1" : "0",
repliesDisabled: !allowReplies ? "1" : "0",
repliesToAllDisabled: !allowReplies || repliesToAuthorOnly ? "1" : "0",
selectedUser: this.getUserString(parents),
text: text,
typ: TIMELINE_ITEM_TYPE.MESSAGE,
votingParams: hasPoll ? JSON.stringify({
"answers": poll.options.map(e => ({
text: e.text,
id: e.id || Math.random().toString(16).slice(2)
})),
"multiple": false
}) : undefined
}
});
if(res.status !== API_STATUS.OK) {
error(`Received invalid status from the server '${res.status}'`);
throw new APIError(`Failed to send message: Invalid status received '${res.status}'`, res);
}
if(!res.changes?.length) {
error(`Failed to send message (no changes made) (${res.changes})`);
throw new MessageError(`Failed to send message: No changes were made`, res);
}
if(res.changes.length > 1) debug(`[Message] Multiple changes after posting single message`, res.changes);
return new (require("./Message"))(res.changes[0], this.edupage);
}
async login(username, password, options = {}) {
if(!username || !password) throw new LoginError(`Invalid credentials`);
if(!options) options = {};
if(!options.code2FA) options.code2FA = undefined;
if(!options.user) options.user = undefined;
this.cookies = new CookieJar();
return new Promise((resolve, reject) => {
debug(`[Login] Logging in as ${username}...`);
const payload = {
"m": username,
"h": password,
"edupage": options.edupage || "",
"plgc": null,
"ajheslo": "1",
"hasujheslo": "1",
"ajportal": "1",
"ajportallogin": "1",
"mobileLogin": "1",
"version": "2020.0.18",
"fromEdupage": options.edupage || "",
"device_name": null,
"device_id": null,
"device_key": "",
"os": null,
"murl": null,
"edid": ""
};
const loginServer = options.edupage || "login1";
const skip2Fa = options.code2FA === null;
if(typeof options.code2FA === "string") payload.t2fasec = options.code2FA;
fetch(`https://${loginServer}.edupage.org/login/mauth`, {
"headers": {
"accept": "application/json, text/javascript, */*; q=0.01",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
},
"method": "POST",
"body": new URLSearchParams(payload).toString()
}).then(res => {
debug(`[Login] Saving received cookies...`);
this.cookies.setCookie(res);
return res.json();
}).then(async (json) => {
let selectedUser = null;
if(!json.users.length) {
if(json.needEdupage) {
reject(new LoginError(`Failed to login: Incorrect username. (If you are sure that the username is correct, try providing 'edupage' option)`));
} else {
reject(new LoginError(`Failed to login: Incorrect password. (If you are sure that the password is correct, try providing 'edupage' option)`));
}
return;
}
if(json.users.length == 1) {
if(json.users[0].need2fa == "1" && !skip2Fa) {
if(json.t2fasec) {
debug(`[Login] 2FA code is invalid`);
return reject(new LoginError(`Invalid 2FA code`));
} else {
debug(`[Login] 2FA was requested by the Edupage`);
return resolve(null);
}
} else {
debug(`[Login] Successfully logged in`);
selectedUser = json.users[0];
}
} else {
debug(`[Login] Found multiple users with the same username`);
if(options.user) {
const user = json.users.find(user => user.userid == options.user);
if(user) {
debug(`[Login] Selected user by 'user' option`);
selectedUser = user;
}
}
if(!selectedUser) {
error(`[Login] No user selected`);
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.`));
}
}
this.cookies.setCookie("PHPSESSID", selectedUser.esid);
this.origin = selectedUser.edupage;
this.isLoggedIn = true;
this.credentials = {
username,
password
};
debug(`[Login] Login successful`);
resolve(this);
}).catch(err => {
error(`[Login] Failed to login user:`, err);
reject(err);
});
});
}
getUserString() {
return this.userString;
}
static from(userString, data = {}, edupage = null) {
const {id, type} = this.parseUserString(userString);
data.id = id || null;
if(type == ENTITY_TYPE.TEACHER) return new (require("./Teacher"))(data, edupage);
if(type == ENTITY_TYPE.STUDENT) return new (require("./Student"))(data, edupage);
if(type == ENTITY_TYPE.PARENT) return new (require("./Parent"))(data, edupage);
const user = new User(data, edupage);
user.userString = userString;
return user;
}
static parseUserString(userString) {
const id = (userString.match(/-?\d+/) || [])[0];
const type = ((userString.match(/[a-z]+/i) || [])[0] || "");
const wildcard = userString.indexOf("*") > -1;
return {id, type, wildcard};
}
}
module.exports = User;