diff --git a/README.md b/README.md index 696c84a..70f696d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - [x] [个人微信](https://github.com/zhayujie/bot-on-anything#2%E4%B8%AA%E4%BA%BA%E5%BE%AE%E4%BF%A1) - [x] [订阅号](https://github.com/zhayujie/bot-on-anything#3%E4%B8%AA%E4%BA%BA%E8%AE%A2%E9%98%85%E5%8F%B7) - [x] [服务号](https://github.com/zhayujie/bot-on-anything#4%E4%BC%81%E4%B8%9A%E6%9C%8D%E5%8A%A1%E5%8F%B7) - - [ ] 企业微信 + - [x] 企业微信 - [x] [Telegram](https://github.com/zhayujie/bot-on-anything#6telegram) - [x] [QQ](https://github.com/zhayujie/bot-on-anything#5qq) - [x] [钉钉](https://github.com/zhayujie/bot-on-anything#10%E9%92%89%E9%92%89) @@ -590,6 +590,29 @@ pip3 install requests flask 3. 订阅菜单添加事件(接收消息v2.0) 配置请求地址(配置中的对外地址如 https://xx.xx.com:8081) 4. 版本管理与发布中上架应用,app中会收到审核信息,通过审核后在群里添加自建应用 +### 12.企业微信 + +**需要:** 一个服务器、一个已认证的企业微信。 + +企业微信的 `config.json` 配置只需修改type为`wechat_com`,默认接收消息服务器URL:http://ip:8888/wechat + +```bash +"channel": { + "type": "wechat_com", + + "wechat_mp": { + "wechat_token": "YOUR TOKEN", # token值 + "port": "8888", # 程序启动监听的端口 + "app_id": "YOUR APP ID", # app ID + "app_secret": "YOUR APP SECRET" # app secret + "wechat_corp_id": "YOUR CORP ID" + "wechat_encoding_aes_key": "YOUR AES KEY" + } +} +``` + +注意:需将服务器ip地址配置在 "企业可信IP" 内,否则用户将收不到主动推送的消息。 + ### 通用配置 + `clear_memory_commands`: 对话内指令,主动清空前文记忆,字符串数组可自定义指令别名。 diff --git a/channel/channel_factory.py b/channel/channel_factory.py index 1346499..320fa3a 100644 --- a/channel/channel_factory.py +++ b/channel/channel_factory.py @@ -25,6 +25,10 @@ def create_channel(channel_type): from channel.wechat.wechat_mp_service_channel import WechatServiceAccount return WechatServiceAccount() + elif channel_type == const.WECHAT_COM: + from channel.wechat.wechat_com_channel import WechatEnterpriseChannel + return WechatEnterpriseChannel() + elif channel_type == const.QQ: from channel.qq.qq_channel import QQChannel return QQChannel() diff --git a/channel/wechat/wechat_com_channel.py b/channel/wechat/wechat_com_channel.py new file mode 100644 index 0000000..17e5e4a --- /dev/null +++ b/channel/wechat/wechat_com_channel.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# -*- coding=utf-8 -*- +""" +@time: 2023/4/10 22:24 +@Project :bot-on-anything +@file: wechat_com_channel.py + +""" +from channel.channel import Channel +from concurrent.futures import ThreadPoolExecutor +from common.log import logger +from config import conf + +from wechatpy.enterprise.crypto import WeChatCrypto +from wechatpy.enterprise import WeChatClient +from wechatpy.exceptions import InvalidSignatureException +from wechatpy.enterprise.exceptions import InvalidCorpIdException +from wechatpy.enterprise import parse_message +from flask import Flask, request, abort + +thread_pool = ThreadPoolExecutor(max_workers=8) +app = Flask(__name__) + + +@app.route('/wechat', methods=['GET', 'POST']) +def handler_msg(): + return WechatEnterpriseChannel().handle() + + +_conf = conf().get("channel").get("wechat_com") + + +class WechatEnterpriseChannel(Channel): + def __init__(self): + self.CorpId = _conf.get('wechat_corp_id') + self.Secret = _conf.get('secret') + self.AppId = _conf.get('appid') + self.TOKEN = _conf.get('wechat_token') + self.EncodingAESKey = _conf.get('wechat_encoding_aes_key') + self.crypto = WeChatCrypto(self.TOKEN, self.EncodingAESKey, self.CorpId) + self.client = WeChatClient(self.CorpId, self.Secret, self.AppId) + + def startup(self): + # start message listener + app.run(host='0.0.0.0', port=_conf.get('port')) + + def send(self, msg, receiver): + logger.info('[WXCOM] sendMsg={}, receiver={}'.format(msg, receiver)) + self.client.message.send_text(self.AppId, receiver, msg) + + def _do_send(self, query, reply_user_id): + try: + if not query: + return + context = dict() + context['from_user_id'] = reply_user_id + reply_text = super().build_reply_content(query, context) + if reply_text: + self.send(reply_text, reply_user_id) + except Exception as e: + logger.exception(e) + + def handle(self): + query_params = request.args + signature = query_params.get('msg_signature', '') + timestamp = query_params.get('timestamp', '') + nonce = query_params.get('nonce', '') + if request.method == 'GET': + # 处理验证请求 + echostr = query_params.get('echostr', '') + try: + echostr = self.crypto.check_signature(signature, timestamp, nonce, echostr) + except InvalidSignatureException: + abort(403) + print(echostr) + return echostr + elif request.method == 'POST': + try: + message = self.crypto.decrypt_message( + request.data, + signature, + timestamp, + nonce + ) + except (InvalidSignatureException, InvalidCorpIdException): + abort(403) + msg = parse_message(message) + if msg.type == 'text': + thread_pool.submit(self._do_send, msg.content, msg.source) + else: + reply = 'Can not handle this for now' + self.client.message.send_text(self.AppId, msg.source, reply) + return 'success' diff --git a/common/const.py b/common/const.py index bd28a5c..e0fa6e8 100644 --- a/common/const.py +++ b/common/const.py @@ -3,6 +3,7 @@ TERMINAL = "terminal" WECHAT = "wechat" WECHAT_MP = "wechat_mp" WECHAT_MP_SERVICE = "wechat_mp_service" +WECHAT_COM = "wechat_com" QQ = "qq" GMAIL = "gmail" TELEGRAM = "telegram" diff --git a/config-template.json b/config-template.json index 8b61dc2..099d9e2 100644 --- a/config-template.json +++ b/config-template.json @@ -45,6 +45,14 @@ "token": "YOUR TOKEN", "port": "80" }, + "wechat_com": { + "wechat_token": "", + "wechat_encoding_aes_key":"", + "wechat_corp_id":"", + "appid":"", + "secret":"", + "port": "8888" + }, "gmail": { "subject_keyword": ["bot", "@bot"],