bigbluebutton-api-js
Version:
A very simple Javascript library that generates links to all methods in BigBlueButton's API
222 lines (205 loc) • 8.07 kB
JavaScript
const jsSHA = require("jssha");
let indexOf = [].indexOf;
// Ruby-like include() method for Objects
const include = function(input, _function) {
var _match, _obj, key, value;
_obj = new Object;
_match = null;
for (key in input) {
value = input[key];
if (_function.call(input, key, value)) {
_obj[key] = value;
}
}
return _obj;
};
// creates keys without "custom_" and deletes the ones with it
const filterCustomParameters = function(params) {
var key, v;
for (key in params) {
v = params[key];
if (key.match(/^custom_/)) {
params[key.replace(/^custom_/, "")] = v;
}
}
for (key in params) {
if (key.match(/^custom_/)) {
delete params[key];
}
}
return params;
};
const noChecksumMethods = function() {
return ['setConfigXML', '/', 'enter', 'configXML', 'signOut'];
};
class BigBlueButtonApi {
// `url`: The complete URL to the server's API, e.g. `http://server.com/bigbluebutton/api`
// `salt`: The shared secret of your server.
// `debug`: Turn on debug messages, printed to `console.log`.
// `opts`: Additional options
constructor(url, salt, debug = false, opts = {}) {
var base;
this.url = url;
this.salt = salt;
this.debug = debug;
this.opts = opts;
if ((base = this.opts).shaType == null) {
base.shaType = 'sha1';
}
}
// Returna a list with the name of all available API calls.
availableApiCalls() {
return ['/', 'create', 'join', 'isMeetingRunning', 'getMeetingInfo', 'end', 'getMeetings', 'getDefaultConfigXML', 'setConfigXML', 'enter', 'configXML', 'signOut', 'getRecordings', 'publishRecordings', 'deleteRecordings', 'updateRecordings', 'getRecordingTextTracks'];
}
// Returns a list of supported parameters in the URL for a given API method.
// The return is an array of arrays composed by:
// [0] - RegEx or string with the parameter name
// [1] - true if the parameter is required, false otherwise
urlParamsFor(param) {
switch (param) {
case "create":
return [["meetingID", true], ["name", true], ["attendeePW", false], ["moderatorPW", false], ["welcome", false], ["dialNumber", false], ["voiceBridge", false], ["webVoice", false], ["logoutURL", false], ["maxParticipants", false], ["record", false], ["duration", false], ["moderatorOnlyMessage", false], ["autoStartRecording", false], ["allowStartStopRecording", false], [/meta_\w+/, false]];
case "join":
return [["fullName", true], ["meetingID", true], ["password", true], ["createTime", false], ["userID", false], ["webVoiceConf", false], ["configToken", false], ["avatarURL", false], ["redirect", false], ["clientURL", false]];
case "isMeetingRunning":
return [["meetingID", true]];
case "end":
return [["meetingID", true], ["password", true]];
case "getMeetingInfo":
return [["meetingID", true], ["password", true]];
case "getRecordings":
return [["meetingID", false], ["recordID", false], ["state", false], [/meta_\w+/, false]];
case "publishRecordings":
return [["recordID", true], ["publish", true]];
case "deleteRecordings":
return [["recordID", true]];
case "updateRecordings":
return [["recordID", true], [/meta_\w+/, false]];
case "getRecordingTextTracks":
return [["recordID", true]];
}
}
// Filter `params` to allow only parameters that can be passed
// to the method `method`.
// To use custom parameters, name them `custom_parameterName`.
// The `custom_` prefix will be removed when generating the urls.
filterParams(params, method) {
var filters, r;
filters = this.urlParamsFor(method);
if ((filters == null) || filters.length === 0) {
({});
} else {
r = include(params, function(key, value) {
var filter, i, len;
for (i = 0, len = filters.length; i < len; i++) {
filter = filters[i];
if (filter[0] instanceof RegExp) {
if (key.match(filter[0]) || key.match(/^custom_/)) {
return true;
}
} else {
if (key.match(`^${filter[0]}$`) || key.match(/^custom_/)) {
return true;
}
}
}
return false;
});
}
return filterCustomParameters(r);
}
// Returns a url for any `method` available in the BigBlueButton API
// using the parameters in `params`.
// Parameters received:
// * `method`: The name of the API method
// * `params`: An object with pairs of `parameter`:`value`. The parameters will be used only in the
// API calls they should be used. If a parameter name starts with `custom_`, it will
// be used in all API calls, removing the `custom_` prefix.
// Parameters to be used as metadata should use the prefix `meta_`.
// * `filter`: Whether the parameters in `params` should be filtered, so that the API
// calls will contain only the parameters they accept. If false, all parameters
// in `params` will be added to the API call.
getUrl(method, params, filter = true) {
var checksum, i, key, keys, len, param, paramList, property, query, sep, url;
if (this.debug) {
console.log("Generating URL for", method);
}
if (filter) {
params = this.filterParams(params, method);
} else {
params = filterCustomParameters(params);
}
url = this.url;
// mounts the string with the list of parameters
paramList = [];
if (params != null) {
// add the parameters in alphabetical order to prevent checksum errors
keys = [];
for (property in params) {
keys.push(property);
}
keys = keys.sort();
for (i = 0, len = keys.length; i < len; i++) {
key = keys[i];
if (key != null) {
param = params[key];
}
if (param != null) {
paramList.push(`${this.encodeForUrl(key)}=${this.encodeForUrl(param)}`);
}
}
if (paramList.length > 0) {
query = paramList.join("&");
}
} else {
query = '';
}
// calculate the checksum
checksum = this.checksum(method, query);
// add the missing elements in the query
if (paramList.length > 0) {
query = `${method}?${query}`;
sep = '&';
} else {
if (method !== '/') {
query = method;
}
sep = '?';
}
if (indexOf.call(noChecksumMethods(), method) < 0) {
query = `${query}${sep}checksum=${checksum}`;
}
return `${url}/${query}`;
}
// Calculates the checksum for an API call `method` with
// the params in `query`.
checksum(method, query) {
var c, shaObj, str;
query || (query = "");
if (this.debug) {
console.log(`- Calculating the checksum using: '${method}', '${query}', '${this.salt}'`);
}
str = method + query + this.salt;
if (this.opts.shaType === 'sha256') {
shaObj = new jsSHA("SHA-256", "TEXT");
} else {
shaObj = new jsSHA("SHA-1", "TEXT");
}
shaObj.update(str);
c = shaObj.getHash("HEX");
if (this.debug) {
console.log("- Checksum calculated:", c);
}
return c;
}
// Encodes a string to set it in the URL. Has to encode it exactly like BigBlueButton does!
// Otherwise the validation of the checksum might fail at some point.
encodeForUrl(value) {
return encodeURIComponent(value).replace(/%20/g, '+').replace(/[!'()]/g, escape).replace(/\*/g, "%2A"); // use + instead of %20 for space to match what the Java tools do. // encodeURIComponent doesn't escape !'()* but browsers do, so manually escape them.
}
// Replaces the protocol for `bigbluebutton://`.
setMobileProtocol(url) {
return url.replace(/http[s]?\:\/\//, "bigbluebutton://");
}
}
module.exports = BigBlueButtonApi