UNPKG

node-red-contrib-chatbot

Version:

REDBot a Chat bot for a full featured chat bot for Telegram, Facebook Messenger and Slack. Almost no coding skills required

355 lines (288 loc) 10 kB
var _ = require('underscore'); var moment = require('moment'); var SmoochBot = require('../lib/smooch/smooch-bot'); var ChatContextStore = require('../lib/chat-context-store'); var ChatLog = require('../lib/chat-log'); var helpers = require('../lib/smooch/helpers'); var clc = require('cli-color'); var DEBUG = false; var green = clc.greenBright; var white = clc.white; var grey = clc.blackBright; module.exports = function(RED) { function SmoochBotNode(n) { RED.nodes.createNode(this, n); var self = this; this.botname = n.botname; this.log = n.log; this.usernames = []; if (n.usernames) { this.usernames = _(n.usernames.split(',')).chain() .map(function(userId) { return userId.match(/^[a-zA-Z0-9_]+?$/) ? userId : null }) .compact() .value(); } this.handleMessage = function(botMsg) { var facebookBot = self.bot; if (DEBUG) { // eslint-disable-next-line no-console console.log('START:-------'); // eslint-disable-next-line no-console console.log(botMsg); // eslint-disable-next-line no-console console.log('END:-------'); } // mark the original message with the platform botMsg.transport = 'smooch'; var userId = botMsg.authorId; var chatId = botMsg.authorId; var messageId = botMsg._id; // todo fix this //var isAuthorized = node.config.isAuthorized(username, userId); var isAuthorized = true; var chatContext = ChatContextStore.getOrCreateChatContext(self, chatId); // decode the message, eventually download stuff self.getMessageDetails(botMsg, self.bot) .then(function(payload) { // store some information chatContext.set('chatId', chatId); chatContext.set('messageId', messageId); chatContext.set('userId', userId); chatContext.set('firstName', botMsg.name); chatContext.set('lastName', null); chatContext.set('authorized', isAuthorized); chatContext.set('transport', 'smooch'); chatContext.set('message', botMsg.text); var chatLog = new ChatLog(chatContext); return chatLog.log({ payload: payload, originalMessage: { transport: 'smooch', chat: { id: chatId } }, chat: function() { return ChatContextStore.getChatContext(self, chatId); } }, self.log) }) .then(function (msg) { var currentConversationNode = chatContext.get('currentConversationNode'); // if a conversation is going on, go straight to the conversation node, otherwise if authorized // then first pin, if not second pin if (currentConversationNode != null) { // void the current conversation chatContext.set('currentConversationNode', null); // emit message directly the node where the conversation stopped RED.events.emit('node:' + currentConversationNode, msg); } else { facebookBot.emit('relay', msg); } }) .catch(function (error) { facebookBot.emit('relay', null, error); }); }; if (this.credentials) { this.keyId = this.credentials.keyId; this.secret = this.credentials.secret; if (this.keyId != null && this.secret != null) { this.keyId = this.keyId.trim(); this.secret = this.secret.trim(); if (!this.bot) { this.bot = new SmoochBot({ keyId: this.keyId, secret: this.secret }); var uiPort = RED.settings.get('uiPort'); // eslint-disable-next-line no-console console.log(''); // eslint-disable-next-line no-console console.log(grey('------ Smooch Webhook ----------------')); // eslint-disable-next-line no-console console.log(green('Webhook URL: ') + white('http://localhost' + (uiPort != '80' ? ':' + uiPort : '') + '/redbot/smooch')); // eslint-disable-next-line no-console console.log(''); // mount endpoints on local express this.bot.addExpressMiddleware(RED.httpNode); this.bot.on('message', this.handleMessage); } } } this.on('close', function (done) { self.bot.removeExpressMiddleware(RED.httpNode); done(); }); this.isAuthorized = function (username, userId) { if (self.usernames.length > 0) { return self.usernames.indexOf(username) != -1 || self.usernames.indexOf(String(userId)) != -1; } return true; }; // creates the message details object from the original message this.getMessageDetails = function (message) { return new Promise(function (resolve, reject) { var chatId = message.authorId; var messageId = message._id; if (message.mediaUrl != null && message.mediaType.indexOf('image') !== -1) { helpers.downloadFile(message.mediaUrl) .then(function(buffer) { resolve({ chatId: chatId, messageId: messageId, type: 'photo', content: buffer, date: moment(message.received), inbound: true }); }) .catch(function() { reject('Unable to download ' + message.mediaUrl); }); } else if (!_.isEmpty(message.text)) { resolve({ chatId: chatId, messageId: messageId, type: 'message', content: message.text, date: moment(message.received), inbound: true }); } }); } } RED.nodes.registerType('chatbot-smooch-node', SmoochBotNode, { credentials: { keyId: { type: 'text' }, secret: { type: 'text' } } }); function SmoochInNode(config) { RED.nodes.createNode(this, config); var node = this; this.bot = config.bot; this.config = RED.nodes.getNode(this.bot); if (this.config) { this.status({fill: 'red', shape: 'ring', text: 'disconnected'}); node.bot = this.config.bot; if (node.bot) { this.status({fill: 'green', shape: 'ring', text: 'connected'}); node.bot.on('relay', function(message, error) { if (error != null) { node.error(error); } else { node.send(message); } }); } else { node.warn('no bot in config.'); } } else { node.warn('Missing configuration in Facebook Messenger Receiver'); } } RED.nodes.registerType('chatbot-smooch-receive', SmoochInNode); function SmoochOutNode(config) { RED.nodes.createNode(this, config); var node = this; this.bot = config.bot; this.track = config.track; this.config = RED.nodes.getNode(this.bot); if (this.config) { this.status({fill: 'red', shape: 'ring', text: 'disconnected'}); node.bot = this.config.bot; if (node.bot) { this.status({fill: 'green', shape: 'ring', text: 'connected'}); } else { node.warn('no bot in config.'); } } else { node.warn('no config.'); } function sendMessage(msg) { return new Promise(function(resolve, reject) { var type = msg.payload.type; var bot = node.bot; //var credentials = node.config.credentials; var reportError = function(err) { if (err) { reject(err); } else { resolve() } }; switch (type) { case 'action': // implement actions break; case 'message': bot.sendMessage(msg.payload.chatId, msg.payload.content, reportError); break; case 'inline-buttons': // docs here https://docs.smooch.io/rest/#action-buttons return bot.sendActions(msg.payload.chatId, msg.payload.content, msg.payload.buttons) .catch(function(err) { reject(err); }) .then(function() { resolve(); }); case 'photo': var image = msg.payload.content; bot.uploadBuffer(msg.payload.chatId, image) .catch(function(err) { reject(err); }) .then(function() { resolve(); }); break; default: reject('Unable to prepare unknown message type'); } }); } // relay message var handler = function(msg) { node.send(msg); }; RED.events.on('node:' + config.id, handler); // cleanup on close this.on('close',function() { RED.events.removeListener('node:' + config.id, handler); }); this.on('input', function (msg) { // check if the message is from facebook if (msg.originalMessage != null && msg.originalMessage.transport !== 'smooch') { // exit, it's not from smooch return; } var track = node.track; var chatContext = msg.chat(); // check if this node has some wirings in the follow up pin, in that case // the next message should be redirected here if (chatContext != null && track && !_.isEmpty(node.wires[0])) { chatContext.set('currentConversationNode', node.id); chatContext.set('currentConversationNode_at', moment()); } var chatLog = new ChatLog(chatContext); chatLog.log(msg, this.config.log) .then(function () { return sendMessage(msg); }) .catch(function(err) { node.error(err); }); }); } RED.nodes.registerType('chatbot-smooch-send', SmoochOutNode); };