From e8ca1d29fb91ef168bd331ec14a3112a093d40a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Marx?= Date: Thu, 17 Dec 2020 16:43:26 +0100 Subject: [PATCH 1/3] Ability to enter a pre-existing room by room name --- .gitignore | 1 + README.md | 13 ++++++- config/config.json | 5 +-- lib/api.js | 13 +++++-- lib/conf.js | 4 ++- lib/pool.js | 3 ++ lib/run.js | 28 +++++++++++---- lib/util.js | 86 ++++++++++++++++++++++++++++++++++++++++++++-- package-lock.json | 13 +++++++ package.json | 3 +- 10 files changed, 153 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 4c96c08..ab7bb3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules .env .idea +run.sh diff --git a/README.md b/README.md index e8d216a..5e0ae06 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ At the `.env` file you just copied, set: ``` BIGBLUEBOT_HOST=https://your.bigbluebutton.server ``` - - [optional] room name or meetingID + - [optional] room meetingID ``` BIGBLUEBOT_ROOM=yourbigbluebuttonroomidentifier ``` @@ -30,6 +30,17 @@ If you would like the bots to join an existing room, you may fill out the password variables and use the `meetingID` value as `BIGBLUEBOT_ROOM` To find out these data, you may call the `getMeetings` route of your BBB instance as described [here](https://docs.bigbluebutton.org/dev/api.html#getmeetings). + +Attention: this is NOT the room ID of a room made bei Greenlight! If you want to join an existing room +(this is good and impressive for watching the test in headless mode), use the next option. + - [optional] room name +``` +BIGBLUEBOT_NAME=yourbigbluebuttonroomname +``` +If you want to join a preexisting room, then use this option. YOU HAVE TO PROVIDE the serverAPI secret +to work with this. When you give the (hopefully) unique room name and the server secret then all +other credentials are extracted from the `getMeetings` route of the BBB instance. +Here you can join any room only by knowing his meetingName. - [optional] the `attendeePW` and `moderatorPW` as shown in getMeetings ``` BIGBLUEBOT_ATTENDEE_PW=yourattendeepassword diff --git a/config/config.json b/config/config.json index 09b536e..66574e4 100644 --- a/config/config.json +++ b/config/config.json @@ -12,7 +12,8 @@ }, "meeting": { "param": "meetingname", - "name": "Demo Meeting" + "room": "Demo Meeting", + "name": "This is Demo Meeting Room" } }, "api": { @@ -71,7 +72,7 @@ "type": 100 }, "timeout": { - "selector": 60000 + "selector": 1800000 }, "logger": { "level": "info" diff --git a/lib/api.js b/lib/api.js index c44d2c6..ed68e0d 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,5 +1,6 @@ const sha1 = require('crypto-js/sha1'); const conf = require('./conf'); +const logger = require('./logger'); const { config } = conf; @@ -35,6 +36,7 @@ const getURL = (action, params, options) => { const api = config.api.path; const url = `${host}/${api}/${action}?${query}&checksum=${checksum}`; + logger.debug(url); return url; }; @@ -47,7 +49,7 @@ const calculateChecksum = (action, query, options) => { const getCreateURL = (options) => { const params = { - meetingID: options.room || config.url.meeting.name, + meetingID: options.room || config.url.meeting.room, record: true, moderatorPW: getPassword('moderator', options), attendeePW: getPassword('attendee', options), @@ -58,7 +60,7 @@ const getCreateURL = (options) => { const getEndURL = (options) => { const params = { - meetingID: options.room || config.url.meeting.name, + meetingID: options.room || config.url.meeting.room, password: getPassword('moderator', options), }; @@ -72,7 +74,7 @@ const getJoinURL = (username, options) => { } const params = { - meetingID: options.room || config.url.meeting.name, + meetingID: options.room || config.url.meeting.room, fullName: username, password: password, }; @@ -80,8 +82,13 @@ const getJoinURL = (username, options) => { return getURL('join', params, options); }; +const getMeetingsURL = (options) => { + return getURL('getMeetings', {}, options); +}; + module.exports = { getCreateURL, getEndURL, getJoinURL, + getMeetingsURL, }; diff --git a/lib/conf.js b/lib/conf.js index 8cb1f67..b5fde40 100644 --- a/lib/conf.js +++ b/lib/conf.js @@ -9,6 +9,7 @@ const { BIGBLUEBOT_HOST, BIGBLUEBOT_SECRET, BIGBLUEBOT_ROOM, + BIGBLUEBOT_NAME, BIGBLUEBOT_ATTENDEE_PW, BIGBLUEBOT_MODERATOR_PW, BIGBLUEBOT_BOTS, @@ -24,7 +25,8 @@ const { } = process.env; if (BIGBLUEBOT_HOST) custom.url.host = BIGBLUEBOT_HOST; -if (BIGBLUEBOT_ROOM) custom.url.meeting.name = BIGBLUEBOT_ROOM; +if (BIGBLUEBOT_ROOM) custom.url.meeting.room = BIGBLUEBOT_ROOM; +if (BIGBLUEBOT_NAME) custom.url.meeting.name = BIGBLUEBOT_NAME; if (BIGBLUEBOT_SECRET) custom.api.secret = BIGBLUEBOT_SECRET; if (BIGBLUEBOT_BOTS) custom.bot.population = parseInt(BIGBLUEBOT_BOTS); if (BIGBLUEBOT_WAIT) custom.bot.wait = parseInt(BIGBLUEBOT_WAIT); diff --git a/lib/pool.js b/lib/pool.js index dc06469..c7590a8 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -4,6 +4,8 @@ const conf = require('./conf'); const { browser, bot, data } = conf.config; +require('events').EventEmitter.defaultMaxListeners = 0 + const args = [ `--lang=${browser.lang}`, `--disable-dev-shm-usage`, @@ -23,6 +25,7 @@ const factory = { } else { return await puppeteer.launch({ headless, + ignoreHTTPSErrors: true, executablePath: path, args, }); diff --git a/lib/run.js b/lib/run.js index 175150c..bf328c1 100644 --- a/lib/run.js +++ b/lib/run.js @@ -3,7 +3,7 @@ const pool = require('./pool'); const conf = require('./conf'); const logger = require('./logger'); -const { api, bot, url, misc } = conf.config; +var { api, bot, url, misc } = conf.config; const dependencies = (options) => { if (!url.host && !options.host) { @@ -22,12 +22,28 @@ const run = async (actions, options = {}) => { logger.info(`Life: ${bot.life / 1000} seconds`); // Assume a private room if the server secret was configured - const private = api.secret || options.secret; + const privileged = options.secret || api.secret; - // Create the meeting if not created yet - if (private) { - const success = await util.create(options); - if (!success) return null; + // Create the meeting if not created yet or use a preexisting one + if (privileged) { + let meet; + // let's see if we can dig out missing information when we ask the server via 'getMeeting' + // first we look for a named meeting + if (url.meeting.name) { + meet = await util.findMeetingByName(url.meeting.name, options); + if (meet) { + url.meeting.room = meet.meetingID; + api.password.moderator = meet.moderatorPW; + api.password.attendee = meet.attendeePW; + } + } + + // ok this might be inefficient if a name was provided, but who cares + meet = await util.findMeetingByID(url.meeting.room, options); + if (!meet) { + const success = await util.create(options); + if (!success) return null; + } } // Fetch the UI labels from locale diff --git a/lib/util.js b/lib/util.js index 8ec86d0..3cf6c08 100644 --- a/lib/util.js +++ b/lib/util.js @@ -3,6 +3,7 @@ const faker = require('faker'); const conf = require('./conf'); const logger = require('./logger'); const api = require('./api'); +const convert = require('xml-js'); const { config } = conf; const { timeout } = config; @@ -14,7 +15,7 @@ const random = collection => collection[Math.floor(Math.random() * collection.le const getDemoJoinURL = (username, options) => { const user = `${config.url.user.param}=${encodeURI(username)}`; const moderator = `${config.url.moderator.param}=${options.moderator !== undefined ? options.moderator : config.url.moderator.value}`; - const meeting = `${config.url.meeting.param}=${encodeURI(options.room || config.url.meeting.name)}`; + const meeting = `${config.url.meeting.param}=${encodeURI(options.room || config.url.meeting.room)}`; return `${options.host || config.url.host}/${config.url.demo}&${user}&${moderator}&${meeting}`; }; @@ -29,6 +30,84 @@ const url = (username, options) => { return getDemoJoinURL(username, options); }; +// https://github.com/nashwaan/xml-js/issues/53 +function nativeType(value) { + var nValue = Number(value); + if (!isNaN(nValue)) { + return nValue; + } + var bValue = value.toLowerCase(); + if (bValue === 'true') { + return true; + } else if (bValue === 'false') { + return false; + } + return value; +} + +const removeJsonTextAttribute = function(value, parentElement) { + try { + const parentOfParent = parentElement._parent; + const pOpKeys = Object.keys(parentElement._parent); + const keyNo = pOpKeys.length; + const keyName = pOpKeys[keyNo - 1]; + const arrOfKey = parentElement._parent[keyName]; + const arrOfKeyLen = arrOfKey.length; + if (arrOfKeyLen > 0) { + const arr = arrOfKey; + const arrIndex = arrOfKey.length - 1; + arr[arrIndex] = nativeType(value); + } else { + parentElement._parent[keyName] = nativeType(value); + } + } catch (e) {} +}; + +const meetings = async (options) => { + if (options.secret || config.api.secret) { + const url = api.getMeetingsURL(options); + + let meets; + await axios.get(url).then(response => { + meets = convert.xml2js(response.data, { compact: true, nativeType: false, textFn: removeJsonTextAttribute }); + }).catch(error => { + logger.error(error); + }); + logger.debug(meets.response.meetings.meeting); + return meets.response.meetings.meeting; + } else { + logger.info('Set BBB-secret to get the meetings') + } + + return undefined; +}; + +const findMeetingByName = async (name, options) => { + let ret; + let all = await meetings(options); + all.forEach(function (item) { + if (item.meetingName && item.meetingName === name) { + logger.info("Found meeting by name: " + name) + ret = item; + } + }); + + return ret; +}; + +const findMeetingByID = async (id, options) => { + let ret; + let all = await meetings(options); + all.forEach(function (item) { + if (item.meetingID && item.meetingID === id) { + logger.info("Found meeting by ID: " + id) + ret = item; + } + }); + + return ret; +}; + const once = async (page, event, callback) => { return new Promise((resolve, reject) => { let fired = false; @@ -137,9 +216,12 @@ module.exports = { random, generateText, localize, + meetings, + findMeetingByName, + findMeetingByID, join: async (page, locale, options) => { const username = generateUsername(); - logger.info(`${username}: join ${options.host || config.url.host} at ${options.room || config.url.meeting.name}`); + logger.info(`${username}: join ${options.host || config.url.host} at ${options.room || config.url.meeting.room}`); const { width, height } = config.browser.window; await page.setViewport({ width, height }); await page.goto(url(username, options)); diff --git a/package-lock.json b/package-lock.json index 8ae7dee..bb4a425 100644 --- a/package-lock.json +++ b/package-lock.json @@ -362,6 +362,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -422,6 +427,14 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==" }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index e8086c8..66bc2d8 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "dotenv": "^8.2.0", "faker": "^5.1.0", "generic-pool": "^3.7.1", - "puppeteer": "^5.5.0" + "puppeteer": "^5.5.0", + "xml-js": "^1.6.11" }, "author": "Pedro Beschorner Marin ", "license": "MIT", From 8f7cd4902e45666ee89e34bdb91805ceb6b0d9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Marx?= Date: Thu, 17 Dec 2020 16:47:14 +0100 Subject: [PATCH 2/3] Small text corrections --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e0ae06..84d2d10 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ BIGBLUEBOT_NAME=yourbigbluebuttonroomname If you want to join a preexisting room, then use this option. YOU HAVE TO PROVIDE the serverAPI secret to work with this. When you give the (hopefully) unique room name and the server secret then all other credentials are extracted from the `getMeetings` route of the BBB instance. -Here you can join any room only by knowing his meetingName. +Here you can join any room only by knowing the `meetingName` like in getMeetings answer. - [optional] the `attendeePW` and `moderatorPW` as shown in getMeetings ``` BIGBLUEBOT_ATTENDEE_PW=yourattendeepassword From b377beec615a90f422d43d752a7fa9d1c0f4d7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Marx?= Date: Mon, 21 Dec 2020 20:17:37 +0100 Subject: [PATCH 3/3] Changed function to always return an array --- lib/util.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util.js b/lib/util.js index 3cf6c08..f486e03 100644 --- a/lib/util.js +++ b/lib/util.js @@ -74,12 +74,12 @@ const meetings = async (options) => { logger.error(error); }); logger.debug(meets.response.meetings.meeting); - return meets.response.meetings.meeting; + return [].concat(meets.response.meetings.meeting); } else { logger.info('Set BBB-secret to get the meetings') } - return undefined; + return []; }; const findMeetingByName = async (name, options) => {