php中引入facebook的messenger消息接口

时间:2024-02-21 13:59:58

前一段时间需要开发一个messenger的消息接口,但是facebook的官方文档似是而非,而且由于在国内比较小众,之前也没有另外的人写过中文的开发教程,只好自己进行了一番研究并完成了一个demo,希望给后来的人能带来一点方便。

一.创建Facebook应用和主页

https://developers.facebook.com/docs/messenger-platform/guides/quick-start

直接按照官方文档的第一步去完成即可

二.设置webhook

这里的webhook实际上就是你的第三方服务器,这里facebook的主页类似于微信的公众号。其中webhook的callback url就是回调函数的地址。类似于微信的服务器验证方式,这里的webhook也需要返回给facebook服务器指定的信息。

在 webhook 网址中,添加身份验证代码。此代码应对应上述验证口令,并以验证请求中发送的 challenge 为响应。点击“新建主页订阅”窗口中的“验证并保存”,以便通过 GET 请求调用 Webhook

然后给了一个官方案例,返回信息

app.get(\'/webhook\', function(req, res) {
  if (req.query[\'hub.mode\'] === \'subscribe\' &&
      req.query[\'hub.verify_token\'] === <VERIFY_TOKEN>) {
    console.log("Validating webhook");
    res.status(200).send(req.query[\'hub.challenge\']);
  } else {
    console.error("Failed validation. Make sure the validation tokens match.");
    res.sendStatus(403);          
  }  
});

我不是很熟悉node.js,发现get[‘hub.verify_token’]并不能拿到并返回验证的信息,这里贴出我的php的验证方式

private function setupWebhook()
    {
        if(isset($_REQUEST[\'hub_challenge\']) && isset($_REQUEST[\'hub_verify_token\']) && $this->getValidationToken()==$_REQUEST[\'hub_verify_token\'])
        {
            echo $_REQUEST[\'hub_challenge\'];
            exit;
        }
    }

 

hub_verify_token就是你自己设定的token,需要验证每次get的token,然后echo出hub_challenge

三.获取主页访问口令并订阅主页

建议把所有配置信息放在单独的文件里面,方便调用和修改。生成的口令不会在此界面保存。每次选择此主页都会生成一个新的口令。但之前生成的任何口令仍然有效。

四.接收消息
完成订阅后,系统需要在 webhook 中侦听 POST 调用。所有回调都将向此 webhook 发出。

Messenger Platform 的所有回调都使用相同的结构。

{
  "object":"page",
  "entry":[
    {
      "id":"PAGE_ID",
      "time":1458692752478,
      "messaging":[
        {
          "sender":{
            "id":"USER_ID"
          },
          "recipient":{
            "id":"PAGE_ID"
          },

          ...
        }
      ]
    }
  ]
} 

纯文字信息的详细结构

{
  "object":"page",
  "entry":[
    {
      "id":"PAGE_ID",
      "time":1458692752478,
      "messaging":[
        {
          "sender":{
            "id":"USER_ID"
          },
          "recipient":{
            "id":"PAGE_ID"
          },
          "timestamp":1458692752478,
          "message":{
            "mid":"mid.1457764197618:41d102a3e1ae206a38",
            "seq":73,
            "text":"hello, world!",
            "quick_reply": {
              "payload": "DEVELOPER_DEFINED_PAYLOAD"
            }
          }
        }
      ]
    }
  ]
} 

附上webhook的参考文档的地址

https://developers.facebook.com/docs/messenger-platform/webhook-reference
  • 1

关于具体处理消息的过程,我贴上我的代码
入口文件

require_once \'config.php\';
require_once \'FacebookBot.php\';
$bot = new FacebookBot(FACEBOOK_VALIDATION_TOKEN, FACEBOOK_PAGE_ACCESS_TOKEN);
$bot->run();
$messages = $bot->getReceivedMessages();
 foreach ($messages as $message)
 {
    $recipientId = $message->senderId;
    $text=$message->text;
    if($text)
    {
        $bot->sendTextMessage($recipientId, $text);
    }
    elseif($message->attachments)
    {
        $bot->sendTextMessage($recipientId, "Attachment received");
    }
}

定义类的文件

<?php
require_once \'config.php\';
class FacebookBot
{
    private $_validationToken;
    private $_pageAccessToken;
    private $_receivedMessages;
    public function __construct($validationToken, $pageAccessToken)
    {
        $this->_validationToken = $validationToken;
        $this->_pageAccessToken = $pageAccessToken;
        $this->setupWebhook();
    }
    public function getReceivedMessages()
    {
        $this->run();
        return $this->_receivedMessages;
    }
    public function getPageAccessToken()
    {
        return $this->_pageAccessToken;
    }
    public function getValidationToken()
    {
        return $this->_validationToken;
    }
    private function setupWebhook()
    {
        if(isset($_REQUEST[\'hub_challenge\']) && isset($_REQUEST[\'hub_verify_token\']) && $this->getValidationToken()==$_REQUEST[\'hub_verify_token\'])
        {
            echo $_REQUEST[\'hub_challenge\'];
            exit;
        }
    }
    public function sendTextMessage($recipientId, $text)
    {
        $url = "https://graph.facebook.com/v2.6/me/messages?access_token=%s";
        $url = sprintf($url, FACEBOOK_PAGE_ACCESS_TOKEN);
        $recipient = new stdClass();
        $recipient->id = $recipientId;
        $message = new stdClass();
        $message->text = $text;
        $parameters = [\'recipient\' => $recipient, \'message\' => $message];
        $response = self::executePost($url, $parameters, true);
        if($response)
        {
            $responseObject = json_decode($response);
            return is_object($responseObject) && isset($responseObject->recipient_id) && isset($responseObject->message_id);
        }
        return false;
    }

    public function run()
    {
        $request = self::getJsonRequest();
        //var_dump($request);
        if(!$request) return;
        $entries = isset($request->entry) ? $request->entry : null;
        if(!$entries) return;
        $messages = [];
        foreach ($entries as $entry)
        {
            $messagingList = isset($entry->messaging) ? $entry->messaging : null;
            if(!$messagingList) continue;
            foreach ($messagingList as $messaging)
            {
                $message = new stdClass();
                $message->entryId = isset($entry->id) ? $entry->id : null;
                $message->senderId = isset($messaging->sender->id) ? $messaging->sender->id : null;
                $message->recipientId = isset($messaging->recipient->id) ? $messaging->recipient->id : null;
                $message->timestamp = isset($messaging->timestamp) ? $messaging->timestamp : null;
                $message->messageId = isset($messaging->message->mid) ? $messaging->message->mid : null;
                $message->sequenceNumber = isset($messaging->message->seq) ? $messaging->message->seq : null;
                $message->text = isset($messaging->message->text) ? $messaging->message->text : null;
                $message->attachments = isset($messaging->message->attachments) ? $messaging->message->attachments : null;
                $messages[] = $message;
            }
        }
        $this->_receivedMessages = $messages;
    }
    private static function getJsonRequest()
    {
        $content = file_get_contents("php://input",true);
        $return=json_decode($content, false, 512, JSON_BIGINT_AS_STRING);
        return $return;
    }
    private static function executePost($url, $parameters, $json = false)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        if($json)
        {
            $data = json_encode($parameters);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array(\'Content-Type: application/json\', \'Content-Length: \' . strlen($data)));
        }
        else
        {
            curl_setopt($ch, CURLOPT_POST, count($parameters));
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($parameters));
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
}


我这里做测试,返回的就是复述发送者的信息
这里写图片描述

六.测试
接入第三方平台和自己开发的差别还是很大的,起码报错消息你是看不见的,所以光从messenger那里发消息给主页是不方便调试的。
我这次用了shell下的curl来进行测试,用主页访问口令向 https://graph.facebook.com/v2.6/me/messages?access_token= 发出 POST 请求。负载必须以如下所述的 JSON 格式提供:

curl -X POST -H "Content-Type: application/json" -d \'{
  "recipient":{
    "id":"USER_ID"
  },
  "message":{
    "text":"hello, world!"
  }
}\' "https://graph.facebook.com/v2.6/me/messages?access_token=PAGE_ACCESS_TOKEN"

windows和linux略有区别。windows下建议用git bash等基于linux的环境而不要用cmd,例如上面的post请求,在cmd下其引号会失效,必须手动进行转义

七.关于一些坑
首先是facebook会偶尔向你的绑定的回调地址get消息,如果8个小时之内没有正确的返回的话,它就会自动解绑,我前一天晚上绑定的url第二天早上自动失效了,发现没有消息返回之后调了半天,才发现了这个问题。
其次是post回messenger的时候用了curl,需要开启php的curl,linux下的安装和windows不太一样,装的时候不要装错了php的版本,否则是不能生效的。
最后是messenger的应用没有提交审核之前是只能开发者发消息才有作用~

messenger的开发文档不算友好,全文算是译文吧,有的地方感觉比较生硬,比如它说的接受消息和发送消息是以用户做主体的,和一般人的理解还有一点出入。