跳转至

本篇是基于企业微信API的消息推送的下集,之前也提到过,接收信息比推送稍微复杂一丢丢,需要实现一个回调服务。

根据官方文档,配置回调服务时,需要能同时支持HttpGet以及HttpPost两种能力

  • 企业微信会先判断URL服务是否具备解析企业微信推送消息的能力。 具体方式是,企业微信往URL服务上发一条Get请求带签名及密文参数到URL服务上,如果URL服务检查签名通过,并能正确返回密文参数对应的明文字符串,则验证通过。此时在企业微信的配置就开始生效。

  • 后续的业务请求(比如应用菜单的点击事件,用户消息等),都会类似的方式(签名+密文)向服务URL推送消息。URL服务验证签名通过后,需要将POST数据解密,就可以得到对应的业务消息明文。

1. 首先设置接收消息的参数

在企业的管理端后台,进入需要设置接收消息的目标应用,点击“接收消息”的“设置API接收”按钮,进入配置页面。

用的URL、Token、EncodingAESKey三个参数

  • URL是企业后台接收企业微信推送请求的访问协议和地址,支持http或https协议(为了提高安全性,建议使用https)。
  • Token可由企业任意填写,用于生成签名。
  • EncodingAESKey用于消息体的加密。

2. 回调服务

回调服务用什么实现都可以,我这里django比较熟就使用django了。

主要在于消息的加解密,鉴于加解密算法相对复杂,企业微信提供了算法库。目前已有c++/python/php/java/golang/c#等语言版本,均提供了解密、加密、验证URL三个接口,自己去官方文档下就是了。

views.py :

```python from django.http import HttpResponse from django.shortcuts import render from django.views import View from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator import urllib.parse

from .WXBizMsgCrypt3 import WXBizMsgCrypt import xml.etree.cElementTree as ET import sys

Create your views here.

@method_decorator(csrf_exempt, name='dispatch') class Receive(View): def init(self, kwargs) -> None: super().init(kwargs) self.wxcpt = WXBizMsgCrypt( sToken='你的Token', sEncodingAESKey='你的EncodingAESKey', sReceiveId='你的corp_id') # corp_id

def get(self, request):
    msg_signature = urllib.parse.unquote(
        request.GET.get('msg_signature', ''))  # 加密签名
    timestamp = urllib.parse.unquote(
        request.GET.get('timestamp', ''))  # 时间戳
    nonce = urllib.parse.unquote(request.GET.get('nonce', ''))  # 随机数
    echostr = urllib.parse.unquote(
        request.GET.get('echostr', ''))  # 加密的字符串

    ret, sEchoStr = self.wxcpt.VerifyURL(
        msg_signature, timestamp, nonce, echostr)
    # print(sEchoStr)
    if(ret != 0):
        print("ERR: VerifyURL ret: " + str(ret))
        sys.exit(1)
    # 验证URL成功,将sEchoStr返回给企业号
    return HttpResponse(sEchoStr)

def post(self, request):
    msg_signature = urllib.parse.unquote(
        request.GET.get('msg_signature', ''))  # 加密签名
    timestamp = urllib.parse.unquote(
        request.GET.get('timestamp', ''))  # 时间戳
    nonce = urllib.parse.unquote(request.GET.get('nonce', ''))  # 随机数
    sReqData = request.body.decode('utf-8')  # 加密的消息

    ret, sMsg = self.wxcpt.DecryptMsg(
        sReqData, msg_signature, timestamp, nonce)
    #print(ret, sMsg)
    if(ret != 0):
        print("ERR: DecryptMsg ret: " + str(ret))
        sys.exit(1)
    # 解密成功,sMsg即为xml格式的明文
    # TODO: 对明文的处理
    xml_tree = ET.fromstring(sMsg)
    content = xml_tree.find("Content").text
    ToUserName = xml_tree.find("ToUserName").text
    FromUserName = xml_tree.find("FromUserName").text
    CreateTime = xml_tree.find("CreateTime").text
    MsgId = xml_tree.find("MsgId").text
    AgentID = xml_tree.find("AgentID").text
    print(f'content:{content}', f'ToUserName:{ToUserName}',
          f'FromUserName:{FromUserName}',)

    # 构造回复消息结构体
    sRespData = f'''
    <xml>
    <ToUserName>{ToUserName}</ToUserName>
    <FromUserName>{FromUserName}</FromUserName>
    <CreateTime>{CreateTime}</CreateTime>
    <MsgType>text</MsgType>
    <Content>我也{content}!</Content>
    <MsgId>{MsgId}</MsgId>
    <AgentID>{AgentID}</AgentID>
    </xml>
    '''
    ret, sEncryptMsg = self.wxcpt.EncryptMsg(sRespData, nonce, timestamp)
    if(ret != 0):
        print("ERR: EncryptMsg ret: " + str(ret))
        sys.exit(1)
    # ret == 0 加密成功,企业需要将sEncryptMsg返回给企业号
    # 假如企业无法保证在五秒内处理并回复,或者不想回复任何内容,可以直接返回200(即以空串为返回包)。
    # 企业后续可以使用主动发消息接口进行异步回复。
    return HttpResponse(sEncryptMsg)

```

python版本有个坑的地方在于使用了pycrypto第三方库

```python """

关于Crypto.Cipher模块,ImportError: No module named 'Crypto'解决方案

请到官方网站 https://www.dlitz.net/software/pycrypto/ 下载pycrypto。

下载后,按照README中的“Installation”小节的提示进行pycrypto安装。

pip install pycryptodome """ ```

3. 效果

微信图片_20220610144038

~~不小心暴露了价值百万的人工智能IP~~,可以看到emoji也能显示,消息类型支持:文本、图片、语音、视频、位置以及链接信息,具体可以参考官方文档。

后台接收到信息后就可以自己去实现相应的业务逻辑,比如用户向应用发消息,识别消息关键词,回复不同的消息内容;用户点击应用菜单时,转化为指令,执行自动化任务等等等。