ForcePilot/backend/package/yuxi/channels/adapters/qqbot/voice_send.py
Kris ef5483dc1a refactor(qqbot): 重构QQ机器人适配器代码,优化多项功能与结构
主要变更:
1. 修复速率限流器使用setdefault替代重复创建令牌桶
2. 重构交互注册表匹配逻辑,优化精确匹配查找
3. 重构去重缓存逻辑,移到适配器实例方法
4. 重构发送URL解析,增加合法性校验并拆分公共方法
5. 优化流式消息处理逻辑,简化flush_controller调用
6. 重构群聊类型判断代码,简化语法
7. 修复重连管理器对None类型关闭分类的处理
8. 新增消息缓存、线程模拟器、发送初始化模块
9. 重构凭证备份与会话存储逻辑,支持环境变量指定状态目录
10. 新增配置提示与向导二维码绑定功能
11. 优化媒体上传逻辑,增加重试机制与缓存
12. 新增审批键盘模板构建函数
13. 重构消息格式处理,修正媒体发送字段与长度限制
14. 修复令牌过期时间计算,使用time.time替代monotonic
15. 新增群组激活缓冲区与用户追踪器增强功能
16. 修复换行符问题,统一文件结尾格式
2026-05-13 16:13:48 +08:00

167 lines
5.0 KiB
Python

from __future__ import annotations
import aiohttp
from yuxi.channels.models import DeliveryResult
from yuxi.utils.logging_config import logger
from .constants import GROUP_CHAT_PREFIX
from .media_upload import (
FILE_TYPE_VOICE,
build_media_payload,
upload_media,
validate_media_size,
)
from .send import resolve_send_url
async def send_voice(
voice_data: bytes,
chat_id: str,
token: str,
http_client: aiohttp.ClientSession,
api_base: str,
filename: str = "voice.mp3",
max_size_mb: int = 100,
) -> DeliveryResult:
validate_media_size(voice_data, max_size_mb=max_size_mb, label="voice")
group_openid = None
if chat_id.startswith(GROUP_CHAT_PREFIX):
group_openid = chat_id.replace(GROUP_CHAT_PREFIX, "")
try:
file_id = await upload_media(
voice_data,
token,
http_client=http_client,
filename=filename,
file_type=FILE_TYPE_VOICE,
group_openid=group_openid,
)
except Exception as e:
return DeliveryResult(success=False, error=f"Voice upload failed: {e}")
payload = build_media_payload(chat_id, file_id, msg_type=7)
url = resolve_send_url(api_base, chat_id)
headers = {
"Authorization": f"QQBot {token}",
"Content-Type": "application/json",
}
try:
async with http_client.post(url, json=payload, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
return DeliveryResult(
success=True,
message_id=data.get("id") or data.get("message_id"),
)
return DeliveryResult(success=False, error=f"Voice send failed: HTTP {resp.status}")
except Exception as e:
logger.error(f"[QQBot] Voice send error: {e}")
return DeliveryResult(success=False, error=str(e))
async def send_video(
video_data: bytes,
chat_id: str,
token: str,
http_client: aiohttp.ClientSession,
api_base: str,
filename: str = "video.mp4",
max_size_mb: int = 100,
) -> DeliveryResult:
from .media_upload import FILE_TYPE_VIDEO
validate_media_size(video_data, max_size_mb=max_size_mb, label="video")
group_openid = None
if chat_id.startswith(GROUP_CHAT_PREFIX):
group_openid = chat_id.replace(GROUP_CHAT_PREFIX, "")
try:
file_id = await upload_media(
video_data,
token,
http_client=http_client,
filename=filename,
file_type=FILE_TYPE_VIDEO,
group_openid=group_openid,
)
except Exception as e:
return DeliveryResult(success=False, error=f"Video upload failed: {e}")
payload = build_media_payload(chat_id, file_id, msg_type=7)
url = resolve_send_url(api_base, chat_id)
headers = {
"Authorization": f"QQBot {token}",
"Content-Type": "application/json",
}
try:
async with http_client.post(url, json=payload, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
return DeliveryResult(
success=True,
message_id=data.get("id") or data.get("message_id"),
)
return DeliveryResult(success=False, error=f"Video send failed: HTTP {resp.status}")
except Exception as e:
logger.error(f"[QQBot] Video send error: {e}")
return DeliveryResult(success=False, error=str(e))
async def send_file(
file_data: bytes,
chat_id: str,
token: str,
http_client: aiohttp.ClientSession,
api_base: str,
filename: str = "file.bin",
max_size_mb: int = 100,
) -> DeliveryResult:
from .media_upload import FILE_TYPE_FILE
validate_media_size(file_data, max_size_mb=max_size_mb, label="file")
group_openid = None
if chat_id.startswith(GROUP_CHAT_PREFIX):
group_openid = chat_id.replace(GROUP_CHAT_PREFIX, "")
try:
file_id = await upload_media(
file_data,
token,
http_client=http_client,
filename=filename,
file_type=FILE_TYPE_FILE,
group_openid=group_openid,
)
except Exception as e:
return DeliveryResult(success=False, error=f"File upload failed: {e}")
payload = build_media_payload(chat_id, file_id, msg_type=7)
url = resolve_send_url(api_base, chat_id)
headers = {
"Authorization": f"QQBot {token}",
"Content-Type": "application/json",
}
try:
async with http_client.post(url, json=payload, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
return DeliveryResult(
success=True,
message_id=data.get("id") or data.get("message_id"),
)
return DeliveryResult(success=False, error=f"File send failed: HTTP {resp.status}")
except Exception as e:
logger.error(f"[QQBot] File send error: {e}")
return DeliveryResult(success=False, error=str(e))