const puppeteer = require("puppeteer"); const sleep = require("sleep-promise"); module.exports.createFreshPage = async function createFreshPage() { const browser = await puppeteer.launch({ headless: true, defaultViewport: { width: 1200, height: 600, isLandscape: true, }, }); const context = await browser.createIncognitoBrowserContext(); return context.newPage(); }; module.exports.joinRoom = async function joinRoom( page, roomUrl, displayName = "Puppet" ) { await page.goto(roomUrl); console.log("joinRoom > Gone to start page for", roomUrl); try { // Set displayname for program await page.type("input.join-form", displayName); } catch (error) { console.warn("could not set displayname"); } try { // Join and load room await page.click("button#room-join"); } catch (error) { await page.click('input[value="Join"]'); } const dismissButton = await page.waitForSelector( 'button[aria-describedBy="modalDismissDescription"]' ); console.log("joinRoom > Joined room"); // Click audio choice panel away await dismissButton.click(); console.log("joinRoom > clicked audio choice away."); return page; }; module.exports.sendChatMessage = async function sendChatMessage( page, messageText ) { const chatInputHandle = await page.$("textarea#message-input"); const chatSendButtonHandle = await page.$( 'form > div > button[type="submit"]' ); // Send a text message await chatInputHandle.type(messageText); await chatSendButtonHandle.click(); console.log("sendChatMessage > sent Message"); }; module.exports.signIn = async function signIn( bbbUrl, username, password, page = null ) { // Create page if not done already if (page === null) { page = await createFreshPage(); } await page.goto(bbbUrl); await page.waitForSelector("a.sign-in-button"); const signInButton = await page.$("a.sign-in-button"); if (signInButton === null) { console.log( "SignIn > Can't find sign-in button: User is probably already authenticated." ); return page; } await page.click("a.sign-in-button"); await page.waitForNavigation(); console.log("SignIn > NAVIGATED TO LOGIN PAGE"); await page.type("input#session_username", username); await page.type("input#session_password", password); await page.click("input.signin-button"); console.log("SignIn > entered credentials"); await page.waitForNavigation(); console.log("SignIn > Probably logged in."); return page; }; module.exports.screenShotPresentationArea = async function screenShotPresentationArea( authenticatedPage, path ) { const presentationAreaHandle = await authenticatedPage.$("div#container"); presentationAreaHandle.screenshot({ path, type: "jpeg", quality: 33 }); }; module.exports.getUserCount = async function getUserCount(authenticatedPage) { const selector = 'div[role="complementary"] > div > div > h2'; const h2Handle = (await authenticatedPage.$$(selector))[2]; return h2Handle.evaluate( (node) => node.innerText.split("(")[1].split(")")[0] ); }; module.exports.getUserList = async function getUserList(authenticatedPage) { const users = []; const selector = 'div[class*="userItemContents"]'; const userHandles = await authenticatedPage.$$(selector); for (const userHandle of userHandles) { const avatarHandle = await userHandle.$('div[class*="avatar"]'); const classes = (await avatarHandle.evaluate((node) => node.className)) .split(" ") .map((wholeClass) => wholeClass.split("--")[0]) .filter((name) => name !== "avatar"); const userNameHandle = await userHandle.$( 'span[class*="userNameMain"] > span' ); const userName = await userNameHandle.evaluate((node) => node.innerText); users.push({ classes, name: userName, }); } return users; }; module.exports.getRoomName = async function getRoomName(authenticatedPage) { const roomNameHandle = await authenticatedPage.$( 'h1[class*="presentationTitle"]' ); if (roomNameHandle === null) { process.send({ eventName: "sessionClosed", data: null }); await sleep(100); process.exit(0); } return await roomNameHandle.evaluate((node) => node.innerText); }; module.exports.createRoom = async function createRoom( authenticatedPage, roomSettings ) { const pageUrl = await authenticatedPage.url(); const urlparts = pageUrl.split("/"); const url = urlparts[0] + "//" + urlparts[2]; const authenticityTokenHandle = await authenticatedPage.$( 'input[name="authenticity_token"]' ); const authenticityToken = await authenticityTokenHandle.evaluate( (node) => node.value ); await authenticatedPage.evaluate( async (url, authenticityToken, roomSettings) => { function urlencodeFormData(fd) { var s = ""; function encode(s) { return encodeURIComponent(s).replace(/%20/g, "+"); } for (var pair of fd.entries()) { if (typeof pair[1] == "string") { s += (s ? "&" : "") + encode(pair[0]) + "=" + encode(pair[1]); } } return s; } // Prepare form data const form = new FormData(); form.set("utf8", "✓"); form.set("authenticity_token", authenticityToken); for (const key of Object.keys(roomSettings)) { // Maybe fix this, because the POST format is weird if (typeof roomSettings[key] === "boolean") { form.append("room[" + key + "]", 0); if (roomSettings[key]) { form.append("room[" + key + "]", 1); } } else { form.set("room[" + key + "]", roomSettings[key]); } } form.set("room[auto_join]", "0"); form.set("commit", "Create+Room"); try { // Send request to create room const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, redirect: "follow", // manual, *follow, error body: urlencodeFormData(form), }); return response; } catch (error) { return error; } }, url + "/b", authenticityToken, roomSettings ); await authenticatedPage.goto(url); };