Files
gzth/wecom_api/controllers/main.py
T
2024-06-27 21:46:29 +08:00

132 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
import json
import logging
import xml.etree.cElementTree as ET
from lxml import etree
import sys
from odoo.addons.wecom_api.api.wecom_msg_crtpt import WecomMsgCrypt # type: ignore
from odoo import http, models, fields, _
from odoo.http import request
from odoo.http import Response
_logger = logging.getLogger(__name__)
class StripeController(http.Controller):
""" """
@http.route(
["/wecom_callback", "/wecom_callback/"],
type="http",
auth="public",
methods=["GET", "POST"],
)
def WecomCallbackService(self): # type: ignore
"""
企业微信回调服务
"""
@http.route(
["/wecom_callback/<int:id>/<string:service>", "/wecom_callback/contacts"],
type="http",
auth="public",
methods=["GET", "POST"],
csrf=False,
)
def WecomCallbackService(self, service, id, **kw):
"""
企业微信回调服务
:param id: 公司id
:param service: 回调服务名称 code
文档URL: https://work.weixin.qq.com/api/doc/90000/90135/90930#3.2%20%E6%94%AF%E6%8C%81Http%20Post%E8%AF%B7%E6%B1%82%E6%8E%A5%E6%94%B6%E4%B8%9A%E5%8A%A1%E6%95%B0%E6%8D%AE
"""
company_id = request.env["res.company"].sudo().search([("id", "=", id)]) # type: ignore
sCorpID = company_id.corpid
callback_service = (
company_id.contacts_app_id.app_callback_service_ids.sudo().search(
[
("app_id", "=", company_id.contacts_app_id.id),
("code", "=", service),
"|",
("active", "=", True),
("active", "=", False),
]
)
)
if callback_service.active is False:
_logger.info(
_("App [%s] does not have service [%s] enabled")
% (company_id.contacts_app_id.name, callback_service.name)
)
return Response("success", status=200)
else:
wxcpt = WecomMsgCrypt(
callback_service.callback_url_token,
callback_service.callback_aeskey,
sCorpID,
)
# 获取企业微信发送的相关参数
sVerifyMsgSig = kw["msg_signature"]
sVerifyTimeStamp = kw["timestamp"]
sVerifyNonce = kw["nonce"]
if request.httprequest.method == "GET": # type: ignore
# ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
# ^ 企业管理员在保存回调配置信息时,企业微信会发送一条验证消息到填写的URL,请求内容如下:
# ^ 请求方式:GET
# ^ http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
# ^ 请求参数:
# ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
# ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
# ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
# ^ echostr: 加密的随机字符串。用于保证签名不可预测。
sVerifyEchoStr = kw["echostr"]
ret, msg = wxcpt.VerifyURL(
sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce, sVerifyEchoStr
)
if ret != 0:
logging.error("ERR: VerifyURL ret: " + str(ret))
sys.exit(1)
return msg
if request.httprequest.method == "POST": # type: ignore
# ^ 假设企业的接收消息的URL设置为http://api.3dept.com。
# ^ 当用户触发回调行为时,企业微信会发送回调消息到填写的URL,请求内容如下:
# ^ 请求方式:POST
# ^ 请求地址 :http://api.3dept.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS&timestamp=13500001234&nonce=123412323
# ^ 请求参数:
# ^ msg_signature: 企业微信加密签名,msg_signature计算结合了企业填写的token、请求中的timestamp、nonce、加密的消息体。
# ^ timestamp: 时间戳。与nonce结合使用,用于防止请求重放攻击。
# ^ nonce: 随机数。用于保证签名不可预测。与timestamp结合使用,用于防止请求重放攻击。
# ^ ToUserName: 企业微信的CorpID,当为第三方应用回调事件时,CorpID的内容为suiteid
# ^ AgentID: 接收的应用id,可在应用的设置页面获取。仅应用相关的回调会带该字段。
# ^ Encrypt: 消息结构体加密后的字符串
sReqData = request.httprequest.data # type: ignore
ret, msg = wxcpt.DecryptMsg(
sReqData, sVerifyMsgSig, sVerifyTimeStamp, sVerifyNonce
)
if ret != 0:
logging.error("ERR: DecryptMsg ret: " + str(ret))
sys.exit(1)
# 解密成功,msg即明文的xml消息结构体
# xml_tree = etree.fromstring(msg) # xml解析为一个xml元素
# print("解密成功", msg)
try:
return (
request.env["wecom.app.event_type"] # type: ignore
.sudo()
.with_context(xml_tree=msg, company_id=company_id)
.handle_event()
) # 传递xml元素和公司
except:
pass
finally:
# ^ 正确响应企业微信本次的POST请求,企业微信将不会再次发送请求
# ^ ·企业微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
# ^ ·当接收成功后,http头部返回200表示接收ok,其他错误码企业微信后台会一律当做失败并发起重试
# return Response("success", status=200)
pass