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

533 lines (523 loc) 21.6 kB
<script type="text/javascript"> $.RedBot.registerType('chatbot-slack-node', { category: 'config', defaults: { botname: { value: '', required: true }, usernames: { value: '', required: false }, username: { value: '' }, iconEmoji: { value: '' }, store: { value: '', type: 'chatbot-context-store', required: false }, log: { value: null }, debug: { value: false }, serverPort: { value: '3001' }, useWebSocket: { value: true }, storeMessages: { value: true }, enableMissionControl: { value: false }, inspectMessages: { value: true }, chatbotId: { value: '' } }, oneditprepare: function() { var node = this; var nodeRedUrl = $.RedBot.getNodeRedUrl(); // fetch available context providers $.get(nodeRedUrl + 'redbot/globals') .done(function(response) { if (response != null && response.slack != null && response.slack[node.botname] != null) { $('#node-config-input-botname').prop('readonly', true); $('.form-editable').hide(); $('.form-warning').show(); $('.form-warning .bot-name').html('"' + node.botname + '"'); } // hide mission control options if not enabled if (!response.missionControl) { $('.form-row-not-mission-control').show(); $('.form-row-mission-control').hide(); $('#node-config-input-enableMissionControl') .prop('disabled', true) .prop('checked', false); } }); // enable or disable mission control $('#node-config-input-enableMissionControl') .change(function() { if ($(this).is(':checked')) { $('.form-row-not-mission-control').hide(); $('.form-row-mission-control').show(); } else { $('.form-row-not-mission-control').show(); $('.form-row-mission-control').hide(); } }); // if enable mission control, set a random chatbot id if empty $('#node-config-input-enableMissionControl') .change(function() { if ($(this).is(':checked')) { const chatbotId = $('#node-config-input-chatbotId').val(); if (chatbotId == null || chatbotId === '') { $.ajax({ url: '/mc/chatbotIdGenerator' }) .then(res => { $('#node-config-input-chatbotId').val(res); }); } } }); // handle web-sockets $('#node-config-input-useWebSocket') .click(function() { const checked = $(this).is(':checked'); if (checked) { $('.form-row-applicationToken').show(); } else { $('.form-row-applicationToken').hide(); } }); if (node.useWebSocket) { $('.form-row-applicationToken').show(); } else { $('.form-row-applicationToken').hide(); } }, paletteName: 'Slack Bot', credentials: { token: { type: 'text' }, appToken: { type: 'text' }, signingSecret: { type: 'text' } }, label: function () { return this.botname; } }); </script> <script type="text/x-red" data-template-name="chatbot-slack-node"> <div class="form-row"> <label for="node-config-input-botname"><i class="icon-bookmark"></i> Bot Name</label> <input type="text" id="node-config-input-botname"> </div> <div class="form-editable"> <div class="redbot-separator"> <div>Slack settings</div> </div> <div class="form-row"> <label for="node-config-input-signingSecret">Signing Secret</label> <input type="text" id="node-config-input-signingSecret"> <div class="redbot-form-hint"> Find the signing secret under <em>Basic Informations</em> &gt; <em>App Credentials</em> </div> </div> <div class="form-row"> <label for="node-config-input-token">Bot Token</label> <input type="text" id="node-config-input-token"> <div class="redbot-form-hint"> Bot User OAuth Access Token - Used for sending a receiving messages. </div> </div> <div class="form-row"> <label for="node-config-input-useWebSocket">Socket Mode</label> <input type="checkbox" value="true" id="node-config-input-useWebSocket" style="width:auto;"> <div class="redbot-form-hint"> Use Web Sockets to connect with Slack (reccomended) </div> </div> <div class="form-row form-row-applicationToken"> <label for="node-config-input-appToken">Application Token</label> <input type="text" id="node-config-input-appToken"> <div class="redbot-form-hint"> Generate the app-level token in <em>Basic Informations</em> &gt; <em>App-Level Token</em> </div> </div> <div class="form-row"> <label for="node-config-input-serverPort">Web Hook Server Port</label> <input type="text" id="node-config-input-serverPort"> <div class="redbot-form-hint"> The port the webhook will listen to. Due to some technical limitations, the webhooks for Slack will run on a standalone server and not in the same Express instance of Node-RED. </div> </div> <div class="redbot-separator"> <div>RedBot settings</div> </div> <div class="form-row"> <label for="node-config-input-debug">Debug</label> <input type="checkbox" value="true" class="redbot-checkbox" id="node-config-input-debug"> <span class="redbot-form-hint"> Show debug information on send/receive </span> </div> <div class="form-row"> <label for="node-config-input-enableMissionControl" class="redbot-label">Use Mission Control</label> <input type="checkbox" value="true" class="redbot-checkbox" id="node-config-input-enableMissionControl"> <span class="redbot-form-hint"> Enable Mission Control, see <a href="https://github.com/guidone/node-red-contrib-chatbot/wiki/Mission-Control" target="_blank">documentation here</a> </span> </div> <div class="form-row form-row-mission-control"> <div class="redbot-form-hint"> Mission Control uses the built-in <b>SQLite context provider</b>. </div> </div> </div> <div class="form-row form-row-not-mission-control"> <label for="node-input-bot">Context</label> <input type="text" id="node-config-input-store" placeholder="Select storage for chat context"> <div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;margin-top:5px;"> Select the chat context provider to use with this bot, if none is selected then non-persistent "memory" will be used.<br> To extend <strong>RedBot</strong> with a new chat context provider see <a href="https://github.com/guidone/node-red-contrib-chatbot/wiki/Creating-a-Chat-Context-Provider" target="_blank">this tutorial</a>. </div> </div> <div class="form-row form-row-mission-control"> <label for="node-config-input-chatbotId" class="redbot-label">Chatbot ID</label> <input type="text" id="node-config-input-chatbotId" placeholder="Enter a chatbot ID"> <span class="redbot-form-hint"> Specify a chatbot ID if you're running multiple chatbots on this Node-RED instance (it's just a string of your choice). Leave it blank if you're just running a single chatbot. </span> </div> <div class="form-row form-row-mission-control"> <label class="redbot-label" for="node-config-input-storeMessages">Store messages</label> <input type="checkbox" value="true" class="redbot-checkbox" id="node-config-input-storeMessages"> <span class="redbot-form-hint"> Store inbound and outbound messages in Mission Control </span> </div> <div class="form-row form-row-mission-control"> <label for="node-config-input-inspectMessages" class="redbot-label">Inspect messages</label> <input type="checkbox" value="true" class="redbot-checkbox" id="node-config-input-inspectMessages"> <span class="redbot-form-hint"> Inspect messages in real time in Mission Control </span> </div> <div class="redbot-separator"> <div> Legacy settings </div> <span class="redbot-form-hint"> These settings are for retrocompatibility, should only be used in legacy bots. Use <b>Mission Control</b> to control user access or to log messages </span> </div> <div class="form-row"> <label for="node-config-input-usernames">Users</label> <input type="text" id="node-config-input-usernames"> <div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;margin-top:5px;"> Comma separated list of userId authorized to use the chatBot </div> </div> <div class="form-row"> <label for="node-config-input-log">Log file</label> <input type="text" id="node-config-input-log"> <div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;margin-top:5px;"> Store inbound and outbound messages to file </div> </div> </div> <div class="form-warning" style="display:none;"> This bot configuration is stored in <b>Node-RED</b> <em>settings.js</em> and cannot be modified from the UI, check the section <code>functionGlobalContext</code> near the key <em class="bot-name">""</em> </div> <style> #node-config-dialog-edit-form .form-row label { width: 150px; } </style> </script> <script type="text/x-red" data-help-name="chatbot-slack-node"><p>There are two way of connecting to Slack: with WebSockets or with web hooks. The first one is recommended since it doesn’t require any use of reverse proxies like <strong>ngrok.</strong></p> <h2 id="with-websocket">With WebSocket</h2> <ol> <li><p>Go to your <a href="https://api.slack.com/">Slack apps dashboard</a> and click on <em>Create New App</em></p> </li> <li><p>Select <em>From app manifest</em> and paste this code</p> </li> </ol> <pre><code class="language-yaml">display_information: name: MySlackApp features: app_home: home_tab_enabled: false messages_tab_enabled: true messages_tab_read_only_enabled: false bot_user: display_name: MySlackApp always_online: true oauth_config: scopes: bot: - channels:history - channels:join - chat:write - chat:write.customize - commands - im:history - users:write - files:read - files:write settings: event_subscriptions: bot_events: - message.channels - message.im interactivity: is_enabled: true org_deploy_enabled: false socket_mode_enabled: true token_rotation_enabled: false </code></pre> <p>then select the Workspace to install the app to, go through all steps and finally click on <em>Create</em></p> <ol start="3"> <li><p>Get the <strong>Signing Secret</strong> in section <em>Basic Information</em> under <em>App Credentials</em></p> </li> <li><p>Get the <strong>Bot Token</strong> in section <em>OAuth &amp; Permissions</em> under <em>OAuth Tokens for Your Workspace</em>: click in <em>Install To Workspace</em> and get the generated <em>Bot User OAuth Token</em></p> </li> <li><p>Check the <em>Socket Mode</em></p> </li> <li><p>Get the <strong>App-Level Token</strong> in <em>Basic Information</em> under <em>App-Level Tokens</em>: click on <em>Generate Token and Scopes</em>, pick any name for the token, add the scope <em>connections:write</em> then click on ‘Generate’</p> </li> </ol> <h2 id="with-web-hooks">With Web Hooks</h2> <ol start="7"> <li><strong>Slack</strong> API talks to <strong>Red-Bot</strong> via a https callback (a self signed certificate is not enough). We’ll use <a href="https://ngrok.com/">ngrok</a> to create a https tunnel for our local <strong>Node-RED</strong> instance. Install it, then open a shell window and run</li> </ol> <pre><code class="language-plain">ngrok http 3001 </code></pre> <p>You should get something like</p> <p><img src="https://s3.us-west-2.amazonaws.com/secure.notion-static.com/b33faf56-5fdb-4bb5-892c-bb137a31c541/facebook-1-1.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20230218%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20230218T124916Z&X-Amz-Expires=3600&X-Amz-Signature=07711c319902f60515991efc0fd00e0d9205c1cf92565e02bb720f33925181c7&X-Amz-SignedHeaders=host&x-id=GetObject" alt="Ngrok"></p> <p>Grab the https address you get, something like <em><a href="https://123123.ngrok.io">https://123123.ngrok.io</a></em>, this is the base url that points back to your <strong>Node-RED</strong> instance.</p> <p><strong>Warning</strong> <em>Slack API</em> libraries are not compatible with the stack of middlewares used by <strong>Node-RED</strong>, for this reason the <strong>Slack</strong> web-hooks in <strong>RedBot</strong> run in a separate <em>Express</em> server on a different port (the default one in 3001). Keep in mind that in production it’s needed to open the firewall in order to expose this port.</p> <ol start="8"> <li><p>Go to your <a href="https://api.slack.com/">Slack apps dashboard</a> and click on <em>Create New App</em></p> </li> <li><p>Select <em>From app manifest</em> and paste this code</p> </li> </ol> <pre><code class="language-yaml">display_information: name: MySlack WebHook features: app_home: home_tab_enabled: false messages_tab_enabled: true messages_tab_read_only_enabled: false bot_user: display_name: MySlack WebHook always_online: true oauth_config: scopes: bot: - channels:history - channels:join - chat:write - commands - files:read - files:write - im:history - users:write - chat:write.customize settings: event_subscriptions: request_url: https://123456.ngrok.io/slack/events bot_events: - message.channels - message.im interactivity: is_enabled: true request_url: https://123456.ngrok.io/slack/events org_deploy_enabled: false socket_mode_enabled: false token_rotation_enabled: false </code></pre> <p>Replace the <em>123456.ngrok.io</em> domain with the one got in <em>step 1</em></p> <ol start="10"> <li><p>Get the <strong>Signing Secret</strong> in section <em>Basic Information</em> under <em>App Credentials</em></p> </li> <li><p>Get the <strong>Bot Token</strong> in section <em>Oauth &amp; Permissions</em> under <em>OAuth Tokens for Your Workspace</em>: click in <em>Install To Workspace</em> and get the generated <em>Bot User OAuth Token</em></p> </li> <li><p>Un-check <em>Use WebSocket</em></p> </li> <li><p>In order to support <em>Slack Commands</em>, use as <em>Request URL</em> the same callback (i.e. <a href="https://123456.ngrok.io/slack/events">https://123456.ngrok.io/slack/events</a>, be sure to replace the url got in <em>step 1</em>)</p> </li> </ol> <p><code>Slack Receiver</code> and <code>Slack Sender</code> have a double bot configuration for <em>development</em> and <em>production</em>. By default is used the <em>development</em> configuration. To use <em>production</em> configuration, edit <strong>Node-RED</strong> settings file (<em>settings.js</em>) and set the <em>environment</em> global variable to <em>“production”</em>. See <a href="https://www.notion.so/c0c2de46b48a4def837753c7e284b356">Deploying RedBot</a> for more details.</p> <p><strong>RedBot</strong> also supports <strong>Slack</strong> events, in the chatbot use the <a href="https://www.notion.so/4113636f565d4ff4af08bc61a644206b">Rules node</a> to control the flow based on incoming events (the payload of the event will be in <code>msg.payload</code>) and use the <a href="https://www.notion.so/6cfd957b91f642b5894a76e2b15feb05">Support table</a> to see the list of supported events.</p> </script> <script type="text/javascript"> $.RedBot.registerType('chatbot-slack-receive', { category: $.RedBot.config.name + ' Platforms', color: '#FFCC66', defaults: { bot: { value: '', type: 'chatbot-slack-node', required: true }, botProduction: { value: "", type: 'chatbot-slack-node', required: false }, }, inputs: 0, outputs: 1, icon: 'slack.svg', paletteLabel: 'Slack Receiver', label: function () { return "Slack Receiver"; } }); </script> <script type="text/x-red" data-template-name="chatbot-slack-receive"> <div class="form-row"> <label for="node-input-bot" style="display:block;width:100%;">Bot configuration <span class="redbot-environment">(development)</span></label> <input type="text" id="node-input-bot" placeholder="Bot"> </div> <div class="form-row" style="margin-top:25px;"> <label for="node-input-botProduction" style="display:block;width:100%;">Bot configuration <span class="redbot-environment">(production)</span></label> <input type="text" id="node-input-botProduction" placeholder="Bot"> </div> <div class="redbot-form-hint"> Bot for <strong>production</strong> will be launched only if the global variable <em>"environment"</em> in <em>settings.js</em> is set to <em>"production"</em>, otherwise will be used the configuration for <strong>development</strong>. </div> </script> <script type="text/x-red" data-help-name="chatbot-slack-receive"> <p>Input node for Slack.</p> </script> <script type="text/javascript"> $.RedBot.registerType('chatbot-slack-send', { category: $.RedBot.config.name + ' Platforms', color: '#FFCC66', defaults: { bot: { value: '', type: 'chatbot-slack-node', required: true }, botProduction: { value: "", type: 'chatbot-slack-node', required: false }, track: { value: false }, passThrough: { value: false }, errorOutput: { value: false }, outputs: { value: 0 } }, inputs: 1, outputs: 1, icon: 'slack.svg', paletteLabel: 'Slack Sender', label: function () { return 'Slack Sender'; }, inputLabels: 'Chat flow', outputLabels: function(idx) { if (idx === 0 && this.track) { return 'Track'; } else if (idx === 0 && this.passThrough) { return 'Pass through'; } else if (idx === 1) { return 'Error'; } }, oneditprepare: function() { // cannot be checked at the same time $('#node-input-passThrough') .click(function() { if ($(this).is(':checked')) { $('#node-input-track').prop('checked', false); } }); $('#node-input-track') .click(function() { if ($(this).is(':checked')) { $('#node-input-passThrough').prop('checked', false); } }); }, oneditsave: function() { var count = 0; var track = $('#node-input-track').is(':checked'); var passThrough = $('#node-input-passThrough').is(':checked'); var errorOutput = $('#node-input-errorOutput').is(':checked'); if (track || passThrough) { count = 1; } if (errorOutput) { count += 1; } this.outputs = count; } }); </script> <script type="text/x-red" data-template-name="chatbot-slack-send"> <div class="form-row"> <label for="node-input-bot" style="display:block;width:100%;">Bot configuration <span class="redbot-environment">(development)</span></label> <input type="text" id="node-input-bot" placeholder="Bot"> </div> <div class="form-row" style="margin-top:25px;"> <label for="node-input-botProduction" style="display:block;width:100%;">Bot configuration <span class="redbot-environment">(production)</span></label> <input type="text" id="node-input-botProduction" placeholder="Bot"> </div> <div class="redbot-form-hint"> Bot for <strong>production</strong> will be launched only if the global variable <em>"environment"</em> in <em>settings.js</em> is set to <em>"production"</em>, otherwise will be used the configuration for <strong>development</strong>. </div> <div class="form-row" style="margin-top:25px;"> <label for="node-input-track" style="margin-bottom:0px;">Track</label> <input type="checkbox" value="true" id="node-input-track" style="margin-top:0px;"> <div class="redbot-form-hint"> Track response of the user for this message: any further answer will be redirect to the output pin. </div> <label for="node-input-track" style="margin-bottom:0px;margin-top:15px;">Pass Through</label> <input type="checkbox" value="true" id="node-input-passThrough" style="margin-top:0px;"> <div class="redbot-form-hint"> Forward the message to the output pin after sending (useful to chain messages and keep the right order) </div> <label for="node-input-errorOutput" style="margin-bottom:0px;margin-top:15px;">Error Output</label> <input type="checkbox" value="true" id="node-input-errorOutput" style="margin-top:0px;"> <div class="redbot-form-hint"> Redirect the flow to the error pin in case of platform specific error on sending the message </div> </div> </script> <script type="text/x-red" data-help-name="chatbot-slack-send"> <p>Output node for Slack.</p> </script>