公众账号接收非文字消息
在微信公众平台上用户都是用使用文字在进行交互的,但是有时候使用图片、语音、视频以及地理位置等可以实现一些非常棒的功能,比如“语音提醒”公众号里的发送语音就可以实现定时的事件提醒功能;比如ZTalk曾经搞过微信拍照晒电脑桌面的活动(响应的人太少,桑心……);比如一些预定本地化服务的公众号里发送当前地理位置就可以查询周边商家。
要实现这些功能首先得学会接收这些消息类型,比文字消息类型肯定要复杂很多了,目前我们能够接收的消息类型只有图片、地理位置以及退订消息,其他的如链接消息、语音消息是需要官方授权的。接收到用户消息以后回复的消息类型可以参考第11章,目前也就那三种。
一、图片消息接收
相信拍照是目前智能手机用户用得最多的功能没有之一,在微信里也是一样,看看每天朋友圈里分享的那些照片就知道了,那么如何接收和保存用户照片捏,首先我们得了解用户发送的图片消息结构,如下图:
大家可以看到图片消息除了固定的消息发送用户ID、接收公众号ID、生成时间等固定字段外,多了一个图片链接PicUrl,这个就是用户向公众账号发送图片后,保存到微信的服务器上返回给公众号的链接,该图片链接格式如下:
也就是说我们如果要把图片保存到本地服务器,就要先将图片从微信服务器上抓取下来,当然你也可以直接保存这个链接以后直接访问,这个看具体应用了。今天要讲的是图片保存到本地服务器上,请看下面代码:
由于图片消息是另一种消息类型,因此代码添加位置可以位于文字消息上面或者下面,只要别在文字消息的判断语句内就行。然后我来解释下每个语句的作用:
第20行,判断消息类型是否为图片消息,条件为$form_MsgType==image;
第24行,获取图片消息数据中的图片链接并赋值给$from_PicUrl;
第26行,生成要保存到本地服务器的图片名称,为了避免重复新图片命名使用了发送用户的OPENID+当前时间戳,文件的后缀名我是直接设定为jpg文件。(由于从图片链接上无法获取到图片后缀名,又懒得通过头文件获取图片格式,就直接固定死了JPG格式,貌似没有什么问题,哈哈哈);
第28行,SAE上的内置接口类很多,SaeFetchurl是一个用来抓取远程网页的类,使用这个类就可以很方便的实现抓取其他网站的内容,否则使用PHP的curl或者file_get_contents这些估计还得解释半天,这句代码是新建一个抓取类的对象。
第30行,执行抓取图片链接,其中抓取的函数是fetch(),图片链接是之前赋值的$from_PicUrl,抓取后的结果赋值给$res;
第32行,判断抓取结果,errno()返回的是抓取结束后的错误代码,如果为0则成功,其他的就是不成功。
第35行,图片抓取成功后,新建一个Storage的对象,我们要保存图片了。
第37行,这句代码其实已经在以前出现多次,这次将抓取的内容($res)写入指定的文件($filename),并保存到Storage里,请注意把“weixincourse”替换成自己创建的Storage空间名。
第39行到41行,保存成功后给用户提示图片上传成功。
第45行到48行,文件没有抓取到提示用户图片上传失败。
当然我们如果严谨一点,还要判断图片文件是否保存成功,可以把第37行后面改写一下,如下图:
大家可以尝试把Storage的空间名字故意写错,看看会有什么样的提示。如果提示上传成功,我们到SAE的Storage列表里就应该可以看到刚上传的文件了。
二、地理位置消息接收
手机上基于地理位置的APP很多,是个应用现在都得跟LBS扯上点关系,SoLoMo里重要的一环就是地理位置,微信里也有很多应用是跟地理位置相关的,比如查个本地天气、附近酒店餐馆啥的。今天举的例子是查本地天气。先了解下地理位置消息的结构,如下图:
地理位置消息多了四项,分别是经纬度的X和Y坐标、地图缩放比例以及地址信息,而实际上由于网络原因我们经常是收不到地址信息的,只有坐标信息,因此地理位置的开发基本围绕着坐标来。先来看本地天气查询代码吧,如下图:
代码添加位置同图片消息,另起一个消息类型判断语句,可以放在图片消息前面或者后面。前面说了我们主要使用的是经纬度,经纬度是可以通过一些地图api接口来获取实际地址、周边商家等信息的,天气代码这里我用的是百度地图API接口,主要是因为它有URL接口,代码解释开始:
第21行,消息类型判断语句,消息类型为location;
第24行到27行,将用户推送地理消息的经纬度、地图比例、地址信息分别赋值。经纬度分别为Location_X和Location_Y,相当于用经线和纬线的交叉点来标注地理位置。Scale是用户发送地理位置时地图的缩放比例。Label是地址信息(经常是获取不到的,获取了也没啥用,因为都是连在一起的,无法提取地市县信息)。
第29行,定义百度地图API接口的反向地址解析URL,反向地址解析是指通过经纬度获取当前位置的地址信息。
第31行,由于各家地图不一样因此传输过来的经纬度也会有所偏差,这里我选的是wgs84即手机GPS的坐标。
第33行,又要抓页面了,先建个抓取类的对象。
第35行,百度地图API接口的反向地址解析规则是URL+坐标类型+坐标值,其中$map_api_url.$map_coord_type两个变量拼接就是URL+坐标类型,然后再加上经纬度参数,用location=经度,纬度来赋值。
这里说下URL的规则,URL就是大家常看到的网页链接,一般由HTTP://后面加网址加参数组成,主要说下参数,参数一般是“参数名=赋值”组成,普通的URL参数格式是跟在网址后面第一个参数前用“?”号分隔,第二个参数开始用“&”分隔,参数在程序里是可以获取到的,我们上面获取坐标解析的实际地址形式为:
接收的实际地址为http://api.map.baidu.com/geocoder,获取到的参数是coord_type和location,值为相应后面跟着的。
第37行,判断是否抓取成功,如果抓取成功$geocoder的数据实际是如下格式:
这里一大堆信息里只需要提取城市,即CITY这个标签内的数据。
第40行,这是一个正则表达式,比较复杂,作用就是根据规则将$geocoder里的<city>北京市</city>数据提取出来赋予$city这个变量,如果成功这个数据是会是一个多维数组,其中city标签内的数据即北京市是存储在$city[1][0]里的,$city[0][0]的值是“<city>北京市</city>”;
第41行,将$city[1][0]的值即“北京市”提取出来,同时使用str_replace函数将“市”替换掉再重新赋值给$city,str_replace按照字面意思就是字符串替换,用法是:str_replace(要替换的内容,替换成的内容,字符串);其中替换的内容和替换成的内容可以使用数组,也可以使用单个字符串,我这里是用了数组,即将市县区都替换成了空,替换的用处是因为后面查天气预报的接口只支持城市名称,不能有市县区啥的……经过这一步$city的值就是“北京”;
第43行,定义天气API接口的URL;
第45行,做了三件事,第一个使用iconv()函数将$city的字符编码从UTF-8转换成GBK,第二件是使用urlencode将汉字转换成英文编码方便URL传值,第三件是将URL中的“&city=”的参数名拼接了。
关于字符,有时候我们上网的时候会发现网页有乱码,大部分是因为字符编码不对造成的,可以调节浏览器的编码来切换,在程序里也是一样,由于新浪接口接收的字符串是GBK的,而我们程序里使用UTF-8,所以需要转码后才能通讯,否则新浪接口收到的就是乱码。
PS:GBK或者GB2312是中文简体编码,属于ANSI编码,但是同个ANSI编码值在不同国家的编码对应是不同的文字,会非常混乱,所以有了Unicode以及UTF-8,这是国际通用的文字编码格式,所有文字都被分配了不同的编码,也就不怕乱码了。
第46行,查询天气日期,0表示当天,1表示明天,以此类推……
第49行,抓取天气内容并赋值给$weather,这里不需要再建立抓取类的对象了,因为之前已经建立了可以直接用。
第51行,判断是否抓到天气,这里我多加了一个
strstr函数是用来检查$weather里是否存在“Weather”这个字符串,&&表示并且,这里的判断就是不仅要抓取成功并且在抓取到的内容里存在“Weather”。这样写的目的是因为新浪天气接口不管有没有查询到天气都会返回数据,而判断数据里是否有天气信息,只有判断返回内容里有木有“Weather”这个字符串。成功抓取到的会是如下内容:
这又是一个XML,然后用的是一些拼音首字母做了标签,把这个回复给用户估计会疯的,我们要进行一些整理,方法嘛就是用正则表达式来提取我们需要的内容,我这里提取的标签是city(城市)、status2-status1(天气变化)、temperature2-temperature1(温度变化)、direction2-power2(风向风力)、chy_shuoming(穿着建议)、savedate_weather(信息发布时间)
第54行到62行就是提取这些数据的正则表达式,可以发现其实改动的只是标签名和赋值的参数,如果大家还想加写数据的可以参照着提取;
第64行到71行判断天气变化是否相同,比如上面显示的天气1和天气2其实都是阴,如果不做判断就会返回给用户“阴转阴”,非常2,所以这里判断如果两个天气是一致的则将任意一个天气赋值给$w_status变量,否则就按照天气2转天气1赋值给$w_status变量,最后输出时用$w_status这个变量。
第73行到81行新建一个数组,将前面获取到的天气数据添加为数组元素,格式就是$weather_res=array();然后在括号里用逗号分割每个天气数据,最后一个后面不要加逗号,这样做的好处是避免代码行过长,而且很清晰。
第82行,将数组用implode()函数转化成数组,用"\n"这个换行符来分割。
其实73到82行完成的就是将所有数据拼接成一个字符串,使用“.”一个个拼也可以,不过效率低下而且代码也不够清晰。
全部拼接完成就可以输出了,在手机上效果如下:
后面的就不详细说了,都有标注,都是些判断提示语句,大家可以自己学着看,不懂的可以直接在后台问。另外强调一点,一般天气预报是拿中国天气网接口做的会比这个更好,能够显示县区级的天气,但是需要搞个城市代码表,留到以后说吧,新浪的接口可以直接用城市名查就先演示下。
三、退订消息接收
不知道还有多少人记得我曾写过一篇《那些离开的朋友们》,是因为微信把退订消息接口开了,每天看到不少人退订心里有点小难过写的,到今天我看了下差不多有2000个退订用户了,估计是我现在写的东西不合他们胃口。
退订消息接口的代码很简单,跟用户订阅一样是一个事件类型的消息,只是事件类型的标示是”unsubscribe“,退订用户最好是配合数据库来记录,我这里给大家的是一个保存成文件的例子,如下图:
代码添加的位置请看仔细了,在事件消息判断里的获取事件类型之后。代码很短,解释如下:
第300行,判断事件类型标示是否为”unsubscribe“,大家可以看下订阅消息是”subscribe“;
第303行,新建一个Storage的对象;
第305行,写入文件,文件名为退订用户的OPENID,文件后缀是.txt,内容我用了退订时间。
当用户退订后在Storage的存储空间里就会有一个文件生成。
微信公众平台开发的基础教程差不多已经讲完,后面还会讲下数据库和云存储平台的使用,然后就会是完整案例,之所以花那么长时间讲那么细,是希望大家能够先熟悉一些基础的编程,有一个循序渐进的过程,所有复杂的程序其实都是这些基础模块组成,如果你觉得看不懂,那可能是没有跟着动手做,后面的教程估计会跟不上,so,有时间就动动手吧!anyway,我会把教程坚持写完,因为很多人有期待。