优化IO问题

This commit is contained in:
liuwei
2025-06-04 09:16:39 +08:00
parent b25d43963f
commit 7bf512ce2b
2 changed files with 125 additions and 183 deletions

145
README.MD
View File

@@ -85,105 +85,6 @@ pip install -r requirements.txt
- uvicorn~=0.34.2
- 更多依赖请查看 requirements.txt
## 🔌 插件依赖说明
### 核心服务依赖
- MySQL数据库用于存储用户数据、积分记录、群组信息等
- Redis服务用于缓存和会话管理
- Dify服务用于AI聊天功能[Dify开源地址](https://docs.dify.ai/zh-hans/introduction)
### 外部API依赖
- 天气API用于天气查询功能
- 新闻API用于全球快讯功能
- 音乐API用于音乐点播功能
- 抖音解析服务:用于视频解析功能
### 系统依赖
- Python 3.10+
- Git用于系统更新
- 文件系统存储空间:用于存储媒体文件
- 网络代理支持(可选):用于访问特定服务
### 插件特定依赖
1. **Dify插件**
- Dify服务自部署或云服务
- API密钥配置
- 代理设置(可选)
2. **抖音解析插件**
- 网络请求库
- 文件系统存储
- 代理支持(可选)
3. **群智闯关插件**
- MySQL数据库
- 积分系统集成
4. **积分交易插件**
- MySQL数据库
- 用户系统集成
5. **全球快讯插件**
- 新闻API服务
- 网络请求库
6. **音乐插件**
- 音乐API服务
- 音频处理库
7. **视频插件**
- 视频处理库
- 存储系统
8. **群管理插件**
- 数据库支持
- 群管理API
## 📖 使用说明
### 基础命令
1. 群智闯关
- `/s` - 加入答题游戏
- `/t` - 获取新问题
- `/a 任务ID 答案` - 回答问题
- `/r` - 查看排行榜
- `/l` - 查看活跃问题
- `/h` - 查看未解决问题
2. 积分系统
- `积分转账 积分数 @用户` - 转账积分
- `我的积分` - 查询积分
- `积分排行` - 查看排行榜
- `打劫 @用户` - 打劫积分
- `保释 @用户` - 保释用户
3. 新闻功能
- `全球新闻` - 获取国际新闻
- 支持:`国际新闻`、`环球新闻`、`政经新闻`
4. AI聊天
- `聊天 问题` - 与AI对话
- 支持:`ai`、`dify`、`AI` 开头
5. 插件管理
- `插件 列表` - 查看插件
- `插件 启用 [插件名]` - 启用插件
- `插件 禁用 [插件名]` - 禁用插件
- `插件 信息 [插件名]` - 查看插件信息
### 媒体功能
- `图来`/`秀人` - 获取图片
- `猛男` - 获取视频
- `美腿`/`腿来` - 获取美腿图片
- `点歌 歌曲名` - 点播音乐
- 直接发送抖音链接可自动解析
### 系统功能
- `签到` - 每日签到
- `更新系统` - 系统更新(管理员)
- `#总结` - 消息总结
## ⚙️ 配置说明
### 1. 配置文件
@@ -259,6 +160,50 @@ GameTask:
command-format: "游戏命令格式说明"
```
## 📖 使用说明
### 基础命令
1. 群智闯关
- `/s` - 加入答题游戏
- `/t` - 获取新问题
- `/a 任务ID 答案` - 回答问题
- `/r` - 查看排行榜
- `/l` - 查看活跃问题
- `/h` - 查看未解决问题
2. 积分系统
- `积分转账 积分数 @用户` - 转账积分
- `我的积分` - 查询积分
- `积分排行` - 查看排行榜
- `打劫 @用户` - 打劫积分
- `保释 @用户` - 保释用户
3. 新闻功能
- `全球新闻` - 获取国际新闻
- 支持:`国际新闻`、`环球新闻`、`政经新闻`
4. AI聊天
- `聊天 问题` - 与AI对话
- 支持:`ai`、`dify`、`AI` 开头
5. 插件管理
- `插件 列表` - 查看插件
- `插件 启用 [插件名]` - 启用插件
- `插件 禁用 [插件名]` - 禁用插件
- `插件 信息 [插件名]` - 查看插件信息
### 媒体功能
- `图来`/`秀人` - 获取图片
- `猛男` - 获取视频
- `美腿`/`腿来` - 获取美腿图片
- `点歌 歌曲名` - 点播音乐
- 直接发送抖音链接可自动解析
### 系统功能
- `签到` - 每日签到
- `更新系统` - 系统更新(管理员)
- `#总结` - 消息总结
## 📁 项目结构
```
abot/
@@ -351,4 +296,4 @@ python -m pip install --upgrade pip
## 🙏 致谢
感谢所有为本项目做出贡献的开发者。
感谢所有为本项目做出贡献的开发者。

View File

@@ -19,6 +19,7 @@ from utils.decorator.points_decorator import plugin_points_cost
from utils.media_downloader import MediaDownloader
from utils.string_utils import remove_trailing_content
from wechat_ipad import WechatAPIClient
import aiohttp
# 常见的图片和视频文件扩展名
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'}
@@ -146,7 +147,7 @@ class DifyPlugin(MessagePluginInterface):
try:
# 调用Dify API获取回复
success, response = self._chat_with_dify((roomid if roomid else sender), sender, query)
success, response = await self._chat_with_dify((roomid if roomid else sender), sender, query)
if not success:
return False, response
# 去除广告内容
@@ -162,7 +163,8 @@ class DifyPlugin(MessagePluginInterface):
await bot.send_image_message((roomid if roomid else sender), Path(response))
elif file_type == 2:
first_farme = self._get_first_frame(response, f"dify_frame_{int(time.time())}.jpg")
await bot.send_video_message((roomid if roomid else sender), Path(response), Path(first_farme))
await bot.send_video_message((roomid if roomid else sender), Path(response),
Path(first_farme))
else:
return False, "获取媒资失败"
else:
@@ -219,7 +221,7 @@ class DifyPlugin(MessagePluginInterface):
try:
# 调用Dify API获取回复
success, response = self._chat_with_dify(session_id, user_id, query)
success, response = await self._chat_with_dify(session_id, user_id, query)
if not success:
return False, response
# 去除广告内容
@@ -269,7 +271,7 @@ class DifyPlugin(MessagePluginInterface):
revoke.add_message_to_revoke((roomid if roomid else sender), client_msg_id, create_time, new_msg_id, 5)
return False, f"处理出错: {e}"
def _chat_with_dify(self, session_id: str, user_id: str, query: str) -> Tuple[bool, Optional[str]]:
async def _chat_with_dify(self, session_id: str, user_id: str, query: str) -> Tuple[bool, Optional[str]]:
"""
与Dify API交互获取回复
@@ -330,12 +332,7 @@ class DifyPlugin(MessagePluginInterface):
data["conversation_history"] = self.conversations[session_id]
# 设置代理
proxies = None
if self.http_proxy:
proxies = {
"http": self.http_proxy,
"https": self.http_proxy
}
proxy = self.http_proxy if self.http_proxy else None
# 发送请求
url = f"{self.base_url}/workflows/run"
@@ -344,89 +341,89 @@ class DifyPlugin(MessagePluginInterface):
self.LOG.info(f"请求数据: {json.dumps(data, ensure_ascii=False)}")
try:
# 使用普通请求(非流式)
response = requests.post(url, headers=headers, json=data, proxies=proxies, timeout=40)
async with aiohttp.ClientSession() as session:
response = await session.post(url, headers=headers, json=data, proxy=proxy, timeout=40)
if response.status != 200:
error_text = await response.text()
self.LOG.error(f"Dify API请求失败: {response.status} {error_text}")
return False, f"请求失败,状态码: {response.status}"
if response.status_code != 200:
self.LOG.error(f"Dify API请求失败: {response.status_code} {response.text}")
return False, f"请求失败,状态码: {response.status_code}"
# 解析响应
response_data = await response.json()
self.LOG.info(f"收到Dify API响应: {json.dumps(response_data, ensure_ascii=False)}")
# 解析响应
response_data = response.json()
self.LOG.info(f"收到Dify API响应: {json.dumps(response_data, ensure_ascii=False)}")
# 提取回答内容
answer = ""
total_tokens = 0
# 提取回答内容
answer = ""
total_tokens = 0
# 获取输出内容
outputs = response_data.get("data", {}).get("outputs", {})
if outputs:
# 处理媒体类型返回
if "result" in outputs and "type" in outputs:
if outputs["type"] == "image":
downloader = MediaDownloader()
image_url = outputs["result"]
image_path = downloader.download_media(image_url)
answer = image_path
if outputs["type"] == "video":
downloader = MediaDownloader()
image_url = outputs["result"]
image_path = downloader.download_media(image_url)
answer = image_path
# 处理文本类型返回
elif "text" in outputs and isinstance(outputs["text"], str):
answer = outputs["text"]
# 兼容旧版处理逻辑
else:
for key, value in outputs.items():
if isinstance(value, str) and value.strip():
answer += value
elif isinstance(value, dict):
# 处理嵌套字典的情况
for sub_key, sub_value in value.items():
if isinstance(sub_value, str) and sub_value.strip():
answer += sub_value
elif isinstance(value, list):
# 处理列表的情况
for item in value:
if isinstance(item, str) and item.strip():
answer += item
elif isinstance(item, dict):
# 处理列表中的字典
for item_key, item_value in item.items():
if isinstance(item_value, str) and item_value.strip():
answer += item_value
# 获取输出内容
outputs = response_data.get("data", {}).get("outputs", {})
if outputs:
# 处理媒体类型返回
if "result" in outputs and "type" in outputs:
if outputs["type"] == "image":
downloader = MediaDownloader()
image_url = outputs["result"]
image_path = downloader.download_media(image_url)
answer = image_path
if outputs["type"] == "video":
downloader = MediaDownloader()
image_url = outputs["result"]
image_path = downloader.download_media(image_url)
answer = image_path
# 处理文本类型返回
elif "text" in outputs and isinstance(outputs["text"], str):
answer = outputs["text"]
# 兼容旧版处理逻辑
else:
for key, value in outputs.items():
if isinstance(value, str) and value.strip():
answer += value
elif isinstance(value, dict):
# 处理嵌套字典的情况
for sub_key, sub_value in value.items():
if isinstance(sub_value, str) and sub_value.strip():
answer += sub_value
elif isinstance(value, list):
# 处理列表的情况
for item in value:
if isinstance(item, str) and item.strip():
answer += item
elif isinstance(item, dict):
# 处理列表中的字典
for item_key, item_value in item.items():
if isinstance(item_value, str) and item_value.strip():
answer += item_value
# 获取token使用情况
total_tokens = response_data.get("data", {}).get("total_tokens", 0)
# 获取token使用情况
total_tokens = response_data.get("data", {}).get("total_tokens", 0)
# 更新会话历史
self.conversations[session_id].append({
"role": "user",
"content": query
})
# 更新会话历史
self.conversations[session_id].append({
"role": "user",
"content": query
})
self.conversations[session_id].append({
"role": "assistant",
"content": answer
})
self.conversations[session_id].append({
"role": "assistant",
"content": answer
})
# 限制会话历史长度
if len(self.conversations[session_id]) > self.max_history_length * 2:
self.conversations[session_id] = self.conversations[session_id][-self.max_history_length * 2:]
# 限制会话历史长度
if len(self.conversations[session_id]) > self.max_history_length * 2:
self.conversations[session_id] = self.conversations[session_id][-self.max_history_length * 2:]
# 统计token使用情况
if total_tokens > 0:
if user_id in self.token_usage:
self.token_usage[user_id] += total_tokens
else:
self.token_usage[user_id] = total_tokens
# 统计token使用情况
if total_tokens > 0:
if user_id in self.token_usage:
self.token_usage[user_id] += total_tokens
else:
self.token_usage[user_id] = total_tokens
self.LOG.info(
f"用户 {user_id} 本次消耗 {total_tokens} tokens累计 {self.token_usage[user_id]} tokens")
self.LOG.info(
f"用户 {user_id} 本次消耗 {total_tokens} tokens累计 {self.token_usage[user_id]} tokens")
return True, answer
return True, answer
except Exception as e:
self.LOG.error(f"处理Dify响应时出错: {str(e)}")