const express = require("express"); const bodyParser = require("body-parser"); const cors = require("cors"); const path = require("path"); const cp = require("child_process"); const fs = require("fs"); const multer = require("multer"); const sleep = require("sleep-promise"); const PORT = process.env.PORT || 3000; const BOT_USERNAME = process.env.BOT_USERNAME || console.error("Missing env BOT_USERNAME") || process.exit(1); const BOT_PASSWORD = process.env.BOT_PASSWORD || console.error("Missing env BOT_PASSWORD") || process.exit(1); const BOT_DISPLAY_NAME = process.env.BOT_DISPLAY_NAME || "Puppet"; // Global state: var attachedRooms = {}; async function attachToRoom(roomUrl) { // Get uid: const roomUid = roomUrl.split("/")[4]; // Make sure that no room get's attached to twice if (attachedRooms[roomUid] !== undefined) return; else attachedRooms[roomUid] = false; // Subprocess setup const subProcess = cp.fork(`${__dirname}/room_attacher.js`); subProcess.send({ eventName: "attachTo", data: { username: BOT_USERNAME, password: BOT_PASSWORD, displayName: BOT_DISPLAY_NAME, roomUrl, screenShotPath: "room_previews/" + roomUid + ".jpeg", }, }); const room = { subProcess, uid: roomUid, url: roomUrl, userCount: -1, users: [], name: "???", detach: async () => { try { subProcess.send({ eventName: "detach", data: null }); await sleep(500); subProcess.kill(0); } catch (error) { subProcess.kill(1); } }, sendMessage: (content) => { subProcess.send({ eventName: "sendMessage", data: { content } }); }, }; subProcess.on("message", (m) => { if (m.eventName === "roomInfo") { room.userCount = m.data.count; room.users = m.data.users; room.name = m.data.name; } else if (m.eventName === "sessionClosed") { attachedRooms[roomUid] = undefined; } else console.log("Room[" + roomUid + "]:", m); }); // return a room return room; } // Web server setup const app = express(); app.use(express.static("public")); app.use(bodyParser.json()); app.use(cors()); const fileUpload = multer({ dest: "uploads/", limits: { fileSize: 30 * 1000 * 1000, // ~= 30 MB }, }); app.get("/api/preview/:roomUid", (req, res) => { // console.debug('[Debug] GET "/api/preview/:roomUid"'); const constructedPath = path.join( __dirname, "room_previews", req.params.roomUid.split("/")[0].split("\\")[0] + ".jpeg" ); // console.debug('[Debug] constructed path:',constructedPath); res.setHeader("Cache-Control", "max-age=2"); res.sendFile(constructedPath); }); app.post("/api/attach", async (req, res) => { const roomUrl = req.body.url; console.debug("Received request to attach to room", roomUrl); const room = await attachToRoom(roomUrl); attachedRooms[room.uid] = room; res.send({ uid: room.uid, url: roomUrl, }); }); app.post("/api/broadcast", (req, res) => { const content = "📢 Broadcast 📢\n\n" + req.body.content; console.debug("Received request to broadcast message", content); for (const roomUid of Object.keys(attachedRooms)) { if ( attachedRooms[roomUid] === undefined || attachedRooms[roomUid] === false ) continue; try { attachedRooms[roomUid].sendMessage(content); } catch (error) { console.warn('Could not send message "' + content + '" to room', roomUid); } } res.end({}); }); app.get("/api/attachedRooms", (req, res) => { const roomList = []; if (attachedRooms === undefined) { console.error("attachedRooms is undefined"); process.exit(1); } for (const key of Object.keys(attachedRooms)) { if (attachedRooms[key] === undefined) continue; roomList.push({ uid: attachedRooms[key].uid, url: attachedRooms[key].url, name: attachedRooms[key].name, userCount: attachedRooms[key].userCount, users: attachedRooms[key].users, }); } res.send(roomList); }); app.post("/api/detach", async (req, res) => { const roomUid = req.body.uid; if (attachedRooms[roomUid] !== undefined) { await attachedRooms[roomUid].detach(); attachedRooms[roomUid] = undefined; } res.send({}); }); app.post("/api/bulkcreate", async (req, res) => { const { prefix, amount } = req.body; }); app.post( "/api/uploadPresentation", fileUpload.array("presentation", 10), async (req, res, next) => { console.debug(req.files); res.status(200).send({}).end(); return; } ); try { const autoAttachRoomsPath = path.join(__dirname, "autoAttach.json"); const urlList = JSON.parse(fs.readFileSync(autoAttachRoomsPath)); const attach = async function (roomUrl) { try { const room = await attachToRoom(roomUrl); attachedRooms[room.uid] = room; } catch (error) {} }; for (const url of urlList) { attach(url); } } catch (error) { console.error("Could not read autoAttachroomList:", error); } try { console.debug("[Debug] Running in", process.cwd()); console.debug("[Debug] Running as", __filename); app.listen(PORT, () => { console.log(`Listening on port ${PORT}`); }); } catch (error) { console.error("Could not start to listen on port", PORT); throw error; }