- 增加了通过外部 HTTP API 向 QQ 群组发送消息的核心功能。 - 实现了对长文本消息的图片渲染,以避免被认定为垃圾信息。 - 支持在消息中提及特定的 QQ 用户。 - 创建了用于 API 令牌和图片渲染设置的配置选项。 - 开发了一个测试脚本以验证 API 功能。 - 对现有代码进行了重构,以提高组织性和可维护性。
143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
"""API 接口模块 - FastAPI 路由定义"""
|
|
from fastapi import APIRouter, Request, HTTPException
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
from nonebot import get_driver, logger
|
|
|
|
from .config import Config
|
|
from .text_parser import TextParser
|
|
from .image_render import ImageRenderer
|
|
from .sender import sender
|
|
|
|
|
|
# 请求体模型
|
|
class PushRequest(BaseModel):
|
|
"""推送请求模型"""
|
|
group_id: int
|
|
"""接收消息的 QQ 群号"""
|
|
|
|
qq: int
|
|
"""被 @ 的 QQ 号"""
|
|
|
|
text: str
|
|
"""通知文本(# 表示换行)"""
|
|
|
|
|
|
# 响应模型
|
|
class PushResponse(BaseModel):
|
|
"""推送响应模型"""
|
|
success: bool
|
|
"""是否成功"""
|
|
|
|
message: str
|
|
"""响应消息"""
|
|
|
|
data: Optional[dict] = None
|
|
"""返回数据(如有)"""
|
|
|
|
|
|
# 创建路由器
|
|
router = APIRouter()
|
|
|
|
|
|
def create_routes(token: str, config: Config):
|
|
"""
|
|
创建 API 路由
|
|
|
|
Args:
|
|
token: 鉴权 Token
|
|
config: 配置对象
|
|
"""
|
|
|
|
@router.post(f"/danding/qqpush/{token}", response_model=PushResponse)
|
|
async def qqpush(request: Request, data: PushRequest):
|
|
"""
|
|
QQ 消息推送接口
|
|
|
|
Args:
|
|
request: FastAPI 请求对象
|
|
data: 推送请求数据
|
|
|
|
Returns:
|
|
推送结果
|
|
"""
|
|
try:
|
|
# 1. 验证参数
|
|
if not data.group_id:
|
|
raise HTTPException(status_code=400, detail="group_id 不能为空")
|
|
|
|
if not data.qq:
|
|
raise HTTPException(status_code=400, detail="qq 不能为空")
|
|
|
|
if not data.text or not isinstance(data.text, str):
|
|
raise HTTPException(status_code=400, detail="text 不能为空且必须是字符串")
|
|
|
|
# 2. 检查 Bot 是否在线
|
|
bot = sender.get_bot()
|
|
if not bot:
|
|
logger.error("Bot 实例未设置,无法发送消息")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="Bot 未连接,请检查机器人状态"
|
|
)
|
|
|
|
# 3. 文本处理
|
|
text_parser = TextParser(max_length=config.MaxTextLength)
|
|
if not text_parser.validate_text(data.text):
|
|
raise HTTPException(status_code=400, detail="文本内容无效")
|
|
|
|
parsed_text = text_parser.parse(data.text)
|
|
logger.info(f"解析文本: {parsed_text[:50]}..." if len(parsed_text) > 50 else parsed_text)
|
|
|
|
# 4. 生成图片
|
|
image_renderer = ImageRenderer(
|
|
width=config.ImageWidth,
|
|
font_size=config.ImageFontSize,
|
|
padding=config.ImagePadding,
|
|
line_spacing=config.ImageLineSpacing,
|
|
bg_color=config.ImageBgColor,
|
|
text_color=config.ImageTextColor,
|
|
font_paths=config.FontPaths
|
|
)
|
|
|
|
image_base64 = image_renderer.render_to_base64(parsed_text)
|
|
logger.info("图片生成成功")
|
|
|
|
# 5. 发送消息
|
|
send_result = await sender.send_to_group(
|
|
group_id=data.group_id,
|
|
qq=data.qq,
|
|
image_base64=image_base64
|
|
)
|
|
|
|
if not send_result["success"]:
|
|
logger.error(f"消息发送失败: {send_result['error']}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=send_result["message"]
|
|
)
|
|
|
|
logger.info(f"消息发送成功 - 群: {data.group_id}, @: {data.qq}")
|
|
|
|
return PushResponse(
|
|
success=True,
|
|
message="推送成功",
|
|
data={
|
|
"group_id": data.group_id,
|
|
"qq": data.qq,
|
|
"message_id": send_result["data"].get("message_id")
|
|
}
|
|
)
|
|
|
|
except HTTPException:
|
|
raise
|
|
|
|
except Exception as e:
|
|
logger.exception(f"推送接口异常: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"服务器内部错误: {str(e)}"
|
|
)
|
|
|
|
return router
|