PIONEERS Facebook Messenger Webhooks

DO YOU HAVE FACEBOOK MESSENGER INSTALLED ON YOUR MOBILE DEVICE?

  • YES

  • NO


Results are only viewable after voting.

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
@ABSGINC Re: "Speaking of the ngrok command, I don't know how to get back to my shell without opening another shell, as I haven't dug as deep to see ngrok run as a service and persistent. I've ran in to these same issues when deploying the django server, I have to open another shell as starting the server from what i learned in the tutorials leave the status and conole up, and breaking out stops the server, both ngrok and the django webserver. "

Read up on the screen command. It works perfectly for these apps.

Code:
screen
./ngrok http 8000
Ctrl-a then Ctrl-d to exit 2d screen
use: screen -r to return
or: screen -list to show other screens by number
THEN: curl http://localhost:4040/api/tunnels (to decipher https tunnel address set up by ngrok)
 

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
Anything that runs overnight and still works the next morning is my kind of app. Fun times!

I managed to break it tonight, possibly by sending the default emoticon, holding down the thumb up..I received no reply, and it stopped replying back to me in my echo example. logs shows
Code:
  File "/var/lib/asterisk/agi-bin/virtenv/alertfb/fb_alertfb/views.py", line 39, in post
    sendthis =  "I think you said " +  message['message']['text']
KeyError: 'text'
[20/Sep/2017 03:07:00] "POST /fb_alertfb/3c------------dc86/ HTTP/1.1" 500 93877
CURL pushes still send out messages correctly.. I am not very proficient iin python either, but I'll have some more time with it this week.
I vaguely understand the error, but am confused how I got it to start out of nowhere.
 

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
I managed to break it tonight, possibly by sending the default emoticon, holding down the thumb up..I received no reply, and it stopped replying back to me in my echo example. logs shows
Code:
  File "/var/lib/asterisk/agi-bin/virtenv/alertfb/fb_alertfb/views.py", line 39, in post
    sendthis =  "I think you said " +  message['message']['text']
KeyError: 'text'
[20/Sep/2017 03:07:00] "POST /fb_alertfb/3c------------dc86/ HTTP/1.1" 500 93877
CURL pushes still send out messages correctly.. I am not very proficient iin python either, but I'll have some more time with it this week.
I vaguely understand the error, but am confused how I got it to start out of nowhere.

Figured it out. When attachments are sent, you're not going to find the key message:text. So in this setup, sending an emoticon or attachment might crash your bot. The solution to get it running again was to temporarily comment out the lines that deal with string manipulation and sending, then let those messages pass through your bot without trying to read fields that are not there.

So with this, the incoming hook needs some work. I was hoping we could emulate this on php and a webserver.

Wanted to share with you right away that the current code will bonk out when message text is absent, it seems to hold up everything else too until you can fully acknowledge the last sent announcement in code.
 

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Just a little update for @ABSGINC and others. Getting this to work one time is only moderately difficult because of some missing instructions in the tutorial which may just be outdated a bit.

But getting this to work reliably following a reboot is a huge can of worms unless you want to spring for $5 a month to get a permanent https FQDN from NGROK. Otherwise, every time you restart ngrok, you get a new FQDN which means you have to rework your setup at Facebook as well. That is a huge hassle so...

We prefer free so we're setting up our own Ngrok server that everybody here will be welcome to use with a custom FQDN that you can keep at no cost. If you're not familiar with ngrok, it's a slick little app that hands out an HTTPS FQDN that sets up a tunnel localhost:8000 for Facebook to use in our case.

Those that wish to do so may donate to the cause to keep the cloud server running. There's a link at the top right of Nerd Vittles home page.

This design requires that you use a custom ngrok app which I also will provide. I'm in the process of adding a Let's Encrypt certificate to the Vultr server and adding the code to keep it active. Once that's done, I'll provide more details.
 
Last edited:

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
Just a little update for @ABSGINC and others. Getting this to work one time is only moderately difficult because of some missing instructions in the tutorial which may just be outdated a bit. I'll fix that with a Nerd Vittles tutorial that addresses our environment with both Debian 8 AND CentOS 7.

But getting this to work reliably following a reboot is a huge can of worms unless you want to spring for $5 a month to get a permanent https FQDN from NGROK. Otherwise, every time you restart ngrok, you get a new FQDN which means you have to rework your setup at Facebook as well. That is a huge hassle so...

We prefer free so we're setting up our own Ngrok server that everybody here will be welcome to use with a custom FQDN that you can keep at no cost. You could even donate to the cause to keep the cloud server running if you'd like. This requires that you use a custom ngrok app which I also will provide. I'm in the process of adding a Let's Encrypt certificate and adding the code to keep it active. Once that's done, I'll provide more details.

Very cool @wardmundy . They'll be many other uses for that https tunnel staying reliable on reboot.

What were your thoughts on this https traffic and the django server, I've seen other examples using heroku, jnode. with python in the example i started with this seemed like the least extra dependencies with what i already had going on the pbx. But what about a virtual Apache instance or ngnix with a https certificate, with an htaccess file and rewrite rules or whatever to handle these. That seems like less overhead, unless Im missing something.
 

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Ngrok is a very lightweight server. And I'm running Django on existing Incredible PBX servers with no degradation. The hassle of forcing everyone to obtain and maintain certificates for HTTPS access (which Facebook requires) would pretty much kill the project. The ngrok approach reduces this to about a one-minute HTTPS setup. As for web traffic and overhead, I guess we'll just have to see. If the bandwidth gets burdensome, we may have to pass the hat for donations, but these are simple little messages so I think we're probably OK for the time being.
 
Last edited:

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Good news and bad news. I've scrapped Ngrok. The free source code simply doesn't work after a day of trying dozens of wild goose chases. So... I ended up installing Certbot-apache with LetsEncrypt using PIP not YUM on the Issabel platform. That, too, was a can of worms thanks to old yum code that blows up. But I think I can reproduce what took a full day of wrestling with bugs to finally conquer. That meant I no longer needed Django either.

Anyway, I now have a stable Apache HTTPS platform that talks to FB using generic PHP code (attached) and still runs Issabel on HTTP. We can figure out how to run both on HTTPS once I get things chugging along. Once HTTPS was in place, the Facebook piece was incredibly easy... maybe because I've now done it about a dozen times. And the results in 30 minutes are quite impressive. Here is a Facebook Messenger session from this afternoon. Tomorrow we'll make some real calls instead of just pretending...

DKR5Z1oU8AAb1gL.jpg



See this thread for the latest code.

WARNING: This original design is intended for admins to manage their own PBXs via a Facebook private page. It is not designed for public access to your PBX via a public Facebook page unless you want to have strangers making calls on your nickel or allowing anonymous individuals the keys to the castle sufficient to enable total destruction of your PBX.
 
Last edited:

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
Excellent work in the bot logic.

I spent the majority of the day researching the REST API, Wazo's different API's and webooks and decided that this will end up molding in here eventually most likely. After figuring out this masterpiece you could then discard the self signed certificate generated with wazos install and generate your own for Wazo and the rest is all familiar as Facebook's API lines right up if you made a service specifically for it..

It's got a ways to go, but I feel like this is the direction this needs to head.

I'll be sharing my progress and hopefully talking to @Sylvain Boily soon :)

Good news and bad news. I've scrapped Ngrok. The free source code simply doesn't work after a day of trying dozens of wild goose chases. So... I ended up installing Certbot-apache with LetsEncrypt using PIP not YUM on the Issabel platform. That, too, was a can of worms thanks to old yum code that blows up. But I think I can reproduce what took a full day of wrestling with bugs to finally conquer. That meant I no longer needed Django either.

Anyway, I now have a stable Apache HTTPS platform that talks to FB using generic PHP code (attached) and still runs Issabel on HTTP. We can figure out how to run both on HTTPS once I get things chugging along. Once HTTPS was in place, the Facebook piece was incredibly easy... maybe because I've now done it about a dozen times. And the results in 30 minutes are quite impressive. Here is a Facebook Messenger session from this afternoon. Tomorrow we'll make some real calls instead of just pretending...

DKR5Z1oU8AAb1gL.jpg


Code:
$verify_token = "YOUR-SECRET-CODE";
$access_token = "YOUR-FB-GENERATED-ACCESS-TOKEN";

$hub_verify_token = $verify_token ;
$hub_challenge = "" ;

if (!empty($_REQUEST['hub_verify_token'])) :
 $hub_verify_token = $_REQUEST['hub_verify_token'];
 $hub_challenge = $_REQUEST['hub_challenge'];

 if ( $hub_verify_token == $verify_token ) :
  echo $hub_challenge;
 else :
  exit;
 endif;
endif;

// handle bot's anwser
$input = json_decode(file_get_contents('php://input'), true);
$senderId = $input['entry'][0]['messaging'][0]['sender']['id'];
$messageText = $input['entry'][0]['messaging'][0]['message']['text'];
if ( $messageText == "howdy" ) :
    $answer = "Hi there and welcome to BotWorld.";
    $response = [
 
        'recipient' => [ 'id' => $senderId ],
        'message' => [ 'text' => $answer ]
    ];

    $ch = curl_init('https://graph.facebook.com/v2.6/me/messages?access_token='.$access_token);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($response));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_exec($ch);
    curl_close($ch);
endif;
if ( substr($messageText,0,4) == "call" ) :
    $answer = "Placing call to ". substr($messageText,5) . " now. One moment please...";
    $response = [
 
        'recipient' => [ 'id' => $senderId ],
        'message' => [ 'text' => $answer ]
    ];

    $ch = curl_init('https://graph.facebook.com/v2.6/me/messages?access_token='.$access_token);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($response));
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
    curl_exec($ch);
    curl_close($ch);
endif;
 
Last edited by a moderator:

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Just a couple of lessons learned. First, there appears to be zero security once the original handshake with your server is established during the setup process. This means we'll have to lock down port 443 to your TM3 whitelist plus all of the Facebook IP addresses. Otherwise, anybody can hit your web server with messaging requests. It's probably a good idea to hide this FB directory in a very long random directory name, e.g. 394959637263311 NOT facebook or messenger. But this may cause problems with Wazo updates because they overwrite all of the web directory permissions, and they'd wipe out your webhooks access.

I've also modified the code above and added a new "!" command that lets you run any command you could run from the Linux CLI by sending a FB message prefaced with !, e.g. !ls would list your directory contents in the web folder for FB messaging or !asterisk -rx "database show CF" would show all of your extensions that have call forwarding enabled. It's very powerful AND very dangerous. Also a new "?" command will show that FB is passing no hidden variables with these messages. Like I said there's zero security once someone has permissions to send you messages on FB.

To secure port 443 to your whitelist, be sure that the port is not exposed in /etc/sysconfig/iptables (CentOS) or /etc/iptables/rules.v4 (Debian/Ubuntu/Raspbian):
Code:
sed -i 's|443|450|' /etc/sysconfig/iptables
sed -i 's|443|450|' /etc/iptables/rules.v4
iptables-restart

For Issabel and Incredible PBX 13, add this code to the bottom of /usr/local/sbin/iptables-restart to whitelist all of the FB servers:
Code:
whois -h whois.radb.net -- '-i origin AS32934' | grep ^route: | sed "s|route:     |/usr/sbin/iptables -A INPUT -s |" | sed "s|$| -p tcp -m tcp --dport 443 -j ACCEPT|" > /usr/local/sbin/iptables-facebook
chmod +x /usr/local/sbin/iptables-facebook
/usr/local/sbin/iptables-facebook

For Wazo and Incredible PBX for Wazo, add this code to the bottom of /usr/local/sbin/iptables-restart to whitelist all of the FB servers:
Code:
whois -h whois.radb.net -- '-i origin AS32934' | grep ^route: | sed "s|route:     |/sbin/iptables -A INPUT -s |" | sed "s|$| -p tcp -m tcp --dport 443 -j ACCEPT|" > /usr/local/sbin/iptables-facebook
chmod +x /usr/local/sbin/iptables-facebook
/usr/local/sbin/iptables-facebook


*** IMPORTANT *** Be sure that the whois package is installed on your server, e.g. yum install whois or apt-get install whois

You'll need to remember to periodically restart IPtables with iptables-restart or do it nightly in /etc/crontab since Facebook adds new server IP addresses from time to time:
Code:
20 0 * * * root /usr/local/sbin/iptables-restart >/dev/null 2>&1

Finally, with Wazo, you'll need to enable access to the web directory you created. Assuming it's /var/www/html/fb, edit /etc/nginx/locations/https-available/01_incrediblepbx.

At the top of the file, add:
Code:
location ~* ^/fb/. *\(?:ico|css|js|gif|jpe?g|png)${
 root /var/www/html;
}

At the bottom of the file, add:
Code:
location ~ /fb/ {
 root /var/www/html;
 index index.php;
 try_files $uri $uri/ =404;
 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
 fasstcgi_index index.php;
 include fastcgi_params;
 fastcgi_pass unix:/var/run/php5-fpm.sock;
}

Finally, restart NGINX: service nginx restart

All of this code is required just to add a web directory with NGINX. YIKES! Now you see why mere mortals use Apache. :cool:
 
Last edited:

billsimon

Well-Known Member
Joined
Jan 2, 2011
Messages
1,534
Reaction score
727
A remote shell via Facebook? What could possibly go wrong? :eek6: Captain Obvious here to say, don't do this on a production server.
 

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31

Don't go accidentally setting off the launch codes ;-) :rofl:

The whitelisting method is good, and as long as you don't go requesting app reviews and turn your app on public, only you as an authenticated facebook user should be able to get through on facebooks ip's and talk to your bot. Of course you would need to keep your facebook account secure and be aware of any moderators or testers you have connected to your facebook application.
 
Last edited by a moderator:

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
And let's remember something when we deal with free services through their provided API.


Facebook, I trust them enough, not that I probably should. They do a lot of weird social experiments, (once reading an article stating how facebook saves the messages you didn't send, and the posts you never posted, when you thought twice about posting or hitting send, they saved it, not for you though. Kind of weird) and when we add audio attachments, our text, etc, I'm sure they have a privacy policy, but it might just scare you a little too. Anyone read an itunes service agreement? However, with everything cool, a price, and so far, I don't think I care what Facebook does with my exchanges between me and my bot.

Same goes for all our free Google Voice calls. The more you talk about something in particular on your google voice channels the more related content you will receive if you have your chrome browser connected. Call me crazy. I don't mind it though. It's the way the world is going, the Google Homes, the Amazon Echos. The only thing that concerns me with these common devices are possible vulnerabilities that could allow someones device to be hacked for the purpose of being spied on. This may become more popular then we think in the near future.

I've also been playing with Googles two-factor authentication, using the google authenticator app in other applications, I may introduce that to the pbx's soon if not already seen. Who loves a changing passcode? lol, actually i hate those things, but it might just add a hefty level of security for lets say a DISA

I love looking into new APIs that help in not reinventing the wheel so to say, but plenty of discretion always is required when deploying a third party provided service in something we want to stay glued together. Thankfully we have our open source communities. Reputation and trust is a key, and Facebook at least doesn't seem like it's going anywhere anytime soon, that's the good news here.

I'm liking what I see happening on this forum, it's fun. It's better on the ground floor.
 
Last edited by a moderator:

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
@billsimon We need to like everything else that is uncertain to just simply keep an eye on it.

Tomorrow facebook might decide to deliver you advertisements in your messenger conversations, and over night your pbx think it received a command to shutdown, .001% chance of that. Also its our responsibility to keep our app tight in only accepting communication authenticated and exiting our scripts when the conditions aren't right.

We've had this for less then a week, but I already feel its moderately secure. I would most certainly trust it on a production environment once ironed out, not every ones bot app has to accept shell commands. Some can simply alert you of calls and take commands back for how to route them.
 
Last edited by a moderator:

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Simple answer is to replace ! with a random string that you're comfortable with. Then the CLI feature is only available with that prefix. Or you can delete that section of code permanently.

Funny thing is that every time I write about all the ways to keep your server secure, I get a raft of :shit: on the DSL Reports Forum for being paranoid.

This code is still early alpha, experimental obviously. There are many layers of security. There's an IPtables firewall with no public access and all of Facebook's firewalls and security mechanisms as well. We never leave more than $10 in a VoIP provider account and never with replenishment turned on. I usually use Google Voice numbers on accounts with these new features, and the Google account always has a zero balance for making calls. Having said all of that, I really can't imagine anyone ever turning this on for the entire Facebook community to start messaging you. And, yes, lots can go wrong. That's what all the security layers are for.

:rant:
 
Last edited:

Sylvain Boily

Active Member
Joined
Apr 30, 2016
Messages
259
Reaction score
144
@ABSGINC what's your current status? Looks like in the example you don't use wazo APIs. I'm looking how it works and be back. Are you coming at the astricon finally?
 

ABSGINC

You can call me Scott.
Joined
Oct 1, 2014
Messages
59
Reaction score
31
@ABSGINC what's your current status? Looks like in the example you don't use wazo APIs. I'm looking how it works and be back. Are you coming at the astricon finally?

@Sylvain Boily you're correct, I'm not yet using the Wazo API's. As this week I just came across the Wazo API's and got pretty excited to see those as they look to work very similar to how facebook's apis communicate.. I asked if anyone else worked with them, but I have not got a lot of feedback. I would like to get a better understanding of them so that I can better create content that will work with Wazo.

I am planning on coming to Asticon. I think I am arriving in Orlando on Tuesday the week following next.
 

Sylvain Boily

Active Member
Joined
Apr 30, 2016
Messages
259
Reaction score
144
Yes there is many APIs in wazo. REST Api for the command/control/init, a websocket to get events and a webhook service to push http (or what you want) notification based on events you subscribed. I give a talk about wazo and this interfaces. I'll be there monday, so send me an email sylvainboilydroid on gmail and i give you my phone number.

For information it's possible to use let's encrypt in wazo, i started a plugin, but i haven't yet finish.

https://github.com/sboily/wazo-plugind-letsencrypt

The webhook service is very new, but it offer many possibilities :).

There is also a plugin to control the webhook by the new interface. You need to have wazo 17.14 (dev version)

https://github.com/wazo-pbx/wazo-admin-ui-webhook

http://pix.toile-libre.org/upload/original/1506112669.png
 

wardmundy

Nerd Uno
Joined
Oct 12, 2007
Messages
19,168
Reaction score
5,199
Getting Wazo and NGINX configured for Facebook webhooks was more of a bear. You also have to manually install and configure certificates with certbot and LetsEncrypt since it knows nothing about NGINX. Neither do I, by the way. @Sylvain Boily has bailed me out more than once.

First, you'll need to get certbot installed:
Code:
cd /etc/apt
echo "deb http://ftp.debian.org/debian jessie-backports main" >>  sources.list
apt-get update
apt-get install certbot -t jessie-backports

Next, you have to temporarily turn off the HTTPS setup for Wazo since the certif install requires HTTP access. In /etc/nginx/sites-enabled/xivo, comment out these 3 lines:
Code:
In server section for port 80:
#    include /etc/nginx/locations/http-enabled/*;
In server section for port 443:
 #   listen 443 default_server;
 #   server_name $domain;
Then restart the web server: /etc/init.d/nginx restart. Now you have a basic http web server.

Next, in /var/www/html:
Code:
cd /var/www/html
mkdir .well_known
cd .well_known
mkdir acme-challenge
cd acme-challenge
chown -R asterisk:www-data /var/www/html/.well_known

Disable the firewall temporarily: /etc/init.d/netfilter-persistent flush

Leave that SSH session as it is and open a second SSH session to your server to kick off the certbot script: certbot certonly --manual

You'll be prompted for the FQDN of your server to generate the certificates. Then you'll be given an oddball name AND an expected oddball response. Use the name to create a directory under /var/www/html/.well_known/acme-challenge. In your other session:
Code:
mkdir ODDBALL-NAME
cd ODDBALL-NAME
echo "ODDBALL-RESPONSE > index.html"
chown -R asterisk:www-data /var/www/html/.well_known

Now, use a browser to go to http://YOUR-FQDN/.well_known/acme-challenge/ODDBALL-NAME/ and be sure your web server displays the expected response. You've got to get this working before you continue with the certbot install or it will fail. And you only have a few minutes to do it before certbot with change the ODDBALL-NAME and ODDBALL-RESPONSE. 3 consecutive failures and you have to wait an hour to try again. Guess how we know?

Once you get the expected response, switch back to your SSH session with certbot and press ENTER to continue with the certificate install. When it completes, you'll get a congratulatory note and a reminder that, in less than 90 days, you'll need to run certbot renew to update your certificate.

Now let's install the new certificates in NGINX and put things back together again.
Code:
cd /etc/nginx/sites-enabled
nano -w xivo

First, remove the 3 comment lines we added previously.

Second, at the bottom of the file, comment out these existing certificate lines:
Code:
#    ssl_certificate /usr/share/xivo-certs/server.crt;
#    ssl_certificate_key /usr/share/xivo-certs/server.key;
#    ssl_ciphers ALL:!aNULL:!eNULL:!LOW:!EXP:!RC4:!3DES:!SEED:+HIGH:+MEDIUM;

Third, add these new lines using your actual FQDN:
Code:
    ssl_certificate /etc/letsencrypt/live/YOUR.FQDN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/YOUR.FQDN/privkey.pem;
    ssl_ciphers HIGH:MEDIUM:!aNULL:!MD5:!SEED:!IDEA;

Finally, restart your firewall and NGINX:
Code:
iptables-restart
/etc/init.d/nginx restart

Now comment out the jessie-backports addition in /etc/apt/sources-list and apt-get update.

Verify that you have a working HTTPS implementation by going to your FQDN with a browser. You should be greeted by the Wazo login screen. And Chrome will tell you that your site is SECURE.

Someday soon, we'll document the Facebook piece (which actually works!). To be continued...

DKamsV8XoAAmX59.jpg
 
Last edited:

Members online

Forum statistics

Threads
25,782
Messages
167,509
Members
19,203
Latest member
frapu
Get 3CX - Absolutely Free!

Link up your team and customers Phone System Live Chat Video Conferencing

Hosted or Self-managed. Up to 10 users free forever. No credit card. Try risk free.

3CX
A 3CX Account with that email already exists. You will be redirected to the Customer Portal to sign in or reset your password if you've forgotten it.
Top