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
HTML
<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'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://<your-tunnel>.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'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'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 "Business Manager") at <a href="https://business.facebook.com/">https://business.facebook.com/</a>. Create one if you don'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 "hello world" 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://<your-tunnel>.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'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://<your-tunnel>.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'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'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'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'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'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't need App Review. If you'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>