Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

添加企业微信应用号部署方式,支持插件,支持语音图片交互 #944

Merged
merged 13 commits into from
Apr 25, 2023
Merged
Prev Previous commit
Next Next commit
fix: ensure get access_token thread-safe
  • Loading branch information
lanvent committed Apr 24, 2023
commit c6601aaeed6b1e54914e8fd16b0cb1bc3605d92d
2 changes: 1 addition & 1 deletion channel/wechatcom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@

AIGC开放社区中已经部署了多个可免费使用的Bot,扫描下方的二维码会自动邀请你来体验。

<img width="360" src="./docs/images/aigcopen.png">
<img width="200" src="../../docs/images/aigcopen.png">
6 changes: 3 additions & 3 deletions channel/wechatcom/wechatcomapp_channel.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#!/usr/bin/env python
# -*- coding=utf-8 -*-
import io
import os
import textwrap

import requests
import web
from wechatpy.enterprise import WeChatClient, create_reply, parse_message
from wechatpy.enterprise import create_reply, parse_message
from wechatpy.enterprise.crypto import WeChatCrypto
from wechatpy.enterprise.exceptions import InvalidCorpIdException
from wechatpy.exceptions import InvalidSignatureException, WeChatClientException

from bridge.context import Context
from bridge.reply import Reply, ReplyType
from channel.chat_channel import ChatChannel
from channel.wechatcom.wechatcomapp_client import WechatComAppClient
from channel.wechatcom.wechatcomapp_message import WechatComAppMessage
from common.log import logger
from common.singleton import singleton
Expand All @@ -38,7 +38,7 @@ def __init__(self):
"[wechatcom] init: corp_id: {}, secret: {}, agent_id: {}, token: {}, aes_key: {}".format(self.corp_id, self.secret, self.agent_id, self.token, self.aes_key)
)
self.crypto = WeChatCrypto(self.token, self.aes_key, self.corp_id)
self.client = WeChatClient(self.corp_id, self.secret) # todo: 这里可能有线程安全问题
self.client = WechatComAppClient(self.corp_id, self.secret)

def startup(self):
# start message listener
Expand Down
21 changes: 21 additions & 0 deletions channel/wechatcom/wechatcomapp_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import threading
import time

from wechatpy.enterprise import WeChatClient


class WechatComAppClient(WeChatClient):
def __init__(self, corp_id, secret, access_token=None, session=None, timeout=None, auto_retry=True):
super(WechatComAppClient, self).__init__(corp_id, secret, access_token, session, timeout, auto_retry)
self.fetch_access_token_lock = threading.Lock()

def fetch_access_token(self): # 重载父类方法,加锁避免多线程重复获取access_token
with self.fetch_access_token_lock:
access_token = self.session.get(self.access_token_key)
if access_token:
if not self.expires_at:
return access_token
timestamp = time.time()
if self.expires_at - timestamp > 60:
return access_token
return super().fetch_access_token()
21 changes: 4 additions & 17 deletions channel/wechatcom/wechatcomapp_message.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import re

import requests
from wechatpy.enterprise import WeChatClient

from bridge.context import ContextType
from channel.chat_message import ChatMessage
from common.log import logger
from common.tmp_dir import TmpDir
from lib import itchat
from lib.itchat.content import *


class WechatComAppMessage(ChatMessage):
Expand All @@ -23,9 +18,7 @@ def __init__(self, msg, client: WeChatClient, is_group=False):
self.content = msg.content
elif msg.type == "voice":
self.ctype = ContextType.VOICE
self.content = (
TmpDir().path() + msg.media_id + "." + msg.format
) # content直接存临时目录路径
self.content = TmpDir().path() + msg.media_id + "." + msg.format # content直接存临时目录路径

def download_voice():
# 如果响应状态码是200,则将响应内容写入本地文件
Expand All @@ -34,9 +27,7 @@ def download_voice():
with open(self.content, "wb") as f:
f.write(response.content)
else:
logger.info(
f"[wechatcom] Failed to download voice file, {response.content}"
)
logger.info(f"[wechatcom] Failed to download voice file, {response.content}")

self._prepare_fn = download_voice
elif msg.type == "image":
Expand All @@ -50,15 +41,11 @@ def download_image():
with open(self.content, "wb") as f:
f.write(response.content)
else:
logger.info(
f"[wechatcom] Failed to download image file, {response.content}"
)
logger.info(f"[wechatcom] Failed to download image file, {response.content}")

self._prepare_fn = download_image
else:
raise NotImplementedError(
"Unsupported message type: Type:{} ".format(msg.type)
)
raise NotImplementedError("Unsupported message type: Type:{} ".format(msg.type))

self.from_user_id = msg.source
self.to_user_id = msg.target
Expand Down