UNPKG

prisme-flow

Version:

prisme platform flow engine

337 lines (291 loc) 9.91 kB
/** * Created by prisme.io on 09/06/2017. */ 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('bot-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('bot-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('bot-smooch-send', SmoochOutNode) }