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, probably signed in.");
}
try {
// Join and load room
await page.click("button#room-join");
} catch (error) {
try {
await page.click('input[value="Join"]');
} catch (error2) {
try {
await page.click('input[value="Start"]');
} catch (error3) {
try {
await page.click('input[value="Starten"]');
} catch (error4) {
await page.click('input[value="Teilnehmen"]');
}
}
}
}
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);
};