mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
fix: feishu cert error
This commit is contained in:
@@ -140,6 +140,23 @@ python3 app.py
|
||||
|
||||
**解决**: 安装依赖 `pip install lark-oapi`
|
||||
|
||||
### SSL证书验证失败
|
||||
|
||||
```
|
||||
[Lark][ERROR] connect failed, err:[SSL:CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain
|
||||
```
|
||||
|
||||
**原因**: 网络环境中存在自签名证书或SSL中间人代理(如企业代理、VPN等)
|
||||
|
||||
**解决**: 程序会自动检测SSL证书验证失败,并自动重试禁用证书验证的连接。无需手动配置。
|
||||
|
||||
当遇到证书错误时,日志会显示:
|
||||
```
|
||||
[FeiShu] SSL certificate verification disabled due to certificate error. This may happen when using corporate proxy or self-signed certificates.
|
||||
```
|
||||
|
||||
这是正常现象,程序会自动处理并继续运行。
|
||||
|
||||
### Webhook模式端口被占用
|
||||
|
||||
```
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import ssl
|
||||
import threading
|
||||
# -*- coding=utf-8 -*-
|
||||
import uuid
|
||||
@@ -107,7 +108,30 @@ class FeiShuChanel(ChatChannel):
|
||||
.register_p2_im_message_receive_v1(handle_message_event) \
|
||||
.build()
|
||||
|
||||
# 创建长连接客户端
|
||||
# 尝试连接,如果遇到SSL错误则自动禁用证书验证
|
||||
def start_client_with_retry():
|
||||
"""启动websocket客户端,自动处理SSL证书错误"""
|
||||
for use_ssl_verify in [True, False]:
|
||||
try:
|
||||
# 如果不验证SSL,通过monkey patch禁用证书验证
|
||||
original_wrap_socket = None
|
||||
if not use_ssl_verify:
|
||||
logger.warning("[FeiShu] SSL certificate verification disabled due to certificate error. "
|
||||
"This may happen when using corporate proxy or self-signed certificates.")
|
||||
# 保存原始的wrap_socket方法
|
||||
import ssl as ssl_module
|
||||
original_wrap_socket = ssl_module.SSLContext.wrap_socket
|
||||
|
||||
# 创建一个不验证证书的wrap_socket方法
|
||||
def wrap_socket_no_verify(self, sock, *args, **kwargs):
|
||||
self.check_hostname = False
|
||||
self.verify_mode = ssl.CERT_NONE
|
||||
return original_wrap_socket(self, sock, *args, **kwargs)
|
||||
|
||||
# 替换wrap_socket方法
|
||||
ssl_module.SSLContext.wrap_socket = wrap_socket_no_verify
|
||||
|
||||
try:
|
||||
ws_client = lark.ws.Client(
|
||||
self.feishu_app_id,
|
||||
self.feishu_app_secret,
|
||||
@@ -115,15 +139,33 @@ class FeiShuChanel(ChatChannel):
|
||||
log_level=lark.LogLevel.DEBUG if conf().get("debug") else lark.LogLevel.INFO
|
||||
)
|
||||
|
||||
# 在新线程中启动客户端,避免阻塞主线程
|
||||
def start_client():
|
||||
try:
|
||||
logger.debug("[FeiShu] Websocket client starting...")
|
||||
ws_client.start()
|
||||
except Exception as e:
|
||||
logger.error(f"[FeiShu] Websocket client error: {e}", exc_info=True)
|
||||
# 如果成功启动,跳出循环
|
||||
break
|
||||
finally:
|
||||
# 恢复原始的wrap_socket方法
|
||||
if original_wrap_socket is not None:
|
||||
import ssl as ssl_module
|
||||
ssl_module.SSLContext.wrap_socket = original_wrap_socket
|
||||
|
||||
ws_thread = threading.Thread(target=start_client, daemon=True)
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
# 检查是否是SSL证书验证错误
|
||||
is_ssl_error = "CERTIFICATE_VERIFY_FAILED" in error_msg or "certificate verify failed" in error_msg.lower()
|
||||
|
||||
if is_ssl_error and use_ssl_verify:
|
||||
# 第一次遇到SSL错误,记录日志并继续循环(下次会禁用验证)
|
||||
logger.warning(f"[FeiShu] SSL certificate verification failed: {error_msg}")
|
||||
logger.info("[FeiShu] Retrying connection with SSL verification disabled...")
|
||||
continue
|
||||
else:
|
||||
# 其他错误或禁用验证后仍失败,抛出异常
|
||||
logger.error(f"[FeiShu] Websocket client error: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
# 在新线程中启动客户端,避免阻塞主线程
|
||||
ws_thread = threading.Thread(target=start_client_with_retry, daemon=True)
|
||||
ws_thread.start()
|
||||
|
||||
# 保持主线程运行
|
||||
@@ -277,7 +319,8 @@ class FeiShuChanel(ChatChannel):
|
||||
# 错误码 230055 说明:上传 mp4 时必须使用 msg_type="media"
|
||||
msg_type = "media"
|
||||
reply_content = upload_data # 完整的上传响应数据(包含file_key和duration)
|
||||
logger.info(f"[FeiShu] Sending video: file_key={upload_data.get('file_key')}, duration={upload_data.get('duration')}ms")
|
||||
logger.info(
|
||||
f"[FeiShu] Sending video: file_key={upload_data.get('file_key')}, duration={upload_data.get('duration')}ms")
|
||||
content_key = None # 直接序列化整个对象
|
||||
else:
|
||||
# 其他文件使用 file 类型
|
||||
@@ -320,7 +363,6 @@ class FeiShuChanel(ChatChannel):
|
||||
else:
|
||||
logger.error(f"[FeiShu] send message failed, code={res.get('code')}, msg={res.get('msg')}")
|
||||
|
||||
|
||||
def fetch_access_token(self) -> str:
|
||||
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/"
|
||||
headers = {
|
||||
@@ -342,7 +384,6 @@ class FeiShuChanel(ChatChannel):
|
||||
else:
|
||||
logger.error(f"[FeiShu] fetch token error, res={response}")
|
||||
|
||||
|
||||
def _upload_image_url(self, img_url, access_token):
|
||||
logger.debug(f"[FeiShu] start process image, img_url={img_url}")
|
||||
|
||||
@@ -501,14 +542,16 @@ class FeiShuChanel(ChatChannel):
|
||||
headers=headers,
|
||||
timeout=(5, 60)
|
||||
)
|
||||
logger.info(f"[FeiShu] upload video response, status={upload_response.status_code}, res={upload_response.content}")
|
||||
logger.info(
|
||||
f"[FeiShu] upload video response, status={upload_response.status_code}, res={upload_response.content}")
|
||||
|
||||
response_data = upload_response.json()
|
||||
if response_data.get("code") == 0:
|
||||
# Add duration to the response data (API doesn't return it)
|
||||
upload_data = response_data.get("data")
|
||||
upload_data['duration'] = duration # Add our calculated duration
|
||||
logger.info(f"[FeiShu] Upload complete: file_key={upload_data.get('file_key')}, duration={duration}ms")
|
||||
logger.info(
|
||||
f"[FeiShu] Upload complete: file_key={upload_data.get('file_key')}, duration={duration}ms")
|
||||
return upload_data
|
||||
else:
|
||||
logger.error(f"[FeiShu] upload video failed: {response_data}")
|
||||
@@ -572,7 +615,8 @@ class FeiShuChanel(ChatChannel):
|
||||
headers=headers,
|
||||
timeout=(5, 30) # 5s connect, 30s read timeout
|
||||
)
|
||||
logger.info(f"[FeiShu] upload file response, status={upload_response.status_code}, res={upload_response.content}")
|
||||
logger.info(
|
||||
f"[FeiShu] upload file response, status={upload_response.status_code}, res={upload_response.content}")
|
||||
|
||||
response_data = upload_response.json()
|
||||
if response_data.get("code") == 0:
|
||||
|
||||
@@ -38,7 +38,7 @@ class AgentPlugin(Plugin):
|
||||
"""Load configuration from config.yaml file."""
|
||||
config_path = os.path.join(self.path, "config.yaml")
|
||||
if not os.path.exists(config_path):
|
||||
logger.warning(f"Config file not found at {config_path}")
|
||||
logger.debug(f"Config file not found at {config_path}")
|
||||
return {}
|
||||
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
|
||||
@@ -51,7 +51,7 @@ class Banwords(Plugin):
|
||||
self.reply_action = conf.get("reply_action", "ignore")
|
||||
logger.debug("[Banwords] inited")
|
||||
except Exception as e:
|
||||
logger.warn("[Banwords] init failed, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/banwords .")
|
||||
logger.debug("[Banwords] init failed, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/banwords .")
|
||||
raise e
|
||||
|
||||
def on_handle_context(self, e_context: EventContext):
|
||||
|
||||
Reference in New Issue
Block a user