# Danding_QqPush 插件 用于通过外部 HTTP API 向 QQ 群定向推送通知的 NoneBot 插件。 ## 功能特性 - ✅ 通过 HTTP API 推送消息到指定 QQ 群 - ✅ 自动将文本渲染为图片,避免长文本刷屏 - ✅ 统一通知卡片版式,群聊/私聊使用不同场景徽章 - ✅ 可选 @指定 QQ 用户;省略 `qq` 时直接发送群消息 - ✅ 使用 `#` 符号表示换行 - ✅ 基于 Token 的简单鉴权机制 - ✅ 支持中文文本渲染 - ✅ 自带超级用户预览命令「测试通知图片」,可在群聊/私聊中直接核对卡片版式 ## 目录结构 ``` danding_qqpush/ ├── __init__.py # 插件初始化模块 ├── config.py # 配置模块 ├── api.py # FastAPI 路由模块 ├── text_parser.py # 文本处理模块 ├── image_render.py # 通知卡片生成模块 ├── article_render.py # 公告 HTML 截图模块 ├── preview_image_render.py # 预览命令共用的渲染器实例 ├── sender.py # 消息发送模块 ├── test_command.py # 超级用户「测试通知图片」预览命令 └── utils.py # 工具函数模块 ``` ## 通知卡片样式 群聊与私聊的普通图片通知(即未传 `html` 的文本通知)共用同一套「冷灰外衬 + 白色圆角卡片」版式,只通过右上角的场景徽章区分来源: - 顶部为蓝色品牌头部:左侧固定文案「蛋定助手」,右侧为白底胶囊场景徽章 - 正文使用比纯黑更柔和的墨色,行距不低于 1.6 倍 - 底部为分割线与脚注「本消息由蛋定助手自动生成」 - 正文按宽度自动折行,并做中文避头尾处理 - Emoji 按整体方块宽度测量,避免溢出卡片;Emoji 数据源不可用时自动降级为普通文本绘制,通知仍会正常发出 ### 场景徽章 | 场景 (`scene`) | 徽章文案 | 徽章颜色 | 使用位置 | | -------------- | -------- | -------- | -------- | | `common` | 通用通知 | 灰色 | 渲染默认值 | | `group` | 群聊通知 | 品牌蓝 | `POST /danding/qqpush/{token}` 的普通文本通知 | | `private` | 私聊通知 | 橙色 | `POST /danding/private/{token}` 的图片通知 | - `scene` 是渲染层内部参数,**不新增配置项、也不新增 API 请求字段**:请求体格式保持不变,场景由插件按接口自动选择(群聊接口传 `group`,私聊接口传 `private`) - 未知的 `scene` 取值会回退为 `common` - 传入 `html` 的公告推送使用 Chromium 截图(`article_render.py`),不使用本卡片版式 ```python renderer.render(text) # 通用通知 renderer.render(text, scene="group") # 群聊通知 renderer.render(text, scene="private") # 私聊通知 renderer.render_to_base64(text, title="🏇 赛马开赛", scene="group") # 自定义标题行 renderer.render(text, title=None) # 不绘制标题行 ``` `title` 默认沿用旧版固定文案「蛋定助手通知您:」以保持向后兼容;显式传 `None` 或空字符串才隐藏标题行(卡片头部已展示「蛋定助手」品牌,需要更紧凑的版式时可以隐藏)。 ## 测试通知图片命令 不想注册 HTTP 接口、也不想占用群聊刷屏时,可以直接用机器人自带的预览命令核对卡片版式。 | 项目 | 说明 | | ---- | ---- | | 主命令 | `测试通知图片` | | 别名 | `通知图片测试`、`测试通知卡片` | | 权限 | 仅 NoneBot 超级用户(`SUPERUSER`,即 `.env` 的 `SUPERUSERS`) | | 触发方式 | 群聊直接发送,或与机器人私聊发送;均不需要 @机器人 | - 群聊中触发时渲染 `scene="group"`(群聊通知徽章),私聊中触发时渲染 `scene="private"`(私聊通知徽章) - 预览内容是固定示例,覆盖标题行、中文、多行、Emoji 与中文标点;标题行特意写成 `# 会被解析为换行:`,同时演示 `#` 作为换行符的用法 - 命令复用通知卡片渲染器与插件配置(字体、宽度、行距等),不新增配置项,也不改变任何 HTTP 接口 - 渲染是同步阻塞操作,命令会把它放到 `asyncio.to_thread` 执行,避免阻塞事件循环 - 渲染失败时机器人会记录 `[Danding_QqPush] 通知图片测试失败` 日志,并回复「通知图片生成失败,请查看机器人日志后重试。」 ### 用法示例 ``` (群聊 / 私聊中直接发送) 测试通知图片 ``` ## 安装与配置 ### 1. 确保依赖已安装 ```bash pip install pillow ``` ### 2. 配置 Token 在 `.env` 文件或 NoneBot 配置文件中添加: ```env DANDING_QQPUSH_TOKEN = "your-custom-token-here" ``` 如果不配置,默认使用 `danding-8HkL9xQ2`。 ### 3. 启动 NoneBot ```bash nb run ``` 插件会自动加载,并在日志中显示注册的 API 路径。 ## API 使用说明 ### 接口信息 - **方法**: `POST` - **路径**: `/danding/qqpush/{token}` - **Content-Type**: `application/json` ### 请求参数 | 参数名 | 类型 | 必填 | 说明 | | -------- | ------ | ---- | ------------------------ | | token | path | 是 | 配置的 Token | | group_id | int | 是 | 接收消息的 QQ 群号 | | qq | int | 否 | 可选的被 @ QQ 号;省略时不 @ 任何人 | | text | string | 是 | 通知文本(`#` 表示换行) | ### 请求示例 ```bash curl -X POST "http://localhost:8080/danding/qqpush/danding-8HkL9xQ2" \ -H "Content-Type: application/json" \ -d '{ "group_id": 123456789, "text": "系统告警#数据库连接失败#请立即处理" }' ``` ### Python 请求示例 ```python import requests url = "http://localhost:8080/danding/qqpush/danding-8HkL9xQ2" data = { "group_id": 123456789, "text": "系统告警#数据库连接失败#请立即处理" } response = requests.post(url, json=data) print(response.json()) ``` ### 响应示例 **成功响应** (200): ```json { "success": true, "message": "推送成功", "data": { "group_id": 123456789, "qq": null, "message_id": 12345 } } ``` **错误响应** (400): ```json { "detail": "group_id 不能为空" } ``` **错误响应** (403): ```json { "detail": "Token 验证失败" } ``` **错误响应** (500): ```json { "detail": "Bot 未连接,请检查机器人状态" } ``` ## 配置选项 可以在配置文件中自定义以下选项: ```python class Config(BaseModel): Token: str = "danding-8HkL9xQ2" """API 访问 Token""" ImageWidth: int = 800 """图片宽度(像素)""" ImageFontSize: int = 24 """字体大小(像素)""" ImagePadding: int = 30 """图片内边距(像素)""" ImageLineSpacing: float = 1.4 """行距倍数""" ImageBgColor: tuple = (252, 252, 252) """图片背景颜色 (R, G, B)""" ImageTextColor: tuple = (0, 0, 0) """文本颜色 (R, G, B)""" MaxTextLength: int = 2000 """最大文本长度(字符数)""" FontPaths: list = [...] """字体文件路径列表""" ``` 与通知卡片相关的渲染约定(沿用上述配置,无需新增配置项): - `ImageBgColor` 用作卡片填充色;卡片外衬固定为冷灰色,保证浅色卡片轮廓可见 - `ImageTextColor` 传入纯黑 `(0, 0, 0)` 时会自动替换为柔和的 `(31, 35, 41)`,其他颜色原样使用 - `ImageLineSpacing` 低于 `1.6` 时正文按 `1.6` 倍行距渲染 - `ImageWidth`/`ImagePadding`/`FontPaths` 的语义不变,卡片与正文均按它们计算版式 ## 文本格式说明 - 使用 `#` 符号表示换行 - 示例:`第一行#第二行#第三行` 会被渲染为三行文本 - 超过最大长度的文本会被自动截断 ## 注意事项 1. **安全性**: Token 泄露只影响推送能力,无账号风险,但建议定期更换 2. **Bot 状态**: 确保 Bot 已连接,否则会返回 500 错误 3. **群权限**: 确保 Bot 在目标群中有发送消息的权限 4. **字体支持**: 插件会自动尝试加载系统中的中文字体 ## 故障排查 ### 问题:启动时显示 "未找到可用的 Bot 实例" **说明**: 这是正常现象。插件加载时 Bot 可能还未连接,API 调用时会动态获取 Bot 实例。 ### 问题:返回 "Bot 未连接" **解决方案**: - 检查 NoneBot 是否正常启动 - 检查 OneBot V11 连接状态 - 确认 Bot 已成功连接到 QQ 服务器 ### 问题:图片显示乱码 **解决方案**: - 检查系统是否安装了中文字体 - 在 `FontPaths` 配置中添加正确的字体路径 ### 问题:消息发送失败 **解决方案**: - 检查 Bot 是否在目标群中 - 检查 Bot 是否有发送消息的权限 - 查看日志中的详细错误信息 ### 问题:发送「测试通知图片」没有反应 **解决方案**: - 确认发送者的 QQ 号在 `.env` 的 `SUPERUSERS` 中(该命令仅超级用户可用,其他用户发送不会触发、也不会有提示) - 确认消息就是命令本身(`测试通知图片` / `通知图片测试` / `测试通知卡片`),命令后跟其他内容不会触发 - 若机器人回复「通知图片生成失败,请查看机器人日志后重试。」,按日志中 `[Danding_QqPush] 通知图片测试失败` 的报错排查字体路径与配置 ## 测试 使用提供的测试脚本进行测试: ```bash python test_qqpush.py ``` 修改脚本中的 `group_id` 和 `qq` 为实际值。 插件自带的自动化测试(不需要真实 QQ 连接): ```bash python -m pytest tests/test_danding_qqpush_test_command.py -q ```