const EventEmitter = require("events"); module.exports.XSSBook = class XSSBook extends EventEmitter { constructor({ url, bridge_username, room }) { super(); this.url = url; this.bridge_username = bridge_username; this.room = room; this.tokens = {}; this.posts = {}; this.pollLoop(); } async api(url, method, data, token) { return await fetch(this.url + url, { method, headers: { "Cookie": token ? "auth=" + encodeURIComponent(token) : undefined, "Content-Type": data ? "application/json" : undefined, }, body: data ? JSON.stringify(data) : undefined, }); } async getUserToken(username) { if(username in this.tokens) { return this.tokens[username]; } let req = await this.api("/api/auth/register", "POST", { firstname: username, lastname: username === this.bridge_username ? "[bridge]" : "[matrix]", email: username, password: username, gender: username === this.bridge_username ? "matrix bridge" : "matrix user", day: 1, month: 1, year: 1970, }); if(req.ok) { const token = decodeURIComponent(/auth=([^;]+);/.exec(req.headers.get("set-cookie"))[1]); return this.tokens[username] = token; } console.log("couldn't register " + username + ":", await req.text()); req = await this.api("/api/auth/login", "POST", { email: username, password: username, }); if(req.ok) { const token = decodeURIComponent(/auth=([^;]+);/.exec(req.headers.get("set-cookie"))[1]); return this.tokens[username] = token; } console.log("couldn't login " + username + ":", await req.text()); throw new Error("Couldn't get token for " + username); } async apiUser(url, method, data, username) { const token = await this.getUserToken(username); return await this.api(url, method, data, token); } async getUsers(ids) { const req = await this.apiUser("/api/users/load", "POST", { ids, }, this.bridge_username); return await req.json(); } async post(username, content) { const res = await this.apiUser("/api/posts/create", "POST", { content, }, username); return (await res.json()).post_id; } async pollLoop() { let latest_post_id = 0, first = true; while(true) { await new Promise(r => setTimeout(r, 5000)); const req = await this.apiUser("/api/posts/page", "POST", { page: 0, }, this.bridge_username); const posts = await req.json(); if(!Array.isArray(posts) || !posts.length) { continue; } const new_posts = posts.filter(n => n.post_id > latest_post_id); if(!new_posts.length) { continue; } latest_post_id = Math.max(latest_post_id, ...new_posts.map(n => n.post_id)); if(first) { // ignore posts from the first sync first = false; continue; } const users_list = await this.getUsers(new_posts.map(n => n.user_id)); const users = {}; users_list.forEach(user => { users[user.user_id] = user; }); new_posts.forEach(post => { const user = users[post.user_id]; this.emit("post", post, user); }); } } }