botpress
Version:
The world's first CMS for bots. Easily create, manage and extend chatbots.
229 lines (184 loc) • 10.8 kB
JavaScript
;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _engine = require('./engine');
var _engine2 = _interopRequireDefault(_engine);
var _proactive = require('./proactive');
var _proactive2 = _interopRequireDefault(_proactive);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new _bluebird2.default(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return _bluebird2.default.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /**
* The Content Renderer is in charge of transforming an {@link ContentManager~Element}
* into a channel-specific object.
* @namespace ContentRenderer
* @example
* bp.renderers
*/
module.exports = ({ logger, middlewares, db, contentManager, botfile }) => {
const processors = {}; // A map of all the platforms that can process outgoing messages
const renderers = {}; // A map of all the registered renderers
const registerChannel = ({ platform, processOutgoing }) => {
if (!_lodash2.default.isString(platform)) {
throw new Error(`[Renderers] Platform must be a string, got: ${platform}.`);
}
if (processors[platform]) {
throw new Error(`[Renderers] Platform should only be registered once, platform: ${platform}.`);
}
if (!_lodash2.default.isFunction(processOutgoing)) {
throw new Error(`[Renderers] processOutgoing must be a function, platform: ${platform}.`);
}
logger.verbose(`[Renderers] Enabled for ${platform}.`);
processors[platform] = processOutgoing;
};
/**
* @callback Renderer
* @memberof! ContentRenderer
* @example
* bp.renderers.register('#text', data => ({ text: data.englishText }))
*/
/**
* Registers a new renderer
* @param {String} name Unique name of the renderer (e.g. `#text`).
* @param {ContentRenderer.Renderer} rendererFn The rendering function
* @memberOf! ContentRenderer
*/
const register = (name, rendererFn) => {
if (!_lodash2.default.isString(name)) {
throw new Error(`Renderer name must be a string, received ${name}`);
}
if (name.startsWith('#')) {
name = name.substr(1);
}
renderers[name] = rendererFn;
};
/**
* Removes a specific renderer if it exists
* @param {String} name Unique name of the renderer (e.g. `#text`)
* @memberOf! ContentRenderer
*/
const unregister = name => {
if (!_lodash2.default.isString(name)) {
throw new Error(`Renderer name must be a string, received ${name}`);
}
if (name.startsWith('#')) {
name = name.substr(1);
}
delete renderers[name];
};
/**
* Returns whether or not a renderer is already registered
* @param {String} name Unique name of the renderer (e.g. `#text`)
* @return {Boolean}
* @memberOf! ContentRenderer
*/
const isRegistered = name => {
if (!_lodash2.default.isString(name)) {
throw new Error(`Renderer name must be a string, received ${name}`);
}
if (name.startsWith('#')) {
name = name.substr(1);
}
return !!renderers[name];
};
const invoke = ({ rendererFn, rendererName, context, outputPlatform, incomingEvent = null }) => {
// TODO throw if incomingEvents null <<<==== MOCK IT
const options = {
throwIfNoPlatform: true,
currentPlatform: outputPlatform
};
return (0, _engine2.default)({ rendererFn, rendererName, context, options, processors, incomingEvent });
};
const doSendContent = (() => {
var _ref = _asyncToGenerator(function* (rendererFn, { rendererName, context, outputPlatform, incomingEvent }) {
const messages = yield invoke({ rendererFn, rendererName, context, outputPlatform, incomingEvent });
return _bluebird2.default.mapSeries(messages, function (message) {
if (message.__internal) {
if (message.type === 'wait') {
return _bluebird2.default.delay(message.wait);
}
} else {
return middlewares.sendOutgoing(message);
}
});
});
return function doSendContent(_x, _x2) {
return _ref.apply(this, arguments);
};
})();
const sendContent = (() => {
var _ref2 = _asyncToGenerator(function* (incomingEvent, rendererName, additionalData = {}) {
rendererName = rendererName.startsWith('#') ? rendererName.substr(1) : rendererName;
// "magic" constants that can be used in the renderers
const initialData = {
BOT_URL: botfile.botUrl
};
if (rendererName.startsWith('!')) {
const itemName = rendererName.substr(1);
const contentItem = yield contentManager.getItem(itemName);
if (!contentItem) {
throw new Error(`Could not find content item with ID "${itemName}" in the Content Manager`);
}
const { categoryId: itemCategoryId } = contentItem;
const itemCategory = contentManager.getCategorySchema(itemCategoryId);
if (!itemCategory) {
throw new Error(`Could not find category "${itemCategoryId}" in the Content Manager` + ` for item with ID "${itemName}"`);
}
const itemRenderer = itemCategory.renderer;
if (!_lodash2.default.isString(itemRenderer) || !itemRenderer.startsWith('#') || itemRenderer.length <= 1) {
throw new Error(`Invalid renderer '${itemRenderer}' in category '${itemCategoryId}' of Content Manager.
A renderer must start with '#'`);
}
rendererName = itemRenderer.substr(1);
Object.assign(initialData, _lodash2.default.isArray(contentItem.data) ? { items: contentItem.data } : contentItem.data);
}
const fullContext = _extends({}, initialData, {
user: incomingEvent.user,
event: _lodash2.default.pick(incomingEvent, ['raw', 'text', 'type', 'platform', 'user'])
}, additionalData);
const renderer = renderers[rendererName];
if (!renderer) {
const error = `[Renderer] Renderer not defined (#${rendererName})`;
logger.error(error);
throw new Error(error);
}
yield doSendContent(renderer, {
rendererName,
context: fullContext,
outputPlatform: incomingEvent.platform,
incomingEvent
});
return {
renderer: rendererName,
context: fullContext,
outputPlatform: incomingEvent.platform
};
});
return function sendContent(_x3, _x4) {
return _ref2.apply(this, arguments);
};
})();
const processIncoming = (event, next) => {
event.reply = (rendererName, additionalData = {}) => sendContent(event, rendererName, additionalData);
next();
};
const incomingMiddleware = {
name: 'rendering.instrumentation',
type: 'incoming',
order: 2, // Should really be first
module: 'botpress',
description: 'Built-in Botpress middleware that adds a `.reply` to events. Works with renderers.',
handler: processIncoming
};
const proactiveMethods = (0, _proactive2.default)({ sendContent, db });
return _extends({
registerChannel,
registerConnector: registerChannel, // DEPRECATED Use "channel" instead of "connector"
register,
unregister,
isRegistered,
incomingMiddleware
}, proactiveMethods);
};
//# sourceMappingURL=index.js.map