Send messages on Facebook Messenger as yourself with PhantomJS or facebook-chat-api

Messenger Platform

It looks like there is an official API for chat bots called the Messenger Platform.

But it appears that you need to create a new Facebook page for your bot but I need it to look like the messages come from me.

I can't use this.

PhantomJS

If all you have is a hammer, everything looks like a nail.

In my last post, I wrote about using PhantomJS, a headless browser, to configure websites (like deactivating all plugins during a WordPress installation and then upgrading the blog to a network).

So my first thought was to try it for sending Messenger messages as well.

First, some ground work.

var system = require(' system ') var page = require('webpage').create() var email = system . args [ 1 ] var pass = system . args [ 2 ] var conversation = system . args [ 3 ] var text = system . args [ 4 ] var steps = [] // array of functions

You will need to provide your email as well as your password to log in.

conversation is the conversation (or thread) ID that you can see in the URL in your browser (e.g. https://www.messenger.com/t/firstname.lastname ). It is usually firstname.lastname or sometimes john.smith.99 .

Let's go to messenger.com

function () { console. log ( 'Opening messenger.com' ) page. open ( 'https://www.messenger.com/' ) }

and log in

function ( ) { console .log( 'Logging in' ) page.evaluate( function ( email, pass ) { document .querySelector( 'input[name=email]' ).value = email document .querySelector( 'input[name=pass]' ).value = pass document .querySelector( '#loginbutton' ).click() }, email, pass) },

What I did was right click on the input fields in Google Chrome, click Inspect and look at the <input> elements for email and password.

Then I fill them out programmatically with JavaScript.

I can verify that it works by copy & pasting the document.querySelector code lines in the Google Chrome console.

If I can do that in the Google Chrome console, I can do that in PhantomJS as well.

Everything in page.evaluate(function() { ... }) is executed on the currently open web page. Since email and pass are my variables, they do not exist on messenger.com so I need to pass them as parameters. You can also return a value from page.evaluate .

Next, go to the conversation.

function ( ) { page.open( 'https://www.messenger.com/t/' + conversation) }, function ( ) { console .log( 'Talking to' , page.evaluate( function ( ) { return document .querySelector( 'div[role="main"] h2' ).innerText })) }

Finally, send the message.

function () { text.split( '

' ).for Each( function ( line ) { page.send Event(' keypress ', line ) page.send Event(' keypress ', page . event . key .Enter, null , null , 0x02000000 / * shift * / ) }) page.send Event(' keypress ', page . event . key .Enter) }

I could not figure out how to fake the keyup or paste event (messenger.com uses React and a custom component instead of an <input> ). But the input text box is focused when you open the page and I can just simulate the key presses.

There is no

key so I need to press Shift + Enter to start a new line with the keyboard.

Here is the whole script with some code at the end to execute the array of functions.

var system = require ( 'system' ) var page = require ( 'webpage' ).create() var email = system.args[ 1 ] var pass = system.args[ 2 ] var conversation = system.args[ 3 ] var text = system.args[ 4 ] var steps = [ function ( ) { console .log( 'Opening messenger.com' ) page.open( 'https://www.messenger.com/' ) }, function ( ) { console .log( 'Logging in' ) page.evaluate( function ( email, pass ) { document .querySelector( 'input[name=email]' ).value = email document .querySelector( 'input[name=pass]' ).value = pass document .querySelector( '#loginbutton' ).click() }, email, pass) }, function ( ) { console .log(page.evaluate( function ( ) { return document .querySelector( 'div[role="banner"] a[href="/new"]' ) ? "Logged in" : "Could not log in" })) }, function ( ) { page.open( 'https://www.messenger.com/t/' + conversation) }, function ( ) { console .log( 'Talking to' , page.evaluate( function ( ) { return document .querySelector( 'div[role="main"] h2' ).innerText })) }, function ( ) { text.split( '

' ).forEach( function ( line ) { page.sendEvent( 'keypress' , line) page.sendEvent( 'keypress' , page.event.key.Enter, null , null , 0x02000000 ) }) page.sendEvent( 'keypress' , page.event.key.Enter) }, function ( ) { page.evaluate( function ( ) { console .log( 'Done' ) }) }, function ( ) { setTimeout( function ( ) { phantom.exit() }, 2000 ) } ] var stepindex = 0 var loading = false setInterval(executeRequestsStepByStep, 50 ) function executeRequestsStepByStep ( ) { if (loading == false && steps[stepindex]) { steps[stepindex]() stepindex++ } } page.onLoadStarted = function ( ) { loading = true } page.onLoadFinished = function ( ) { loading = false } page.onConsoleMessage = function ( msg ) { console .log(msg) }

Run it

$ phantomjs messenger.js your@email.com password conversation.name "your message here" Opening messenger.com Logging in Logged in Talking to John Smith Done

If you don't have PhantomJS installed already, this will download and extract a static binary phantomjs to /usr/local/bin .

curl -L https: // bitbucket.org /ariya/ phantomjs /downloads/ phantomjs- 2.1 . 1 -linux-x86_64.tar.bz2 | \ sudo tar xj -C /usr/ local /bin phantomjs-2.1.1-linux-x86_64/ bin /phantomjs --strip-components 2

PhantomJS also works on Windows.

facebook-chat-api

But there's more than one way to skin a cat.

Another way would be to write a script that makes the same HTTP requests that the Messenger client does.

But I had a suspicion that there would be a node.js module for this already. Let's try to use facebook-chat-api.

npm install facebook-chat-api

The script

var login = require ( 'facebook-chat-api' ) var email = process.argv[ 2 ] var pass = process.argv[ 3 ] var conversation = process.argv[ 4 ] var text = process.argv[ 5 ] login({ email: email, password: pass}, ( err, api ) => { if (err) throw err api.getFriendsList( ( err, profiles ) => { if (err) throw err var friendProfile = profiles.filter( p => p.vanity == conversation)[ 0 ] if (friendProfile) { api.sendMessage({ body: text }, friendProfile.userID) } else { throw 'Friend not found' } }) })

Run it

$ node fchat.js your@email.com password conversation.name "your message here" info Logging in .. . info Logged in info Request to reconnect info Request to pull 1 info Request to pull 2 info Request to thread_sync info Done logging in .

Creating an echo bot is just as simple.

This example is taken from the docs. I modified it for brevity.

var login = require ( 'facebook-chat-api' ) var email = process.argv[ 2 ] var pass = process.argv[ 3 ] login({ email: email, password: pass }, ( err, api ) => { if (err) throw err api.listen( ( err, event, stopListening ) => { if (err) return console .error(err) if (event.type === 'message' ) { api.sendMessage(event.body, event.threadID) } }) })

Final remarks

I should point out that doing this is probably against the Terms of Service.

Nevertheless, it was fun playing with PhantomJS.