Skip to content

Instantly share code, notes, and snippets.

@Shaked
Last active July 24, 2020 23:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shaked/177919136e4ffe89f36097fa4a18384d to your computer and use it in GitHub Desktop.
Save Shaked/177919136e4ffe89f36097fa4a18384d to your computer and use it in GitHub Desktop.
Facebook Messenger Platform Webhook Duplicated Messages Bug

Facebook Messenger Platform Webhook Duplicated Messages Bug

https://developers.facebook.com/bugs/143008036331787/

Setup

Download chatbot.php & config.php.

Run $ composer require guzzlehttp/guzzle

Update config.php accordingly.

Set your bot at developers.facebook.com/apps

Interaction

You can send hi to the bot in order to see if it's working.

Once done, you can force it to execute an HTTP call behind scene and decide how much time it should sleep(seconds) by sending a message sleep_NUMBER_OF_SECONDS. For example: sleep_21 which will force the call to sleep 21 seconds.

Code

The bot uses the method acknowledgeRequest in order to provide Messenger a fast response as requested here:

Your webhook should meet the following minimum performance standards:

Respond to all webhook events with a 200 OK. Respond to all webhook events in 20 seconds or less.

This works as long as the HTTP call is not triggered (see lines 24-30 and 60-62 for more information).

Solutions

I know that adding a queue server and process all of the requests from Messenger will probably solve this issue but I am looking for a simple solution i.e KISS - which doesn’t require me over engineering a simple chatbot setup.

I am looking for ideas of how to solve this issue, especially when using a bot which interacts with different APIs behind scene

<?php
require 'vendor/autoload.php';
require 'config.php';
function acknowledgeRequest() {
// In order to return a response and continue processing code, we do this trick.
// this essentially ends the request from the requester (FB)'s perspective.
// source: http://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response
set_time_limit(0);
ob_start();
echo 'success'; // FB does not care return message as long as it's 200 OK
$size = ob_get_length();
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
header('Status: 200 OK');
header('Content-Encoding: none'); // disable compression
header("Content-Length: {$size}");
header('Connection: close');
ob_end_flush();
ob_flush();
flush();
}
if (isset($_GET['http'])) {
$ret = 'hello from http request';
if (isset($_GET['sleep'])) {
sleep((int) $_GET['sleep']);
$ret .= ' slept for ' . (int) $_GET['sleep'];
}
echo $ret;
die;
}
acknowledgeRequest();
// check token at setup
if (isset($_REQUEST['hub_verify_token']) && $_REQUEST['hub_verify_token'] === $hubVerifyToken) {
echo $_REQUEST['hub_challenge'];
exit;
}
// handle bot's anwser
$fbrequest = file_get_contents('php://input');
$input = json_decode($fbrequest, true);
$senderId = $input['entry'][0]['messaging'][0]['sender']['id'];
if (!isset($input['entry'][0]['messaging'][0]['message'])) {
error_log('this is not a message, no need to reply. fb request: ' . PHP_EOL . PHP_EOL . $fbrequest);
die;
}
$messageText = $input['entry'][0]['messaging'][0]['message']['text'];
$response = null;
$answer = 'blah';
//set Message
if ($messageText == 'hi') {
$answer = 'Hello';
}
$sleepTime = 0;
if (strpos($messageText, 'sleep_') !== false) {
list(, $sleepTime) = explode('_', $messageText);
}
$client = new \GuzzleHttp\Client();
$res = $client->request('GET', $entryPoint . '&sleep=' . $sleepTime);
$status = $res->getBody()->getContents();
$answer .= ' using acknowledgeRequest()';
$answer .= ' answer from http request: ' . $status;
$answer .= "\n\n FB REQUEST: " . $fbrequest;
//send message to facebook bot
$response = [
'recipient' => ['id' => $senderId],
'message' => ['text' => $answer],
];
$ch = curl_init('https://graph.facebook.com/v2.10/me/messages?access_token=' . $accessToken);
error_log('response from test: ' . json_encode($response));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($response));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
if (!empty($input)) {
$result = curl_exec($ch);
}
curl_close($ch);
<?php
$appSecret = 'SECRET';
$appId = 'APP_ID';
$entryPoint = 'https://pymwymi.io/bot-test/bot.php?http=true';
$hubVerifyToken = 'HUB VEIRFY TOKEN';
$accessToken = 'PAGE TOKEN';
sleep_2
TheProcess
blah answer from http request: hello from http request
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609445717,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609444865,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JwoAVft1bfqtwNH","seq":622503,"text":"sleep_2"}}]}]}
Shaked
sleep_21
TheProcess
blah answer from http request: hello from http request
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609456300,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609470847,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609485397,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609500077,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609515260,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609532892,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609563865,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
Shaked
-------
hi
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609651051,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609749162,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609845732,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510609941518,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610038863,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610128410,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610220311,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610305916,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610406376,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
TheProcess
blah using acknowledgeRequest() answer from http request: hello from http request slept for 21
FB REQUEST: {"object":"page","entry":[{"id":"1637124163250118","time":1510610504513,"messaging":[{"sender":{"id":"1522824674478341"},"recipient":{"id":"1637124163250118"},"timestamp":1510609455457,"message":{"mid":"mid.$cAAXQ9Ib_cmJl6JxRYVft1cJDAiRB","seq":622506,"text":"sleep_21"}}]}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment