105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
"""
|
||
企业微信桥接模块
|
||
使用 wecom-aibot-python-sdk 与企业微信建立 WebSocket 长连接,
|
||
接收用户消息后通过 ADK API 调用远程 Agent,再将 Agent 回复发送给用户。
|
||
|
||
本模块仅负责:
|
||
- 创建 WSClient 实例
|
||
- 注册事件和消息处理器
|
||
"""
|
||
import json
|
||
import logging
|
||
import traceback
|
||
|
||
from aibot import WSClient, WSClientOptions
|
||
|
||
from config import Config
|
||
from bot.agent_client import close_http_client
|
||
from bot.handlers import handle_message
|
||
from bot.message_parser import extract_metadata
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def create_bot_client() -> WSClient:
|
||
"""
|
||
创建企业微信机器人 WebSocket 客户端,并注册所有事件和消息处理器。
|
||
"""
|
||
ws_client = WSClient(
|
||
WSClientOptions(
|
||
bot_id=Config.WECHAT_BOT_ID,
|
||
secret=Config.WECHAT_BOT_SECRET,
|
||
)
|
||
)
|
||
|
||
# ---- 事件处理器 ----
|
||
|
||
@ws_client.on("authenticated")
|
||
def on_authenticated():
|
||
logger.info("企业微信机器人认证成功")
|
||
|
||
@ws_client.on("event.enter_chat")
|
||
async def on_enter_chat(frame):
|
||
"""用户进入会话,发送欢迎语"""
|
||
meta = extract_metadata(frame)
|
||
user_id = meta.get("user_id", "")
|
||
logger.info("用户进入会话: %s", user_id)
|
||
|
||
try:
|
||
await ws_client.reply_welcome(
|
||
frame,
|
||
{
|
||
"msgtype": "text",
|
||
"text": {
|
||
"content": "您好!我是智能助手,有什么可以帮您的吗?"
|
||
},
|
||
},
|
||
)
|
||
except Exception as e:
|
||
logger.error("发送欢迎语失败: %s", e)
|
||
|
||
@ws_client.on("event.feedback_event")
|
||
async def on_feedback(frame):
|
||
"""用户反馈事件"""
|
||
body = frame.get("body", {})
|
||
logger.info("收到用户反馈: %s", json.dumps(body, ensure_ascii=False))
|
||
|
||
@ws_client.on("disconnected")
|
||
def on_disconnected(reason):
|
||
logger.warning("连接被断开(可能被新连接踢掉): %s", reason)
|
||
|
||
# ---- 消息处理器 ----
|
||
|
||
@ws_client.on("message.text")
|
||
async def on_text(frame):
|
||
await _safe_handle(ws_client, frame, "text")
|
||
|
||
@ws_client.on("message.image")
|
||
async def on_image(frame):
|
||
await _safe_handle(ws_client, frame, "image")
|
||
|
||
@ws_client.on("message.voice")
|
||
async def on_voice(frame):
|
||
await _safe_handle(ws_client, frame, "voice")
|
||
|
||
@ws_client.on("message.file")
|
||
async def on_file(frame):
|
||
# file 类型可能包含视频
|
||
body = frame.get("body", {})
|
||
msg_type = "video" if body.get("video") else "file"
|
||
await _safe_handle(ws_client, frame, msg_type)
|
||
|
||
@ws_client.on("message.mixed")
|
||
async def on_mixed(frame):
|
||
await _safe_handle(ws_client, frame, "mixed")
|
||
|
||
return ws_client
|
||
|
||
|
||
async def _safe_handle(ws_client, frame: dict, msg_type: str):
|
||
"""安全地处理消息,捕获异常防止崩溃"""
|
||
try:
|
||
await handle_message(ws_client, frame, msg_type)
|
||
except Exception as e:
|
||
logger.error("处理%s消息异常: %s\n%s", msg_type, e, traceback.format_exc())
|