python微信公众号接收回复用户消息

关于公众号接收以及回复用户消息的方式有两种, 一种是直接在微信公众号平台进行配置, 请看微信公众平台帮助文档, 另外一种是通过微信服务器转发到后台程序, 再通过微信服务器转发回复的形式, 如下.

首先,微信用户发送消息给公众号, 消息是先是发送到微信服务器, 后面再由微信服务器转发到公众号, 同理, 当公众号收到微信用户消息进行回复时, 也是先发送到微信服务器, 再由微信服务器转发给指定微信用户.

微信公众号可以接收的用户消息类型有这几种:

文本消息(最常见)    图片消息    语音消息    视频消息    小视频消息    地理位置消息    链接消息

以下我们以文本消息为例, 实现微信公众号接收并且回复微信用户消息功能, 关于其他类型消息的接收回复, 可以参考微信公众平台帮助文档.

根据上面图片, 当微信用户发送消息到公众号时, 会先发送到微信服务器, 后续由微信服务器转发到公众号, 由于我们是通过开发者模式进行实现, 所以此处我们需要配置一个服务器地址, 让微信服务器将消息转发到我们设置的服务器地址, 进而提取消息, 进行回复.

1: 接入成为开发者, 设置消息转发服务器URL, 用于接收微信用户消息

首先, 我们需要在微信公众平台进行配置服务器URL以及Token等信息, 在点击 "提交" 时, 微信服务器会发送一个 GET 请求到该服务器URL, 校验该服务器URL能不能正常响应, 如果可以正常响应, 则配置成功, 否则失败!

后续配置成功时, 微信用户发送消息到公众号时, 微信服务器就会以 POST 请求的方式将消息转发到此服务器URL, 届时我们便可以对消息进行提取以及回复.

根据微信公众平台帮助文档提示

  1. 微信发送 GET 请求进行校验服务器URL, 传递如下参数
    参数描述
    signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
    timestamp时间戳
    nonce随机数
    echostr随机字符串
  2. 将参数token、timestamp、nonce三个参数进行字典序排序
  3. 将三个参数字符串拼接成一个字符串进行sha1加密
  4. 开发者获得加密后的字符串可与signature对比,两者相等这表示该请求来自微信服务器
  5. 若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败

后台代码(tornado框架)

配置注意:

URL: 此前听过有网友说过, 服务器URL必须是要域名格式, 不可拼接ip地址, 我也没测试过, 如果有提示错误的可以排除看看

Token: 随便填写, 满足提示要求即可

WECHAT_TOKEN = 上面填写的token
# 假设本接口地址为 http://wodeyuming.com/wechat/control/
class WechatControlHandler(WebBaseHandler):
    def get(self):
        nonce = self.get_argument("nonce")
        echostr = self.get_argument("echostr")
        signature = self.get_argument("signature")
        timestamp = self.get_argument("timestamp")
        # 校验参数是否有空值
        if not all([signature, timestamp, nonce, echostr]):
            return self.render("error.html", error_msg="参数校验失败!")
        # 按照文档提供的方式进行计算签名值,进行比对
        sign_list = [WECHAT_TOKEN, timestamp, nonce]
        sign_list.sort()
        # 拼接字符串
        sign_temp_str = "".join(sign_list)
        # sha1加密,获取签名值
        sign_str = hashlib.sha1(sign_temp_str).hexdigest()
        # 验证请求是否来自微信服务器,比较自己生成的签名与微信签名,若相同则表示请求来自微信服务器
        if signature == sign_str:
            # 微信要求校验成功返回之前发送过来的echostr,原样返回即可, 接入成功
            return self.write(echostr)
        else:
            # 校验失败
            return self.render("error.html", error_msg="错误请求!")

到这里, 将URL地址跟WECHAT_TOKEN值填入上述服务器配置, 点击提交, 若提示成功, 则接入成功

注意, 接入成功后上面你配置的Token可能会发生改变, 记得将WECHAT_TOKEN值更换为新生成的Token

2: 接收微信用户消息, 并进行回复

再接入成功之后, 微信用户发送给公众号的消息便会被微信服务器通过POST请求方式转发到此服务器地址中, 以文本信息为例, 微信服务器传递过来的参数

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>1348831860</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[this is a test]]></Content>
  <MsgId>1234567890123456</MsgId>
</xml>
参数描述
ToUserName开发者微信号
FromUserName发送方帐号(一个OpenID)
CreateTime消息创建时间 (整型)
MsgType消息类型,文本为text
Content文本消息内容
MsgId消息id,64位整型

后台代码(tornado框架)

WECHAT_TOKEN = 上面填写的token
class WechatControlHandler(WebBaseHandler):
    def get(self):
        ......

    def post(self):
        # 验证请求是否来自微信服务器
        nonce = self.get_argument("nonce")
        signature = self.get_argument("signature")
        timestamp = self.get_argument("timestamp")
        sign_list = [WECHAT_TOKEN, timestamp, nonce]
        sign_list.sort()
        sign_temp_str = "".join(sign_list)
        sign_str = hashlib.sha1(sign_temp_str).hexdigest()
        if signature == sign_str:
            # 请求来自微信服务器, 获取消息, 根据微信公众平台提示, 微信用户发送消息到公众号之后, 不管该消息
            # 是否是我们需要处理的, 都要在5秒内进行处理并回复, 否则微信将会给用户发送错误提示, 并且重新进行
            # 校验上述服务器URL是否可用, 所以对于我们不需要处理的消息, 可以直接回复 "success"(微信推荐) 或者 "", 
            # 这样微信服务器将不会发送错误提示到微信, 也不会去重新校验服务器URL
            
            msg_xml_str = self.request.body
            if not msg_xml_str:
                return self.write("success")
            # 解析消息
            msg_xml_dict_all = xmltodict.parse(msg_xml_str)
            msg_xml_dict = msg_xml_dict_all["xml"]
            # 获取消息类型, 消息内容等信息
            msg_type = msg_xml_dict["MsgType"]
            user_open_id = msg_xml_dict["FromUserName"]
            # 需要回复的信息
            response_dict = {
                "xml": {
                    "ToUserName": msg_xml_dict["FromUserName"],
                    "FromUserName": msg_xml_dict["ToUserName"],
                    "CreateTime": int(time.time()),
                    "MsgType": "text",
                }
            }
            # 当msg_type消息类型的值为event时, 表示该消息类型为推送消息, 例如微信用户 关注公众号(subscribe),取消关注(unsubscribe)
            if msg_type == "event":
                # 事件推送消息
                msg_event = msg_xml_dict["Event"]
                if msg_event == "subscribe":
                    # 用户关注公众号, 回复感谢信息
                    response_dict["xml"]["Content"] = "感谢您的关注!"
                    response_xml_str = xmltodict.unparse(response_dict)
                    return self.write(response_xml_str)
            elif msg_type == "text":
                # 文本消息, 获取消息内容, 用户发送 哈哈, 回复 呵呵
                msg_body = msg_xml_dict["Content"]
                if msg_body == "哈哈":
                    response_dict["xml"]["Content"] = "呵呵"
                    response_xml_str = xmltodict.unparse(response_dict)
                    return self.write(response_xml_str)
            # 其他一律回复 success
            return self.write("success")

完毕

 

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页