import axios from "axios"; import crypto from "node:crypto"; import { log } from "../logs.js"; /* This BOLLE (https://bolle-software.de/) parser tries to download the latest substitution plan HTML files from the "Pinnwand". It uses the same API that the mobile app (v0.3.6) uses. To get your login credentials you need to log into your BOLLE account and register a new device in the settings (/einstellungen/geraete_personal). Click on "Manuelle Logindaten Einblenden" and use "ID" as apiUser and "Token" as apiKey. You need to make at least one request with this API token before closing the registration window or else the token will be invalidated immediately. */ // Files to download from the "Pinnwand" const filenames = ["vp_heute", "vp_morgen"]; export class BolleClient { constructor(bolleInstance, apiUser, apiKey) { this.bolleInstance = bolleInstance; this.apiUser = apiUser; this.apiKey = apiKey; } async getFiles() { try { let contents = []; for (let file of filenames) { contents.push(await this.getFile(file)); } return contents; } catch (error) { log("Parser / Bolle", "Error getting data: " + error); return []; } } async getFile(filename) { // Generate the BOLLE api payload let payload = { api_payload: JSON.stringify({ method: "vertretungsplan_html", payload: { content: filename }, }), }; // Generate request headers let headers = this.buildRequestHeaders(payload.api_payload); // Send the POST request let response = await axios.post(this.getRequestUrl(), payload, { headers, }); // The server responds with a json object // containing the base64 encoded html data let base64 = response.data["html_base64"]; // Decode the base64 data using the latin1 (ISO 8859-1) character set return Buffer.from(base64, "base64").toString("latin1"); } getRequestUrl() { // The API that the bolle mobile app uses is available at /app/basic return `https://${this.bolleInstance}/app/basic`; } buildRequestHeaders(payload) { // Bolle needs the sha1 hash of the payload // to be set as the "b-hash" header let hash = crypto.createHash("sha1"); hash.update(payload); return { Accept: "application/json", // Set the auth headers "X-Auth-User": this.apiUser, "X-Auth-Token": this.apiKey, "App-Version": "4", // Set the hash "B-Hash": hash.digest("hex"), "Content-Type": "application/json", Connection: "Keep-Alive", "Accept-Encoding": "gzip", "User-Agent": "okhttp/4.9.2", }; } }