python3 微信公众平台开发——实现“你问我答”

 Pala   2018-01-02 14:59   83 人阅读   条评论

2 实现“你问我答”

目的:

1)理解被动消息的含义

2)理解收\发消息机制

预实现功能:

粉丝给公众号一条文本消息,公众号立马回复一条文本消息给粉丝,不需要通过公众平台网页操作。

2.1 接受文本消息

即粉丝给公众号发送的文本消息。官方wiki链接:消息管理/接收消息-接受普通消息

粉丝给公众号发送文本消息:“欢迎开启公众号开发者模式”,在开发者后台,收到公众平台发送的xml 如下:(下文均隐藏了ToUserName 及 FromUserName 信息)

<xml>
<ToUserName><![CDATA[公众号]]></ToUserName>
<FromUserName><![CDATA[粉丝号]]></FromUserName>
<CreateTime>1460537339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[欢迎开启公众号开发者模式]]></Content>
<MsgId>6272960105994287618</MsgId>
</xml>

解释:

createTime 是微信公众平台记录粉丝发送该消息的具体时间

text: 用于标记该xml 是文本消息,一般用于区别判断

欢迎开启公众号开发者模式: 说明该粉丝发给公众号的具体内容是欢迎开启公众号开发者模式

MsgId: 是公众平台为记录识别该消息的一个标记数值, 微信后台系统自动产生

2.2 被动回复文本消息

即公众号给粉丝发送的文本消息,官方wiki链接: 消息管理/接收消息-被动回复消息

特别强调:

1) 被动回复消息,即发送被动响应消息,不同于客服消息接口

2) 它其实并不是一种接口,而是对微信服务器发过来消息的一次回复

3) 收到粉丝消息后不想或者不能5秒内回复时,需回复“success”字符串(下文详细介绍)

4) 客服接口在满足一定条件下随时调用

公众号想回复给粉丝一条文本消息,内容为“test”, 那么开发者发送给公众平台后台的xml 内容如下:

<xml>
<ToUserName><![CDATA[粉丝号]]></ToUserName>
<FromUserName><![CDATA[公众号]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>

特别备注:

1)ToUserName(接受者)、FromUserName(发送者) 字段请实际填写。

2)createtime 只用于标记开发者回复消息的时间,微信后台发送此消息都是不受这个字段约束。

3)text : 用于标记 此次行为是发送文本消息 (当然可以是image/voice等类型)。

4)文本换行 ‘\n’。

2.3 回复success问题

查询官方wiki 开头强调: 假如服务器无法保证在五秒内处理回复,则必须回复“success”或者“”(空串),否则微信后台会发起三次重试。

解释一下为何有这么奇怪的规定。发起重试是微信后台为了尽可以保证粉丝发送的内容开发者均可以收到。如果开发者不进行回复,微信后台没办法确认开发者已收到消息,只好重试。

真的是这样子吗?尝试一下收到消息后,不做任何回复。在日志中查看到微信后台发起了三次重试操作,日志截图如下:

1.jpg

三次重试后,依旧没有及时回复任何内容,系统自动在粉丝会话界面出现错误提示“该公众号暂时无法提供服务,请稍后再试”。

2.png

如果回复success,微信后台可以确定开发者收到了粉丝消息,没有任何异常提示。因此请大家注意回复success的问题。

2.4 流程图

3.png

2.5 码代码

main.py文件不改变,handle.py 需要增加一下代码,增加新的文件receive.py, reply.py

1)vim handle.py

# -*- coding: utf-8 -*-
# filename: handle.py

import hashlib
import web
import hashlib
import reply
import receive


class Handle(object):
    def GET(self):

        data = web.input()
        if len(data) == 0:
            return "hello, this is handle view"
        signature = data.signature
        timestamp = data.timestamp
        nonce = data.nonce
        echostr = data.echostr
        token = "a123456789"  # 请按照公众平台官网\基本配置中信息填写

        li = [timestamp, nonce, token]
        query_string = ''.join(li)
        sha1 = hashlib.sha1()
        # python3写法
        sha1.update(bytes(query_string, encoding='utf-8'))
        # python2写法
        # sha1.update(query_string)

        hashcode = sha1.hexdigest()
        # print("------handle/GET func: hashcode, signature-----")
        # print("hashcode--->", hashcode)
        # print("signature-->", signature)

        if hashcode == signature:
            return echostr
        else:
            return ""

    def POST(self):
        # try:
        webData = web.data()
        print("Handle Post webdata is ", webData)
        # 后台打日志
        recMsg = receive.parse_xml(webData)
        if isinstance(recMsg, receive.Msg) and recMsg.MsgType == 'text':
            toUser = recMsg.FromUserName
            fromUser = recMsg.ToUserName
            content = "测试回复"
            replyMsg = reply.TextMsg(toUser, fromUser, content)
            return replyMsg.send()
        else:
            print("暂且不处理")
            return "success"
        # except Exception as e:
        #     print('e-->', e)
        #     return e

2)vim receive.py

# -*- coding: utf-8 -*-
#  filename: receive.py
import xml.etree.ElementTree as ET


def parse_xml(web_data):
    if len(web_data) == 0:
        return None
    xmlData = ET.fromstring(web_data)
    msg_type = xmlData.find('MsgType').text
    if msg_type == 'text':
        return TextMsg(xmlData)
    elif msg_type == 'image':
        return ImageMsg(xmlData)


class Msg(object):
    def __init__(self, xmlData):
        self.ToUserName = xmlData.find('ToUserName').text
        self.FromUserName = xmlData.find('FromUserName').text
        self.CreateTime = xmlData.find('CreateTime').text
        self.MsgType = xmlData.find('MsgType').text
        self.MsgId = xmlData.find('MsgId').text


class TextMsg(Msg):
    def __init__(self, xmlData):
        Msg.__init__(self, xmlData)
        self.Content = xmlData.find('Content').text.encode("utf-8")


class ImageMsg(Msg):
    def __init__(self, xmlData):
        Msg.__init__(self, xmlData)
        self.PicUrl = xmlData.find('PicUrl').text
        self.MediaId = xmlData.find('MediaId').text

3)vim reply.py

# -*- coding: utf-8 -*-#
#  filename: reply.py
import time


class Msg(object):
    def __init__(self):
        pass

    def send(self):
        return "success"


class TextMsg(Msg):
    def __init__(self, toUserName, fromUserName, content):
        self.__dict = dict()
        self.__dict['ToUserName'] = toUserName
        self.__dict['FromUserName'] = fromUserName
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['Content'] = content

    def send(self):
        XmlForm = """
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[{Content}]]></Content>
        </xml>
        """
        return XmlForm.format(**self.__dict)


class ImageMsg(Msg):
    def __init__(self, toUserName, fromUserName, mediaId):
        self.__dict = dict()
        self.__dict['ToUserName'] = toUserName
        self.__dict['FromUserName'] = fromUserName
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['MediaId'] = mediaId

    def send(self):
        XmlForm = """
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[image]]></MsgType>
        <Image>
        <MediaId><![CDATA[{MediaId}]]></MediaId>
        </Image>
        </xml>
        """
        return XmlForm.format(**self.__dict)

码好代码之后,重新启动程序,sudo python main.py 80。

本文地址:http://chenxm.cc/post/519.html
版权声明:本文为原创文章,版权归 Pala 所有,欢迎分享本文,转载请保留出处!