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

511 lines (500 loc) 23.5 kB
<script type="text/javascript"> $.RedBot.registerType('chatbot-whatsapp-node', { category: 'config', defaults: { botname: { value: '', required: true }, phoneNumberId: { value: '', required: true }, businessAccountId: { value: '', required: true }, store: { value: '', type: 'chatbot-context-store', required: false }, debug: { value: false }, 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.facebook != null && response.facebook[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); }); } } }); }, paletteName: 'Whatsapp Bot', credentials: { token: { type: 'text' }, verify_token: { type: 'text' }, app_secret: { type: 'text' } }, label: function () { return this.botname; } }); </script> <script type="text/x-red" data-template-name="chatbot-whatsapp-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>Whatsapp settings</div> </div> <div class="form-row"> <label for="node-config-input-token">Token</label> <input type="text" id="node-config-input-token" placeholder="Access Token"> <div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;margin-top:5px;"> The temporary access token or the permanent one from the <em>"Getting started"</em> page </div> </div> <div class="form-row"> <label for="node-config-input-phoneNumberId" style="width:auto;margin-right:10px;">Phone Number ID</label> <input type="text" id="node-config-input-phoneNumberId" placeholder="1234567890 "> </div> </div> <div class="form-row"> <label for="node-config-input-phoneNumberId" style="width:auto;margin-right:10px;">Business Account ID</label> <input type="text" id="node-config-input-businessAccountId" placeholder="1234567890 "> </div> <div class="form-row"> <label for="node-config-input-verify_token">Verify</label> <input type="text" id="node-config-input-verify_token" placeholder="Verify token"> <div style="max-width: 460px;font-size: 12px;color: #999999;line-height: 14px;margin-top:5px;"> The verify token is an arbitrary string, must match the same string in the "Webhooks" section in the xxxx app </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> <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> </script> <script type="text/x-red" data-help-name="chatbot-whatsapp-node"> Placeholder </script> <script type="text/javascript"> $.RedBot.registerType('chatbot-whatsapp-receive', { category: $.RedBot.config.name + ' Platforms', color: '#FFCC66', defaults: { bot: { value: '', type: 'chatbot-whatsapp-node', required: true }, botProduction: { value: '', type: 'chatbot-whatsapp-node', required: false } }, inputs: 0, outputs: 2, icon: 'whatsapp.svg', paletteLabel: 'Whatsapp Receiver', label: function () { return 'Whatsapp Receiver'; }, outputLabels: ['Messages', 'Events'] }); </script> <script type="text/x-red" data-template-name="chatbot-whatsapp-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-whatsapp-receive"><p>WhatsApp Cloud API talks to RedBot through an HTTPS callback (a self-signed certificate is <strong>not</strong> accepted). For local development we expose Node-RED through an <a href="https://ngrok.com/">ngrok</a> tunnel; for production you&#39;ll point Meta at your real HTTPS hostname.</p> <h2 id="1-expose-node-red-over-https-with-ngrok">1. Expose Node-RED over HTTPS with ngrok</h2> <p>Install ngrok and sign in (<code>ngrok config add-authtoken …</code>), then open a shell and run:</p> <pre><code class="language-bash">ngrok http 127.0.0.1:1880 </code></pre> <p>ngrok prints a forwarding URL such as <code>https://abcd-1234.ngrok-free.app</code>. The full webhook RedBot listens on is:</p> <pre><code class="language-plain">https://&lt;your-tunnel&gt;.ngrok-free.app/redbot/whatsapp </code></pre> <p>Keep this terminal open while testing — every time you restart ngrok the URL changes (unless you have a reserved domain), and you&#39;ll have to update the webhook in Meta.</p> <blockquote> <p>Tip: a paid ngrok plan gives you a static domain (<code>--domain my-bot.ngrok.app</code>) so you don&#39;t have to reconfigure the webhook on every restart.</p> </blockquote> <h2 id="2-create-the-meta-app-and-whatsapp-business-account-waba">2. Create the Meta app and WhatsApp Business Account (WABA)</h2> <p>The Meta flow has been reorganised since the original guide. Two things must exist:</p> <ol> <li><p>A <strong>Meta Business Portfolio</strong> (formerly &quot;Business Manager&quot;) at <a href="https://business.facebook.com/">https://business.facebook.com/</a>. Create one if you don&#39;t have it — this is what owns the WABA, the app, and billing.</p> </li> <li><p>A <strong>Meta for Developers app</strong> at <a href="https://developers.facebook.com/apps/create/">https://developers.facebook.com/apps/create/</a>.</p> </li> </ol> <h3 id="21-create-the-app">2.1 Create the app</h3> <ul> <li><p>Go to <a href="https://developers.facebook.com/apps/">https://developers.facebook.com/apps/</a><strong>Create app</strong>.</p> </li> <li><p>Use case: <strong>Other</strong> → app type: <strong>Business</strong>.</p> </li> <li><p>Give it a name, attach the <strong>Business Portfolio</strong> from step 1.</p> </li> </ul> <h3 id="22-add-the-whatsapp-product">2.2 Add the WhatsApp product</h3> <p>In the app dashboard sidebar choose <strong>Add product → WhatsApp → Set up</strong>.</p> <p>This automatically provisions:</p> <ul> <li><p>A test <strong>WhatsApp Business Account</strong> (WABA)</p> </li> <li><p>A <strong>test phone number</strong> (Meta-owned, you can use it free of charge to send messages to up to 5 verified recipients)</p> </li> <li><p>A <strong>temporary access token</strong> valid for 24 hours</p> </li> </ul> <p>These three things are enough to get a &quot;hello world&quot; working. Production setup (real phone number + permanent token) is in <a href="https://www.notion.so/redbot/Whatsapp-Receiver-node-47c201b9b9e945fb909580bab9c31b87#6-going-to-production">§6</a>.</p> <h3 id="23-collect-the-ids">2.3 Collect the IDs</h3> <p>Open <strong>WhatsApp → API setup</strong> in the app dashboard and copy:</p> <table> <thead> <tr> <th>Field in Meta UI</th> <th>Field in RedBot</th> </tr> </thead> <tbody><tr> <td>Temporary access token</td> <td><strong>Token</strong> (Access Token)</td> </tr> <tr> <td>Phone number ID</td> <td><strong>Phone Number ID</strong></td> </tr> <tr> <td>WhatsApp Business Account ID</td> <td><strong>Business Account ID</strong></td> </tr> </tbody></table> <p>Also add your personal phone number to <strong>To → Manage phone number list</strong> so you can receive test messages.</p> <h2 id="3-configure-the-webhook-in-meta">3. Configure the webhook in Meta</h2> <p>In <strong>WhatsApp → Configuration</strong>:</p> <ol start="3"> <li><p>Click <strong>Edit</strong> next to <em>Webhook</em>.</p> </li> <li><p><strong>Callback URL</strong>: <code>https://&lt;your-tunnel&gt;.ngrok-free.app/redbot/whatsapp</code></p> </li> <li><p><strong>Verify token</strong>: any arbitrary string, e.g. <code>redbot-test</code>. Remember it — you&#39;ll paste the same string into the RedBot config.</p> </li> <li><p>Click <strong>Verify and save</strong>. Meta hits your callback with a <code>GET</code> to confirm the verify token; if ngrok is up and Node-RED is running, the check passes.</p> </li> <li><p>Click <strong>Manage</strong> under <em>Webhook fields</em> and <strong>Subscribe</strong> to at least the <code>messages</code> field. (You can add <code>message_template_status_update</code> later if you use templates.)</p> </li> </ol> <h2 id="4-configure-the-redbot-nodes">4. Configure the RedBot nodes</h2> <p>In Node-RED drop three nodes and wire them:</p> <pre><code class="language-plain">Whatsapp Receiver → Text → Whatsapp Sender </code></pre> <p>Set the <strong>Text</strong> node to something like <code>Hello world!</code>.</p> <h3 id="41-whatsapp-receiver">4.1 Whatsapp Receiver</h3> <p>Double-click and create a new configuration:</p> <ul> <li><p><strong>Bot Name</strong>: any label</p> </li> <li><p><strong>Token</strong>: temporary access token from §2.3</p> </li> <li><p><strong>Phone Number ID</strong>: from §2.3</p> </li> <li><p><strong>Business Account ID</strong>: from §2.3</p> </li> <li><p><strong>Verify token</strong>: the exact string you typed in §3 step 3</p> </li> </ul> <h3 id="42-whatsapp-sender">4.2 Whatsapp Sender</h3> <p>Select the same bot configuration you just created.</p> <p>Deploy the flow.</p> <hr> <h2 id="5-test-it">5. Test it</h2> <p>From the phone number you whitelisted in §2.3, send any text to the <strong>test phone number</strong> shown in the Meta dashboard. You should get <code>Hello world!</code> back, and the Receiver node should light up in the Node-RED debug panel.</p> <p>If nothing happens:</p> <ul> <li><p>ngrok dashboard at <a href="http://127.0.0.1:4040/">http://127.0.0.1:4040</a> shows incoming requests — useful to confirm Meta is calling you.</p> </li> <li><p><code>/redbot/whatsapp/test</code> returns <code>ok</code> if the route is mounted. Visit <code>https://&lt;your-tunnel&gt;.ngrok-free.app/redbot/whatsapp/test</code> in a browser.</p> </li> <li><p>Check that your number is on the recipient allow-list — Meta silently drops messages to non-whitelisted numbers when using the test phone.</p> </li> </ul> <hr> <h2 id="6-going-to-production">6. Going to production</h2> <p>The 24-hour temporary token and the Meta test phone number are fine for development, but for production you need:</p> <h3 id="61-a-real-phone-number-on-the-waba">6.1 A real phone number on the WABA</h3> <p>In the WhatsApp dashboard (or directly in Business Portfolio → <strong>WhatsApp Accounts → Phone numbers</strong>):</p> <ol start="8"> <li><p><strong>Add phone number</strong>. Use a number that is <strong>not currently active on a personal/Business WhatsApp app</strong> — if it is, fully delete the WhatsApp account on the device first.</p> </li> <li><p>Verify by SMS or voice.</p> </li> <li><p>Set the display name. The first display name goes through a Meta review (usually a few minutes to a day).</p> </li> </ol> <h3 id="62-business-verification">6.2 Business verification</h3> <p>For anything beyond the test sandbox (sending to numbers that aren&#39;t on your allow-list, increasing tier limits, using templates with media) the owning Business Portfolio must complete <strong>Business Verification</strong> under <strong>Business Portfolio → Security Center</strong>. Plan for a few business days; you&#39;ll need a company registration document.</p> <h3 id="63-a-permanent-access-token-via-a-system-user">6.3 A permanent access token via a System User</h3> <p>Temporary tokens expire after 24 hours and are not suitable for production. Generate a permanent one:</p> <ol start="11"> <li><p><strong>Business Portfolio → Settings → Users → System users → Add</strong>. Create a <strong>System user</strong> with role <strong>Admin</strong>.</p> </li> <li><p><strong>Assign assets</strong>: add the WhatsApp app and the WABA, granting <em>Full control</em>.</p> </li> <li><p>Click <strong>Generate new token</strong>, pick the app, and select scopes <strong><code>whatsapp_business_messaging</code></strong> and <strong><code>whatsapp_business_management</code></strong>.</p> </li> <li><p>Copy the token (you won&#39;t see it again) and paste it into the <strong>Token</strong> field of the Whatsapp Receiver config in place of the temporary one.</p> </li> </ol> <h3 id="64-production-webhook">6.4 Production webhook</h3> <p>Replace the ngrok URL with your real HTTPS endpoint in <strong>WhatsApp → Configuration → Webhook</strong>. The verify token can stay the same. Don&#39;t forget the path is still <code>/redbot/whatsapp</code>.</p> <blockquote> <p>If you run RedBot behind a reverse proxy, make sure the proxy forwards the raw request body — the receiver validates Meta&#39;s payload format before processing it.</p> </blockquote> <h3 id="65-app-review-only-if-you-let-third-parties-install-your-bot">6.5 App review (only if you let third parties install your bot)</h3> <p>If your app stays internal (one Business Portfolio, one WABA), you don&#39;t need App Review. If you&#39;re building a product that other businesses connect to via Embedded Signup, submit the <code>whatsapp_business_messaging</code> and <code>whatsapp_business_management</code> permissions for review.</p> <hr> <h2 id="7-reference">7. Reference</h2> <ul> <li><p>WhatsApp Cloud API version used by RedBot: <code>v22.0</code> (see <a href="https://www.notion.so/lib/platforms/whatsapp/index.js"><code>lib/platforms/whatsapp/index.js</code></a>).</p> </li> <li><p>Callback path: <code>/redbot/whatsapp</code></p> </li> <li><p>Health check: <code>/redbot/whatsapp/test</code> returns <code>ok</code></p> </li> <li><p>Meta docs: <a href="https://developers.facebook.com/docs/whatsapp/cloud-api">https://developers.facebook.com/docs/whatsapp/cloud-api</a></p> </li> </ul> </script> <script type="text/javascript"> $.RedBot.registerType('chatbot-whatsapp-send', { category: $.RedBot.config.name + ' Platforms', color: '#FFCC66', defaults: { bot: { value: "", type: 'chatbot-whatsapp-node', required: true }, botProduction: { value: '', type: 'chatbot-whatsapp-node', required: false }, track: { value: false }, passThrough: { value: false }, errorOutput: { value: false }, outputs: { value: 0 } }, inputs: 1, outputs: 1, icon: 'whatsapp.svg', paletteLabel: 'Whatsapp Sender', label: function () { return 'Whatsapp 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-whatsapp-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-whatsapp-send"> <p>Output node for Whatsapp.</p> </script>