Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
OPENAI_API_KEY=your_api_key_here
COOKIES_STR=your_cookies_here
COOKIES_STR=your_cookies_here
# 飞书通知配置
ENABLE_FEISHU_NOTIFY=true
FEISHU_APP_ID=xxx
FEISHU_APP_SECRET=xxx
FEISHU_RECEIVE_ID=xxx
FEISHU_RECEIVE_ID_TYPE=open_id
55 changes: 55 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from XianyuAgent import XianyuReplyBot
from context_manager import ChatContextManager

from utils.notifier import NotificationManager, FeishuNotifier

class XianyuLive:
def __init__(self, cookies_str):
Expand All @@ -32,6 +33,46 @@ def __init__(self, cookies_str):
self.heartbeat_task = None
self.ws = None

# 添加通知管理器
self.notification_manager = self._init_notification_manager()

# 添加Cookie过期检测
self.cookie_check_interval = 3600 # 每小时检查一次Cookie
self.last_cookie_check_time = time.time()

def _init_notification_manager(self):
"""初始化通知管理器"""
manager = NotificationManager()

# 飞书通知
if os.getenv("ENABLE_FEISHU_NOTIFY", "false").lower() == "true":
feishu_notifier = FeishuNotifier(
app_id=os.getenv("FEISHU_APP_ID", ""),
app_secret=os.getenv("FEISHU_APP_SECRET", ""),
receive_id_type=os.getenv("FEISHU_RECEIVE_ID_TYPE", "chat_id"),
receive_id=os.getenv("FEISHU_RECEIVE_ID", "")
)
manager.add_notifier(feishu_notifier)
logger.info("已启用飞书通知")

return manager

async def check_cookie_valid(self):
"""检查Cookie是否有效"""
try:
# 尝试获取token,如果失败则说明Cookie已过期
token_response = self.xianyu.get_token(self.cookies, self.device_id)
if token_response.get('ret') and token_response['ret'][0] != "SUCCESS::调用成功":
logger.error(f"Cookie已过期: {token_response}")
self.notification_manager.notify_cookie_expired()
return False
return True
except Exception as e:
logger.error(f"检查Cookie时出错: {e}")
self.notification_manager.notify_error("Cookie检查错误", str(e))
return False


async def send_msg(self, ws, cid, toid, text):
text = {
"contentType": 1,
Expand Down Expand Up @@ -340,8 +381,20 @@ async def handle_heartbeat_response(self, message_data):
return False

async def main(self):
# 系统启动通知
self.notification_manager.notify_system_start()

while True:
try:
# 检查Cookie是否有效
current_time = time.time()
if current_time - self.last_cookie_check_time >= self.cookie_check_interval:
self.last_cookie_check_time = current_time
if not await self.check_cookie_valid():
logger.warning("Cookie无效,等待30分钟后重试...")
await asyncio.sleep(1800) # 等待30分钟后重试
continue

headers = {
"Cookie": self.cookies_str,
"Host": "wss-goofish.dingtalk.com",
Expand Down Expand Up @@ -399,6 +452,7 @@ async def main(self):

except websockets.exceptions.ConnectionClosed:
logger.warning("WebSocket连接已关闭")
self.notification_manager.notify_error("连接错误", "WebSocket连接已关闭", "系统将在5秒后尝试重连")
if self.heartbeat_task:
self.heartbeat_task.cancel()
try:
Expand All @@ -409,6 +463,7 @@ async def main(self):

except Exception as e:
logger.error(f"连接发生错误: {e}")
self.notification_manager.notify_error("连接错误", str(e), "系统将在5秒后尝试重连")
if self.heartbeat_task:
self.heartbeat_task.cancel()
try:
Expand Down
Binary file modified requirements.txt
Binary file not shown.
119 changes: 119 additions & 0 deletions utils/notifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import os
import smtplib
import requests
import json
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from abc import ABC, abstractmethod
from loguru import logger
from typing import Dict, Any, List, Optional
from datetime import datetime
import json

import lark_oapi as lark
from lark_oapi.api.im.v1 import *



class BaseNotifier(ABC):
"""通知器基类"""

@abstractmethod
def send(self, title: str, content: str) -> bool:
"""
发送通知

Args:
title: 通知标题
content: 通知内容

Returns:
bool: 是否发送成功
"""
pass


class FeishuNotifier(BaseNotifier):
"""飞书通知器"""

def __init__(self, app_id: str, app_secret: str, receive_id_type: str, receive_id: str):
"""
初始化飞书通知器

Args:
app_id: 飞书应用的App ID
app_secret: 飞书应用的App Secret
receive_id_type: 接收者ID类型,可选值: "open_id", "user_id", "union_id", "email", "chat_id"
receive_id: 接收者ID
"""
try:
self.client = lark.Client.builder().app_id(app_id).app_secret(app_secret).build()
self.receive_id_type = receive_id_type
self.receive_id = receive_id
except ImportError:
logger.error("未安装lark_oapi库,请使用pip install lark_oapi安装")
raise

def send(self, title: str, content: str) -> bool:
try:
# 构建消息请求
req_content = json.dumps({"text": title + "\n" + content})
req: CreateMessageRequest = CreateMessageRequest.builder() \
.receive_id_type(self.receive_id_type) \
.request_body(CreateMessageRequestBody.builder()
.receive_id(self.receive_id)
.msg_type("text")
.content(req_content)
.build()) \
.build()

logger.debug(f"Receive ID : {self.receive_id}")
# 发送消息
resp: CreateMessageResponse = self.client.im.v1.message.create(req)

if resp.success():
logger.info(f"飞书通知发送成功: {title}")
return True
else:
logger.error(f"飞书通知发送失败: {resp.msg}")
return False
except Exception as e:
logger.error(f"飞书通知发送失败: {e}")
return False


class NotificationManager:
"""通知管理器"""

def __init__(self):
self.notifiers: List[BaseNotifier] = []

def add_notifier(self, notifier: BaseNotifier):
"""添加通知器"""
self.notifiers.append(notifier)

def notify(self, title: str, content: str) -> bool:
"""发送通知到所有通知器"""
success = False
for notifier in self.notifiers:
if notifier.send(title, content):
success = True
return success

def notify_error(self, error_type: str, error_msg: str, details: str = "") -> bool:
"""发送错误通知"""
title = f"闲鱼自动客服系统错误: {error_type}"
content = f"错误信息: {error_msg}\n\n详细信息: {details}"
return self.notify(title, content)

def notify_cookie_expired(self) -> bool:
"""发送Cookie过期通知"""
title = "闲鱼自动客服系统警告: Cookie已过期"
content = "您的闲鱼Cookie已过期,请尽快更新Cookie以保持系统正常运行。"
return self.notify(title, content)

def notify_system_start(self) -> bool:
"""发送系统启动通知"""
title = "闲鱼自动客服系统: 已启动"
content = f"系统已成功启动,当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
return self.notify(title, content)