Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to join a pre-existing room #20

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
.env
.idea
run.sh
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,25 @@ 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
```
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 the `meetingName` like in getMeetings answer.
- [optional] the `attendeePW` and `moderatorPW` as shown in getMeetings
```
BIGBLUEBOT_ATTENDEE_PW=yourattendeepassword
Expand Down
5 changes: 3 additions & 2 deletions config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
},
"meeting": {
"param": "meetingname",
"name": "Demo Meeting"
"room": "Demo Meeting",
"name": "This is Demo Meeting Room"
}
},
"api": {
Expand Down Expand Up @@ -71,7 +72,7 @@
"type": 100
},
"timeout": {
"selector": 60000
"selector": 1800000
},
"logger": {
"level": "info"
Expand Down
13 changes: 10 additions & 3 deletions lib/api.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const sha1 = require('crypto-js/sha1');
const conf = require('./conf');
const logger = require('./logger');

const { config } = conf;

Expand Down Expand Up @@ -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;
};

Expand All @@ -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),
Expand All @@ -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),
};

Expand All @@ -72,16 +74,21 @@ const getJoinURL = (username, options) => {
}

const params = {
meetingID: options.room || config.url.meeting.name,
meetingID: options.room || config.url.meeting.room,
fullName: username,
password: password,
};

return getURL('join', params, options);
};

const getMeetingsURL = (options) => {
return getURL('getMeetings', {}, options);
};

module.exports = {
getCreateURL,
getEndURL,
getJoinURL,
getMeetingsURL,
};
4 changes: 3 additions & 1 deletion lib/conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
BIGBLUEBOT_HOST,
BIGBLUEBOT_SECRET,
BIGBLUEBOT_ROOM,
BIGBLUEBOT_NAME,
BIGBLUEBOT_ATTENDEE_PW,
BIGBLUEBOT_MODERATOR_PW,
BIGBLUEBOT_BOTS,
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions lib/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand All @@ -23,6 +25,7 @@ const factory = {
} else {
return await puppeteer.launch({
headless,
ignoreHTTPSErrors: true,
executablePath: path,
args,
});
Expand Down
28 changes: 22 additions & 6 deletions lib/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
86 changes: 84 additions & 2 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}`;
};
Expand All @@ -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 [].concat(meets.response.meetings.meeting);
} else {
logger.info('Set BBB-secret to get the meetings')
}

return [];
};

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;
Expand Down Expand Up @@ -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));
Expand Down
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 <pedrobmarin@gmail.com>",
"license": "MIT",
Expand Down