Getting Invalid signature on incoming request with botBuilder adapter for Facebook - node.js

I have successfully deployed this example repo to azure and it is now working in the web chat and on slack.
Now I'm trying to use the facebook adapter in my bot. I have followed the instructions to use FacebookAdapter with BotBuilder and added the following code into index.js
const { FacebookAdapter } = require('botbuilder-adapter-facebook');
const restify = require('restify');
const adapter = new FacebookAdapter({
verify_token: process.env.FACEBOOK_VERIFY_TOKEN,
app_secret: process.env.FACEBOOK_APP_SECRET,
access_token: process.env.FACEBOOK_ACCESS_TOKEN
});
const server = restify.createServer();
server.use(restify.plugins.bodyParser());
server.use(restify.plugins.queryParser());
server.get('/api/messages', (req, res) => {
if (req.query['hub.mode'] === 'subscribe') {
if (req.query['hub.verify_token'] === process.env.FACEBOOK_VERIFY_TOKEN) {
const val = req.query['hub.challenge'];
res.sendRaw(200, val);
} else {
console.log('failed to verify endpoint');
res.send('OK');
}
}
});
server.post('/api/messages', (req, res) => {
adapter.processActivity(req, res, async(context) => {
await context.sendActivity('I heard a message!');
});
});
server.listen(process.env.port || process.env.PORT || 3000, () => {
console.log(`\n${ server.name } listening to ${ server.url }`);
});
also in my .env file I have added the various tokens and secrets required.
When I try testing the app locally with bot framework emulator I get the error
(node:11588) UnhandledPromiseRejectionWarning: Error: Invalid signature on incoming request
at FacebookAdapter.<anonymous> (/home/ronald/Desktop/03.welcome-users/node_modules/botbuilder-adapter-facebook/lib/facebook_adapter.js:421:23)
at Generator.next (<anonymous>)
at /home/ronald/Desktop/03.welcome-users/node_modules/botbuilder-adapter-facebook/lib/facebook_adapter.js:15:71
I'm not sure what I'm doing wrong

Unfortunately, this appears to be a bug of some variety. An issue already exists on the Botkit Github repo with various customers experiencing a similar problem, however there is no fix at this time. It seemingly doesn't affect all customers, as the Botkit developer (at the time of his posting) was able to use the adapter without error.
In looking into your problem, I was able to determine that the error is generated from the verifySignature() method in the FacebookAdapter class. There should be an "x-hub-signature" header returned from Facebook which is used to check the signature of the request payload for the webhook event. For unknown reasons, this header is missing which results in the "invalid signature" message.
I would recommend you comment on the above GH issue to help facilitate work on the problem.
Hope of help!
-----EDIT-----
The Facebook Adapter is designed to work independently of the Azure Bot Service / ABS Channels, even when integrated with a BotFramework bot. As such, it will not work with BotFramework Emulator. It is designed to connect directly to the bot's adapter.
This also means you need to adjust the Webhook Callback URL in your Facebook app settings to point to your locally running bot. The webhook value, when configured for ABS looks something like:
https://facebook.botframework.com/api/v1/bots/[botname].
You will want to adjust it to point to your ngrok endpoint (used for tunneling between your local bot and external sources like Facebook). The new webhook value would look something like this:
https://1a04e4ad.ngrok.io/api/messages.
Don't forget to include the verify token which also comes from Facebook (found in the settings).
Assuming you've changed the webhook url, supplied the verify token, and are NOT using Emulator, then it should work seemlessly.
Note: Facebook sends echos and delivery confirmations for every event passed. The result is, if you don't filter on incoming event types or turn off additional events in Facebook, then your bot will be hit continuously.

Related

BotFramework-DirectLine JS - Initial Message Missing

I have a Bot I have built with MS BotFramework, hosted on Azure. The Bot is built to start the convo with a Welcome Message. When I test the bot through emulator, or on Azure test webchat, the bot will initiate the conversation as expected with the welcome message.
However, in my chat client using BotFramework-DirectLineJS, it isn't until I send a message that the bot will respond with the Welcome message (along with a response to the message the user just sent).
My expectation is that when I create a new instance of DirectLine and subscribe to its activities, this Welcome message would come through. However, that doesn't seem to be happening.
Am I missing something to get this functionality working?
Given this is working for you on "Test in Webchat", I'm assuming your if condition isn't the issue, but check if it is if (member.id === context.activity.recipient.id) { (instead of !==). The default on the template is !== but that doesn't work for me outside of emulator. With === it works both in emulator and other deployed channels.
However, depending on your use cases you may want to have a completely different welcome message for Directline sessions. This is what I do. In my onMembersAdded handler I actually get channelId from the activity via const { channelId, membersAdded } = context.activity;. Then I check that channelId != 'directline' before proceeding.
Instead, I use the onEvent handler to look for and respond to the 'webchat/join' event from Directline. That leaves for no ambiguity in the welcome response. For a very simple example, it would look something like this:
this.onEvent(async (context, next) => {
if (context.activity.name && context.activity.name === 'webchat/join') {
await context.sendActivity('Welcome to the Directline channel bot!');
}
await this.userState.saveChanges(context);
await this.conversationState.saveChanges(context);
})
You'll still want to have something in your onMembersAdded for non-directline channel welcome messages if you use this approach.

How to use telegraf webhook?

I want to use webHook in telegraf but i don't know how can i use it correctly.
Here is my simple code.
But it still uses polling.
const Telegraf = require('telegraf');
const bot = new Telegraf('123:ABC');
bot.telegram.setWebhook('https://myaddress.com');
bot.startWebhook(`/`, null, 4000);
bot.use(function(ctx, next){
try{
if(ctx.chat == undefined) return;
console.log("Hello World");
}catch (e){
console.log("Error");
}
});
bot.launch();
When bot.startWebhook() is called Telegraf will start listening to the provided webhook url, so you don't need to call bot.launch() after that.
Also bot.launch() will start the bot in polling mode by default if no options are specified as in your case.
Remove bot.launch() and the bot should start in webhook mode.
Telegraf.js ^4.0.0
If you're using Telegraf.js version 4.0 or higher the changelog states that:
Bots should now always be started using bot.launch with the corresponding configuration for either long polling (default) or webhooks.
So you can also try removing bot.telegram.setWebhook() and bot.startWebhook(), adding the following code instead:
bot.launch({
webhook: {
domain: 'https://myaddress.com',
port: 4000
}
})
See this example from the documentation for reference.
This is working for me:
bot.startWebhook('/messages', null, 8443);
bot.launch();
Second parameter are tlsOptions, it's optional.
I have read that Telegram only accepts webhooks on 80, 88, 443 and 8443.
Not sure if true, but is something important to consider since it's very difficult to troubleshoot webhooks.

How to test http request on Dialogflow Fulfillment with the Inline Editor

I am testing Dialogflow Fulfillment with the Inline Editor.
What I am trying to do is a http request using que 'request' library.
Here is the code I am using:
const requesthttp = require('request');
requesthttp('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY', { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
console.log(body.url);
console.log(body.explanation);
});
But It returns me an error of not found.
I also noticed an alert on my Dialogflow with the following message:
"Billing account not configured. External network is not accessible and quotas are severely limited. Configure billing account to remove these restrictions."
So... Probably I can't test this piece of code without configuring a billing account.
My question is... Is there a url that I can use to test this code?
Or the only way for me to test this code is configuring a billing account and paying for it?
Thanks in advance
There are a number of approaches to testing your code.
If you want to continue to use Dialogflow's Inline Editor, you will need to setup Firebase to use a payment plan. However, the Blaze plan is "pay as you go" after a basic level of use. This level of use should be sufficient to cover most testing (and even very light production) uses of the service without imposing a charge. Once your Action has been approved, you're able to receive credits for the Google Cloud Platform, which can be applied to this use in case you go over the minimum level.
You can also use Firebase Cloud Functions, which the Inline Editor is based on, and your own local editor. One advantage of this is that you can serve the function locally, which has many of the same features as deploying it, but doesn't have the URL restriction (it is your own machine, after all). You can use a tool such as ngrok to create a secure tunnel to your your machine during testing. Once you have tested, you can deploy this to Firebase with a paid plan.
You can, of course, choose to use any other hosting method you wish. Google and Dialogflow allow you to run your fulfillment webhook on any server, as long as that server can provide an HTTPS connection using a valid, non-self-signed, certificate. If you're using node.js, you can continue to use these libraries. If you wish to use another language, you will need to be able to parse and return JSON, but otherwise you have no restrictions.
There are a lot of ways to create your own server like using NodeJS client with Express.JS which you can expose to the internet using NGROK as webhook for fulfilment.
Develop a webhook. You can use different client libraries in NodeJS (AoG Client or Dialogflow Client) or in Python (Flask-Assistant or Dialogflow Client) or can create your own just using JSON request/response with Dialogflow and Action-on-Google.
Once the webhook is ready, run it locally and expose to the internet using NGROK.
Start with following code for Actions-on-Google with Express.JS
'use strict';
const {dialogflow} = require('actions-on-google');
const express = require('express');
const bodyParser = require('body-parser');
const app = dialogflow();
app.intent('Default Welcome Intent', conv => {
conv.ask('Hi, Welcome to Assistant by Express JS ');
});
express().use(bodyParser.json(), app).listen(8080);
Since DF uses firebase cloud functions you can use https as in nodejs. But requesting domains outside of the google/firebase universe will require the paid version of firebase.
const https = require('https');
return new Promise((resolve, reject) => {
const hostname = info.hostname;
const pathname = info.pathname;
let data = '';
const request = https.get(`https://${hostname}${pathname}`, (res) => {
res.on('data', (d) => {
data += d;
});
res.on('end', resolve);
});
request.on('error', reject);
});

How to catch Facebook messaging_optins with Azure Bot Channels Registration + Botbuilder SDK?

I've got a chatbot up and running, built using Node.JS Microsoft Bot Framework, and deployed to an Azure server as a Web App, with a Bot Channels Registration resource as the frontend endpoint.
This Bot Channels Registration is connected to Facebook Messenger (via a FB App) - meaning, the webhook for the Facebook App points to https://facebook.botframework.com/api/v1/bots/<BOT_CHANNELS_REGISTRATION_RESOURCE_NAME>.
This all works well for normal chat functionality.
However, I'd now like to add an opt-in checkbox to a separate web page I have. This checkbox works by pinging FB, which then sends a very specific payload to the already configured bot webhook.
{
"recipient":{
"id":"<PAGE_ID>"
},
"timestamp":<UNIX_TIMESTAMP>,
"optin":{
"ref":"<PASS_THROUGH_PARAM>",
"user_ref":"<UNIQUE_REF_PARAM>"
}
}
My question is this:
How does the Bot Channels Registration receive and handle the above payload? Will it just automatically forward it to the Messaging Endpoint I have configured in the Bot Channels Registration settings? Or will it get stuck, and never reach my actual bot Web App?
Finally, if it does reach my normal messages endpoint, how can I handle the specific payload with my botbuilder.ChatConnector() listener? Given that my web app code looks like (in essence)
var restify = require('restify');
var builder = require('botbuilder');
var dialogues = require('./dialogues');
var chatbot = function (config) {
var bot = {};
chatbot.listen = function () {
var stateStorage = new builder.MemoryBotStorage();
var connector = new builder.ChatConnector({
appId: process.env.APP_ID,
appPassword: process.env.APP_PASSWORD
});
bot = new builder.UniversalBot(connector, function (session) {
session.beginDialog(dialogues.base(bot).name);
}).set('storage', stateStorage);
return connector.listen();
};
return chatbot;
}
var server = restify.createServer();
// Listen for messages from users
server.post('/api/messages', chatbot.listen());
server.listen(process.env.port, function () {
console.log('%s listening to %s', server.name, server.url);
});
Thanks!
EDIT: I've figured out how to handle the above payload within my messaging endpoint - by adding a server.pre() handler to my server, e.g.
server.pre(function (req, res, next) {
if (req.body && req.body.optin_payload_specific_field){
// handle opt-in payload
} else {
return next();
}
});
However, via extra logging lines, it seems the opt-in payload isn't even making it to this endpoint. It seems to be stopped within the Bot Channels Registration. Currently looking for a way to resolve that major roadblock.
So, per #JJ_Wailes investigation, it seems like this is not a supported feature (in fact, it's a current feature request). See his comments on the original post for more details.
However, I did find a half-workaround to capture the user_ref identifier generated by the checkbox_plugin, for those interested:
1) From your external site, follow the steps from the documentation here for sending the initial user_ref to FB. FB will then make a callout to your bot, but per the above, that gets blocked by the Bot Channels Registration piece, so it's ignored.
2) From that same external site, use the user_ref to send a message to the user (just using the normal requests library). A successful send means that the user_ref was properly registered in FB by that step #1 call - a failure means you'll need to repeat step #1 (or use some other error handling flow).
3) After that, the next time the user responds to your bot in FB (as long as you don't send any other messages to them), the message your bot will receive will contain this as part of the payload:
{ ...
channelData:
{ ...
prior_message:
{ source: 'checkbox_plugin',
identifier: <user_ref> }
}
}
So I've currently added a check within my bot.use(), where if that section is present in the incoming message payload (session.message.sourceEvent.prior_message) and the source is "checkbox_plugin", I store the corresponding user_ref in the session.userData, and can work from there.
I would love to see this feature added into the supported Azure bot stack, but in the meantime hopefully this helps anyone else encountering this (admittedly niche) hurdle.

Trouble with Twilio / TwiML statusCallback using Node.js

When a user texts my number, I would like to respond with a message using TwiML. I'd also like to know whether my response message sends or fails. I'm creating my TwiML response using the Twilio library for Node.js.
Here is my code:
const response = new twilio.TwimlResponse();
response.message(Response, {
statusCallback: '/sms/status/'
});
Here is the code for the route. Please note that the router is mounted upon the "/sms/" router:
router.post('/status/', acceptRequestsOnlyFromTwilio, (req, res) => {
const Event = require('./../models/schemas/event');
const event = new Event({
description:
`MessageSid = ${req.body.MessageSid}
MessageStatus = ${req.body.MessageStatus}`
});
event.save();
console.log(
`MessageSid = ${req.body.MessageSid}
MessageStatus = ${req.body.MessageStatus}`
);
res.sendStatus(200);
});
When I check my logs using the Heroku CLI, I don't log nor do I see a record when I check MongoDB.
Anyone have any ideas or tips on how I can go about debugging this or if I'm implementing this incorrectly?
Thanks for you time!
Twilio developer evangelist here.
Firstly, have you set your Messaging request URL for the Twilio phone number you are using? Is it pointing at the correct route for your Message TwiML response?
Also, your first code block should also be within a router.post endpoint for your server. Do you correctly return the TwiML response as the server response?
As a tip, it might be better to develop this application locally and test your webhook endpoints using a tool like ngrok. I love using ngrok to test webhooks.

Resources