前一段时间需要开发一个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的开发文档不算友好,全文算是译文吧,有的地方感觉比较生硬,比如它说的接受消息和发送消息是以用户做主体的,和一般人的理解还有一点出入。

php中引入facebook的messenger消息接口的更多相关文章

  1. 如何在项目中引入MetaQ消息收发机制

    当需要异步发送和接收大量消息时,需要在Crystal项目中引入MetaQ消息收发机制. 关于MetaQ使用的官方例子可参考:https://github.com/killme2008/Metamorp ...

  2. JDK8在接口中引入的default

    default关键字介绍 default是在java8中引入的关键字,也可称为Virtual extension methods——虚拟扩展方法.是指,在接口内部包含了一些默认的方法实现(也就是接口中 ...

  3. facebook充值实时更新接口文档翻译 希望对做facebook充值的小伙伴有帮助

    Realtime Updates for Payments are an essential method by which you are informed of changes to orders ...

  4. 微信服务号模板消息接口新增"设置行业"和"添加模板"及细节优化

    微信服务号模板消息可以向用户发送重要的服务通知,如信用卡刷卡通知,商品购买成功通知等.昨日,微信团队发布公告称模板消息新增“设置行业”和“添加模板”接口及细节优化,详细变动如下 模板消息[业务通知]自 ...

  5. 微信公众平台消息接口API指南

    简介 微信公众平台消息接口为开发者提供了一种新的消息处理方式.微信公众平台消息接口为开发者提供与用户进行消息交互的能力.对于成功接入消息接口的微信公众账号,当用户发消息给公众号,微信公众平台服务器会使 ...

  6. 通用权限管理系统接口文档V4.2 版本之消息接口介绍

    通用权限管理系统提供的消息接口可实现消息获取,消息发送,底层使用Redis对消息进行缓存,解决消息的并发请求对数据库的压力. 前端可以通过客户端轮询来获取最新消息,前端效果截图如下:

  7. 微信公众平台消息接口开发-封装weixin.class.php

    原文:微信公众平台消息接口开发-封装weixin.class.php 一.封装weixin.class.php 由于微信公众平台的通信使用的是特定格式的XML数据,每次接受和回复都要去做一大堆的数据处 ...

  8. 微信公众平台消息接口PHP版开发教程

    原文:微信公众平台消息接口PHP版开发教程  一.写好接口程序 在你的服务器上上传好一个接口程序文件,如http://www.yourdomain.com/weixin.php  内容如下: &l ...

  9. vue中Axios的封装和API接口的管理

    前端小白的声明: 这篇文章为转载:主要是为了方便自己查阅学习.如果对原博主造成侵犯,我会立即删除. 转载地址:点击查看 如图,面对一团糟代码的你~~~真的想说,What F~U~C~K!!! 回归正题 ...

随机推荐

  1. PCL智能指针疑云 <一>

    背景: 最近写了一个包,使用ndt算法拼接点云,构建三维壁面环境的点云地图. 设计一个lidar类,表征激光雷达.可以获取点云数据并存储到容器 std::vector<PointCloudPtr ...

  2. mysql增删改查相关操作

    mysql增删改查相关操作 以前用mysql用的少,对于数据库相关的操作不熟悉,现在开始要接触数据库了,记录一下相关的基础操作吧. 1.数据库的授权操作 # mysql -u root -p Ente ...

  3. Egret Tween

    最近开始接触Egret,其实也就是为了写一些小的特效 1.egret.Tween.get() ,激活一个对象,对其添加 Tween 动画 2.to() ,将指定对象的属性修改为指定值 egret.Tw ...

  4. [BZOJ1902]:[NOIP2004]虫食算(搜索)

    题目传送门 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母. 来看一个简单的例子: 43#98650#45+8468#6633=444455069 ...

  5. 几种常见的CSS布局

    本文概要 本文将介绍如下几种常见的布局: 其中实现三栏布局有多种方式,本文着重介绍圣杯布局和双飞翼布局.另外几种可以猛戳实现三栏布局的几种方法 一.单列布局 常见的单列布局有两种: header,co ...

  6. java中的过滤器 --Filter

    package filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.Filter ...

  7. 微信小程序 API 路由

    路由:由于页面的跳转: wx.switchTab() 跳转到 tabBar 页面,并关闭掉其他所有非 tabBar 页面: 参数:为对象, 对象的属性: url:需要跳转的 tabBar 的页面路径( ...

  8. 方法一破解:Excel工作表保护密码

    在excel2016中实测验证过有效 在Excel中,为了保护自已的工作表不被修改,我们可以添加保护密码. 操作步骤: 1.把Excel文件的扩展名xlsx修改为Rar.瞬间Excel文件变成了压缩包 ...

  9. oracle ogg--ogg搭建过程中遇到的错误及处理

    1 PRVF-0002 : Could not retrieve local nodename---# Begin Stacktrace #---------------------------ID: ...

  10. tomcat 404 The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

    1.原因 tomcat没有找到项目,因为是用eclipse启动的,不是在tomcat的目录下启动,所以我们需要在eclipse下配置启动目录,好让tomcat找到项目. 2.解决 用本地安装的tomc ...