Reewrite #1

Merged
Sirherobrine23 merged 2 commits from nearby-guanaco into main 2023-12-04 17:28:34 +00:00
6 changed files with 214 additions and 68 deletions
Showing only changes of commit 50dd18f717 - Show all commits

@ -5,21 +5,17 @@
"type": "module",
"license": "MIT",
"dependencies": {
"cookie": "^0.5.0",
"get-video-duration": "^4.1.0",
"got": "^13.0.0",
"jsdom": "^22.1.0",
"mongodb": "^6.2.0",
"neste": "^3.0.0",
"telegraf": "^4.15.0",
"turndown": "^7.1.2"
"cookie": "^0.6.0",
"got": "^14.0.0",
"jsdom": "^23.0.1",
"mongodb": "^6.3.0",
"neste": "^3.0.0"
},
"devDependencies": {
"@types/cookie": "^0.5.3",
"@types/jsdom": "^21.1.4",
"@types/node": "^20.8.10",
"@types/turndown": "^5.0.3",
"@types/cookie": "^0.6.0",
"@types/jsdom": "^21.1.6",
"@types/node": "^20.10.3",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"typescript": "^5.3.2"
}
}

@ -1,16 +0,0 @@
import { ColaborareadRequest } from "./lib/colabora_req.js";
export default Get;
export async function Get(gotDom: ColaborareadRequest["gotDom"], semestre: string) {
const dashboard = new URL("https://www.colaboraread.com.br/aluno/dashboard/index"); dashboard.searchParams.set("matriculaId", semestre);
const { url: pageUrl, jsdoc } = (await gotDom(dashboard));
const { window: { document } } = jsdoc;
return Array.from(document.querySelectorAll("#navbar-content-aluno-pda > ul > li")).reduce<{ title: string; id: string }[]>((acc, e) => {
if (!(e.querySelector("table > tbody > tr > td:nth-child(1) > a"))) return acc;
const a = e.querySelector("table > tbody > tr > td:nth-child(1) > a");
const title = a.getAttribute("title").trim(), pageHref = (new URL(a.getAttribute("href"), pageUrl)).searchParams.get("ofertaDisciplinaId")
if (pageHref) acc.push({ title, id: pageHref });
return acc;
}, []);
}

@ -3,6 +3,7 @@ import { Got } from "./lib/colabora_req.js";
export enum AulasType {
"Portfólio" = "1",
"Tele Aula" = "2",
"Leitura" = "3",
"Web Aula" = "4",
"Avaliação Virtual" = "6",
"Prova Presencial" = "13",
@ -13,10 +14,28 @@ export enum AulasType {
"Atividade Diagnóstica" = "23",
"Atividade de Aprendizagem" = "24",
"Prova Prática Presencial" = "26",
"Engajamento AVA" = "27",
"Desafio Nota Máxima" = "28",
"Acelere Sua Carreira" = "31",
};
export const AulasTypeIDs: readonly (AulasType[number])[] = Object.freeze([
"1",
"2",
"4",
"6",
"13",
"14",
"16",
"17",
"22",
"23",
"24",
"26",
"28",
"31"
]);
export type Boletim = {
detalheBoletimDto: {
detalheEngajamentoDtoList: {
@ -82,7 +101,6 @@ export async function Get(got: Got, matricula: string, disciplina: string) {
boletimUrl.searchParams.set("ofertaDisciplinaId", disciplina);
const body: Boletim = (await got<Boletim>(boletimUrl, { responseType: "json" })).body;
const media = body.map(body => {
const Acts = Object.keys(body.detalheBoletimDto.detalheDisciplinarDtoList).map<{type: AulasType, pontos: number}>((type: AulasType) => {
if (type === AulasType["Avaliação Virtual"]) {

@ -1,8 +1,12 @@
import path from "path";
import util from "node:util"
import { ColaborareadRequest } from "./lib/colabora_req.js";
import { AulasType, AulasTypeIDs } from "./boletim.js";
import { writeFile } from "fs/promises";
export default Get;
export async function Get(gotdom: ColaborareadRequest["gotDom"]) {
const { window: { document } } = (await gotdom("https://www.colaboraread.com.br/index/index")).jsdoc;
export async function Get(colareq: ColaborareadRequest) {
const { window: { document } } = (await colareq.gotDom("https://www.colaboraread.com.br/index/index")).jsdoc;
document.querySelector("#navbar-content-aluno-cursos > div > div:nth-child(1) > div")
return Array.from(document.querySelectorAll("#navbar-content-aluno-cursos > div > div")).reduce<{ name: string; semestres: {semestreID: string; title: string;}[] }[]>((acc, e) => {
if (!(e.querySelector("div > div:nth-child(2) > h3"))) return acc;
@ -11,13 +15,159 @@ export async function Get(gotdom: ColaborareadRequest["gotDom"]) {
// Title
const title = (e.querySelector("div > div:nth-child(2) > h3") as HTMLHeadingElement).getAttribute("title");
// Push to array
acc.push({
// retornar os cursos
return acc.concat({
name: title,
semestres: Array.from<HTMLOptionElement>(e.querySelectorAll("div > div:nth-child(1) > div:nth-child(3) > form > div:nth-child(1) > select > option")).map(({ value, innerText, innerHTML }) => ({ semestreID: value, title: (innerText||innerHTML).trim() })).reverse()
});
}, []);
}
// retornar os cursos
export namespace Aulas {
export async function Get(colareq: ColaborareadRequest, semestre: string) {
const dashboard = new URL("https://www.colaboraread.com.br/aluno/dashboard/index"); dashboard.searchParams.set("matriculaId", semestre);
await colareq.gotDom(dashboard);
const { url: pageUrl, jsdoc } = (await colareq.gotDom(dashboard));
const { window: { document } } = jsdoc;
console.log(pageUrl.toString());
await writeFile(String().concat("./tmp_data/aulas_", semestre, ".html"), jsdoc.serialize())
return Array.from(document.querySelectorAll("#navbar-content-aluno-pda > ul > li")).reduce<{ title: string; id: string }[]>((acc, e) => {
if (!(e.querySelector("table > tbody > tr > td:nth-child(1) > a"))) return acc;
const a = e.querySelector("table > tbody > tr > td:nth-child(1) > a");
const title = a.getAttribute("title").trim(), pageHref = (new URL(a.getAttribute("href"), pageUrl)).searchParams.get("ofertaDisciplinaId")
if (pageHref) return acc.concat({ title, id: pageHref });
return acc;
}, []);
}
};
export namespace Atividades {
export interface Atividade {
type: string;
title: string;
id: string;
};
export interface Teleaula extends Atividade {
type: "Teleaula";
files: {
title: string;
url: URL;
}[];
videos: {
codigoMidia: string;
mediaType: string;
atividadeOfertaId: number;
exemplarId?: string;
mdsUrl?: string;
}[];
}
export interface Portfolio extends Atividade {
type: "Portfólio";
Dates: Record<string, Date[]>;
files: {
title: string;
url: URL;
}[];
historico?: {
type: string;
text: string;
description?: string;
date?: Date;
}[];
}
export async function Get(colareq: ColaborareadRequest, semestre: string, id: string): Promise<(Atividade|Teleaula|Portfolio)[]> {
const indexURL = new URL(path.posix.join("/aluno/timeline/index", semestre).concat("?ofertaDisciplinaId=", id), "https://www.colaboraread.com.br");
const { jsdoc, url: pageUrl } = (await colareq.gotDom(indexURL));
const { window: { document } } = jsdoc;
return Promise.all(Array.from(document.querySelectorAll("#js-activities-container > li")).map(async act => {
console.log(Array.from(act.classList.values()));
if (AulasTypeIDs.some(s => act.classList.contains(String().concat("tipo-", s)))) {
const type = AulasTypeIDs.find(s => act.classList.contains(String().concat("tipo-", s)));
if (type === AulasType["Portfólio"]) {
const portUrl = new URL(act.querySelector("div.timeline-panel > div > div.timeline-body > div.row.form-group > div > a").getAttribute("href"), pageUrl);
const { jsdoc } = await colareq.gotDom(portUrl);
const { window: { document } } = jsdoc;
const port: Portfolio = Object.create({});
port.type = "Portfólio";
port.id = portUrl.searchParams.get("atividadeDisciplinaId");
port.title = act.querySelector("div.timeline-panel > div > h4 > small").textContent.trim();
port.files = Array.from(document.querySelectorAll("#conteudo > div > div:nth-child(2) > div.col-md-4 > div > div.panel-body > ul:nth-child(4) > li")).map(s => s.querySelector("a")).filter(Boolean).map(s => ({
title: s.getAttribute("name")||s.getAttribute("title"),
url: new URL(s.getAttribute("href"), portUrl)
}));
const daySplit = (s: string) => { const [day, month, yeat] = s.split("/"); return String().concat(month, "/", day, "/", yeat); }
port.Dates = {};
Array.from(document.querySelectorAll("#conteudo > div > div:nth-child(2) > div.col-md-4 > div:nth-child(1) > div.panel-body > ul:nth-child(1) > li")).forEach((doc): any => {
const text = doc.textContent.trim();
let index: number;
if ((index = text.indexOf(":")) >= 0) {
const key = text.slice(0, index).trim();
const [ day1, hor1,, day2, hor2 ] = text.slice(index+1).trim().split(/\s+/);
if (!day2) return port.Dates[key] = [ new Date(String().concat(daySplit(day1), " ", hor1, " GMT-3")) ];
port.Dates[key] = [ new Date(String().concat(daySplit(day1), " ", hor1, " GMT-3")), new Date(String().concat(daySplit(day2), " ", hor2, " GMT-3")) ];
}
}, []);
if (document.querySelector("#conteudo > div > div:nth-child(2) > div.col-md-8 > div")) {
port.historico = [];
Array.from(document.querySelectorAll("#conteudo > div > div:nth-child(2) > div.col-md-8 > div")).forEach(a => {
if (a.querySelector("div:nth-child(1) > h5")) {
const dd = new Date(daySplit(a.querySelector("div:nth-child(1) > h5 > abbr").getAttribute("title")));
const status = a.querySelector("div:nth-child(1) > h5 > span");
port.historico.push({
type: status.textContent.trim(),
date: dd,
description: status.getAttribute("title"),
text: a.querySelector("div:nth-child(2) > p").textContent.trim(),
});
return;
}
port.historico.push({
type: String(a.querySelector("div:nth-child(1) > p:nth-child(1) > span")?.textContent).trim(),
date: new Date(daySplit(a.querySelector("div:nth-child(1) > p:nth-child(2) > abbr").getAttribute("title").trim())),
text: String(a.querySelector("div:nth-child(2) > p")?.textContent).trim(),
});
});
}
return port;
} else if (type === AulasType["Leitura"]) {
await writeFile(String().concat("./tmp_data/", type, ".html"), act.innerHTML);
} else if (type === AulasType["Conteúdo Web"]) {
} else if (type === AulasType["Tele Aula"]) {
const teleUrl = new URL(act.querySelector("div.timeline-panel > div > div.timeline-body > div.row.form-group > div > a").getAttribute("href"), pageUrl);
const { jsdoc } = await colareq.gotDom(teleUrl);
const { window: { document } } = jsdoc;
const tele: Teleaula = Object.create({});
tele.type = "Teleaula";
tele.id = teleUrl.searchParams.get("atividadeDisciplinaId");
tele.title = act.querySelector("div.timeline-panel > div > h4 > small").textContent.trim();
tele.files = Array.from(act.querySelectorAll("div.timeline-panel > div > div.timeline-body > ul > li")).map<HTMLAnchorElement>(s => s.querySelector("small > a")).filter(Boolean).map(s => ({ title: s.getAttribute("title")||s.getAttribute("name"), url: new URL(s.href, pageUrl) }));
const ids = Array.from(document.querySelectorAll("#conteudo > div > div > div.col-md-8.panel.mb-30 > div:nth-child(2) > div > div > div")).map(div => {
/** codigoMidia, mediaType, matriculaId, atividadeOfertaId, exemplarId */
let media: { codigoMidia: string, mediaType: string, atividadeOfertaId: number, exemplarId?: string };
// @ts-ignore
function playVideosMensagem(codigoMidia, mediaType, _matriculaId, atividadeOfertaId, exemplarId) { media = {codigoMidia, mediaType, atividadeOfertaId, exemplarId}; }
eval(div.querySelector("div > div > a").getAttribute("onclick"));
return media;
});
tele.videos = ids.map(s => ({ ...s, mdsUrl: util.format("https://mdstrm.com/video/%s.json", s.codigoMidia), }));
return tele;
}
}
return null;
})).then(s => s.filter(Boolean));
}
};

@ -1,10 +1,9 @@
import "./lib/db_connect.js";
import request, { ColaborareadRequest } from "./lib/colabora_req.js";
import { encrypt } from "./lib/password.js";
import neste from "neste";
import boletim from "./boletim.js";
import Dashboard from "./dashboard.js";
import Atividade from "./atividades.js";
import Dashboard, { Atividades, Aulas } from "./dashboard.js";
import request, { ColaborareadRequest } from "./lib/colabora_req.js";
import "./lib/db_connect.js";
import { encrypt } from "./lib/password.js";
const app = neste();
// Listen HTTP Server
@ -43,6 +42,7 @@ app.use(async (req, res, next): Promise<any> => {
]
});
}
} else if (req.headers.authorization.toLowerCase().startsWith("bearer ")) {
} else if (req.headers.authorization.toLowerCase().startsWith("token ")) {
req.headers.authorization.slice(6).trim();
}
@ -59,20 +59,14 @@ app.use(async (req, res, next): Promise<any> => {
});
// Semestres
app.get("/", async (req, res) => res.json(await Dashboard(req.Got.gotDom)));
// Aulas
app.get("/aula/:semestre", async (req, res) => res.json(await Atividade(req.Got.gotDom, req.params.semestre)));
app.get("/aula/:semestre/:displina");
app.get("/", async (req, res) => res.json(await Dashboard(req.Got)));
// Boletim
app.get("/boletim", async (req, res) => {
res.json(await Promise.all((await Dashboard(req.Got.gotDom)).map(async s => ({ name: s.name, semestres: await Promise.all(s.semestres.map(async s => await Promise.all((await Atividade(req.Got.gotDom, s.semestreID)).map(async s => ({ title: s.title, boletim: await boletim(req.Got.got, req.params.semestre, s.id), }))))) }))));
});
app.get("/boletim/:semestre", async (req, res) => {
res.json(await Promise.all((await Atividade(req.Got.gotDom, req.params.semestre)).map(async s => ({ title: s.title, boletim: await boletim(req.Got.got, req.params.semestre, s.id), }))));
});
app.get("/boletim/:semestre/:displina", async (req, res) => {
const bot = await boletim(req.Got.got, req.params.semestre, req.params.displina);
return res.json(bot);
});
// Aulas
app.get("/aula/:semestre", async (req, res) => res.json(await Aulas.Get(req.Got, req.params.semestre)));
app.get("/aula/:semestre/:displina", async (req, res) => res.json(await Atividades.Get(req.Got, req.params.semestre, req.params.displina)));

@ -1,11 +1,11 @@
import { decrypt } from "./password.js";
import { JSDOM } from "jsdom";
import cookie from "cookie";
import got, { Got, OptionsOfBufferResponseBody, OptionsOfTextResponseBody } from "got";
import { IncomingHttpHeaders } from "http";
import { JSDOM } from "jsdom";
import { decrypt } from "./password.js";
export type { Got };
export { remoteRequest };
export type { Got };
export interface ColaborareadRequest {
cookies: Map<string, Record<string, string>>;
@ -31,27 +31,31 @@ export default async function remoteRequest(username: string, password: string):
throwHttpErrors: true,
encoding: "utf8",
headers: {
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
"accept-language": "pt-BR,pt;q=0.9,en;q=0.8,en-US;q=0.7",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-ch-ua": "\"Google Chrome\";v=\"119\", \"Chromium\";v=\"119\", \"Not?A_Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-site": "none",
"sec-fetch-mode": "navigate",
"sec-fetch-user": "?1",
"sec-fetch-dest": "document",
// "accept-encoding": "gzip, deflate, br",
"accept-language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
"sec-fetch-mode": "navigate",
"sec-fetch-site": "same-origin",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537."
},
cookieJar: {
async getCookieString(url: string) { const data = localCookie.get((new URL(url)).hostname)||{}; return Object.keys(data).reduce<string>((acc, k) => acc.concat(";", k, "=", data[k]), ""); },
async getCookieString(url: string) {
const data = localCookie.get((new URL(url)).hostname)||{};
return Object.keys(data).reduce<string>((acc, k) => acc.concat(";", k, "=", data[k]), "");
},
async setCookie(rawCookie: string, url: string) {
const host = (new URL(url)).hostname
if (!(localCookie.has(host))) localCookie.set(host, {});
const cookieData = cookie.parse(rawCookie);
([ "Expires", "Max-Age", "Domain", "Path", "Secure", "HttpOnly", "SameSite" ]).forEach(k => delete cookieData[k]);
for (const k in cookieData) {
if (([ "expires", "max-age", "domain", "path", "secure", "httponly", "samesite" ]).includes(k.toLowerCase())) continue;
if (cookieData[k]) localCookie.get(host)[k] = cookieData[k];
}
}