前一段时间需要开发一个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. VSCode支持jsx自动补全

    点击settings.json中编辑, 把这段话加上去就可以了 "emmet.includeLanguages": { "javascript": " ...

  2. asp.net批量下载

    1.首先读取文件夹下的文件,可能同时存在多个文件 2.选中文件,然后点击下载,同时可以选择多个文件. 思路:通过生产压缩包的形式进行下载,然后再清楚压缩包,这样用户可以一次性全部下载下来. 一.获取目 ...

  3. BZOJ 3398: [Usaco2009 Feb]Bullcow 牡牛和牝牛 水题~

    水~ #include <cstdio> #define N 100004 #define mod 5000011 #define setIO(s) freopen(s".in& ...

  4. Codeforces 962D Merge Equals ( 模拟 )

    题意 : 给出一个序列,然后每次将重复出现的元素进行求和合并(若有多个,则优先取最小的进行合并),若某重复元素有很多,那么取最左边的那两个进行合并且合并后元素位于原来右边元素的位置,例如 3 2 6 ...

  5. POJ 2289 多重二分匹配+二分 模板

    题意:在通讯录中有N个人,每个人能可能属于多个group,现要将这些人分组m组,设各组中的最大人数为max,求出该最小的最大值 下面用的是朴素的查找,核心代码find_path复杂度是VE的,不过据说 ...

  6. Java虚拟机之JVM系统和内存模型

    1.类加载子系统 负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间里. 2.方法区 存放类信息.常量信息.常量池信息.包括字符串字面量和数字常量等,方法区的大小决 ...

  7. C#获取网页信息并存入数据库

    1,获取以及商品分类信息 给一网页获取网页上商品信息的分类 using Skay.WebBot; using System; using System.Collections.Generic; usi ...

  8. sqli-labs(31)

    0x01找闭合 这里是WAF的jsp调到php的同样 第二个参数构造 偷看源码 闭合是") 我们尝试一下构造爆破数据库名 login.php?id=&id=-") unio ...

  9. springboot 基于@Scheduled注解 实现定时任务

    前言 使用SpringBoot创建定时任务非常简单,目前主要有以下三种创建方式: 一.基于注解(@Scheduled) 二.基于接口(SchedulingConfigurer) 前者相信大家都很熟悉, ...

  10. 20175308 2018-2019-2 实验四 《Android开发基础》实验报告

    20175308 2018-2019-2 实验四 <Android开发基础>实验报告 实验要求 参考 Android开发简易教程 完成云班课中的检查点,也可以先完成实验报告,直接提交.注意 ...