From dedc872f1bc2bbca09ff4c7d62f7e37301b7d97e Mon Sep 17 00:00:00 2001 From: XiaM-Admin <1424473282@qq.com> Date: Tue, 20 Jan 2026 21:19:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=9A=E9=80=9A=E8=BF=87?= =?UTF-8?q?=20HTTP=20API=20=E5=AE=9E=E7=8E=B0=20Danding=5FQqPush=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=EF=BC=8C=E7=94=A8=E4=BA=8E=20QQ=20=E7=BE=A4?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=20-=20=E5=A2=9E=E5=8A=A0=E4=BA=86=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=A4=96=E9=83=A8=20HTTP=20API=20=E5=90=91=20QQ=20?= =?UTF-8?q?=E7=BE=A4=E7=BB=84=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF=E7=9A=84?= =?UTF-8?q?=E6=A0=B8=E5=BF=83=E5=8A=9F=E8=83=BD=E3=80=82=20-=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E4=BA=86=E5=AF=B9=E9=95=BF=E6=96=87=E6=9C=AC=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=9A=84=E5=9B=BE=E7=89=87=E6=B8=B2=E6=9F=93=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E9=81=BF=E5=85=8D=E8=A2=AB=E8=AE=A4=E5=AE=9A=E4=B8=BA?= =?UTF-8?q?=E5=9E=83=E5=9C=BE=E4=BF=A1=E6=81=AF=E3=80=82=20-=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=9C=A8=E6=B6=88=E6=81=AF=E4=B8=AD=E6=8F=90=E5=8F=8A?= =?UTF-8?q?=E7=89=B9=E5=AE=9A=E7=9A=84=20QQ=20=E7=94=A8=E6=88=B7=E3=80=82?= =?UTF-8?q?=20-=20=E5=88=9B=E5=BB=BA=E4=BA=86=E7=94=A8=E4=BA=8E=20API=20?= =?UTF-8?q?=E4=BB=A4=E7=89=8C=E5=92=8C=E5=9B=BE=E7=89=87=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=9A=84=E9=85=8D=E7=BD=AE=E9=80=89=E9=A1=B9?= =?UTF-8?q?=E3=80=82=20-=20=E5=BC=80=E5=8F=91=E4=BA=86=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=84=9A=E6=9C=AC=E4=BB=A5=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=20API=20=E5=8A=9F=E8=83=BD=E3=80=82=20-=20=E5=AF=B9=E7=8E=B0?= =?UTF-8?q?=E6=9C=89=E4=BB=A3=E7=A0=81=E8=BF=9B=E8=A1=8C=E4=BA=86=E9=87=8D?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E4=BB=A5=E6=8F=90=E9=AB=98=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E6=80=A7=E5=92=8C=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- danding_bot/plugins/auto_recall/__init__.py | 4 +- danding_bot/plugins/danding_qqpush/README.md | 219 ++++++++++++++++++ .../plugins/danding_qqpush/__init__.py | 69 ++++++ danding_bot/plugins/danding_qqpush/api.py | 142 ++++++++++++ danding_bot/plugins/danding_qqpush/config.py | 42 ++++ .../plugins/danding_qqpush/image_render.py | 207 +++++++++++++++++ danding_bot/plugins/danding_qqpush/sender.py | 146 ++++++++++++ .../plugins/danding_qqpush/text_parser.py | 69 ++++++ danding_bot/plugins/danding_qqpush/utils.py | 52 +++++ danding_bot/plugins/onmyoji_gacha.zip | Bin 25155 -> 0 bytes danding_bot/plugins/onmyoji_gacha/__init__.py | 19 +- .../plugins/onmyoji_gacha/api_utils.py | 95 +++++--- data/onmyoji_gacha/gacha.db | Bin 1622016 -> 1679360 bytes test_qqpush.py | 49 ++++ 14 files changed, 1065 insertions(+), 48 deletions(-) create mode 100644 danding_bot/plugins/danding_qqpush/README.md create mode 100644 danding_bot/plugins/danding_qqpush/__init__.py create mode 100644 danding_bot/plugins/danding_qqpush/api.py create mode 100644 danding_bot/plugins/danding_qqpush/config.py create mode 100644 danding_bot/plugins/danding_qqpush/image_render.py create mode 100644 danding_bot/plugins/danding_qqpush/sender.py create mode 100644 danding_bot/plugins/danding_qqpush/text_parser.py create mode 100644 danding_bot/plugins/danding_qqpush/utils.py delete mode 100644 danding_bot/plugins/onmyoji_gacha.zip create mode 100644 test_qqpush.py diff --git a/danding_bot/plugins/auto_recall/__init__.py b/danding_bot/plugins/auto_recall/__init__.py index f046ba0..001e592 100644 --- a/danding_bot/plugins/auto_recall/__init__.py +++ b/danding_bot/plugins/auto_recall/__init__.py @@ -25,8 +25,8 @@ plugin_config = get_plugin_config(Config) async def handle_api_result( bot: Bot, exception: Optional[Exception], api: str, data: Dict[str, Any], result: Any ): - """拦截 send_msg API 调用,监控发出的消息""" - if api != "send_msg" or exception: + """拦截 send_msg 和 send_group_msg API 调用,监控发出的消息""" + if api not in ["send_msg", "send_group_msg"] or exception: return # 获取消息 ID diff --git a/danding_bot/plugins/danding_qqpush/README.md b/danding_bot/plugins/danding_qqpush/README.md new file mode 100644 index 0000000..5942858 --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/README.md @@ -0,0 +1,219 @@ +# Danding_QqPush 插件 + +用于通过外部 HTTP API 向 QQ 群定向推送通知的 NoneBot 插件。 + +## 功能特性 + +- ✅ 通过 HTTP API 推送消息到指定 QQ 群 +- ✅ 自动将文本渲染为图片,避免长文本刷屏 +- ✅ 支持 @指定 QQ 用户 +- ✅ 使用 `#` 符号表示换行 +- ✅ 基于 Token 的简单鉴权机制 +- ✅ 支持中文文本渲染 + +## 目录结构 + +``` +danding_qqpush/ +├── __init__.py # 插件初始化模块 +├── config.py # 配置模块 +├── api.py # FastAPI 路由模块 +├── text_parser.py # 文本处理模块 +├── image_render.py # 图片生成模块 +├── sender.py # 消息发送模块 +└── utils.py # 工具函数模块 +``` + +## 安装与配置 + +### 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, + "qq": 987654321, + "text": "系统告警#数据库连接失败#请立即处理" + }' +``` + +### Python 请求示例 + +```python +import requests + +url = "http://localhost:8080/danding/qqpush/danding-8HkL9xQ2" +data = { + "group_id": 123456789, + "qq": 987654321, + "text": "系统告警#数据库连接失败#请立即处理" +} + +response = requests.post(url, json=data) +print(response.json()) +``` + +### 响应示例 + +**成功响应** (200): + +```json +{ + "success": true, + "message": "推送成功", + "data": { + "group_id": 123456789, + "qq": 987654321, + "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 = [...] + """字体文件路径列表""" +``` + +## 文本格式说明 + +- 使用 `#` 符号表示换行 +- 示例:`第一行#第二行#第三行` 会被渲染为三行文本 +- 超过最大长度的文本会被自动截断 + +## 注意事项 + +1. **安全性**: Token 泄露只影响推送能力,无账号风险,但建议定期更换 +2. **Bot 状态**: 确保 Bot 已连接,否则会返回 500 错误 +3. **群权限**: 确保 Bot 在目标群中有发送消息的权限 +4. **字体支持**: 插件会自动尝试加载系统中的中文字体 + +## 故障排查 + +### 问题:启动时显示 "未找到可用的 Bot 实例" + +**说明**: 这是正常现象。插件加载时 Bot 可能还未连接,API 调用时会动态获取 Bot 实例。 + +### 问题:返回 "Bot 未连接" + +**解决方案**: +- 检查 NoneBot 是否正常启动 +- 检查 OneBot V11 连接状态 +- 确认 Bot 已成功连接到 QQ 服务器 + +### 问题:图片显示乱码 + +**解决方案**: +- 检查系统是否安装了中文字体 +- 在 `FontPaths` 配置中添加正确的字体路径 + +### 问题:消息发送失败 + +**解决方案**: +- 检查 Bot 是否在目标群中 +- 检查 Bot 是否有发送消息的权限 +- 查看日志中的详细错误信息 + +## 测试 + +使用提供的测试脚本进行测试: + +```bash +python test_qqpush.py +``` + +修改脚本中的 `group_id` 和 `qq` 为实际值。 diff --git a/danding_bot/plugins/danding_qqpush/__init__.py b/danding_bot/plugins/danding_qqpush/__init__.py new file mode 100644 index 0000000..7b7d7d2 --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/__init__.py @@ -0,0 +1,69 @@ +"""Danding_QqPush 插件初始化模块""" +from nonebot import get_driver, get_bots +from nonebot.log import logger +from nonebot.plugin import PluginMetadata + +from .config import Config +from .api import create_routes +from .sender import sender + + +__plugin_meta__ = PluginMetadata( + name="danding_qqpush", + description="通过外部 HTTP API 向 QQ 群定向推送通知", + usage=""" + API 接口: + POST /danding/qqpush/{token} + + 请求参数: + { + "group_id": 123456789, + "qq": 987654321, + "text": "系统告警#数据库连接失败#请立即处理" + } + + 说明: + - text 中的 # 表示换行 + - 消息会自动渲染为图片并发送到指定群 + """, + config=Config, +) + + +# 加载配置 +plugin_config = Config.parse_obj(get_driver().config) + + +def register_routes(): + """注册 FastAPI 路由""" + driver = get_driver() + + # 创建并注册路由 + routes = create_routes(plugin_config.Token, plugin_config) + driver.server_app.include_router(routes) + + logger.info(f"[Danding_QqPush] API 路由已注册: /danding/qqpush/{plugin_config.Token}") + + +def init_bot(): + """初始化 Bot 实例""" + try: + bots = get_bots() + if bots: + # 获取第一个可用的 Bot + bot = list(bots.values())[0] + sender.set_bot(bot) + logger.info(f"[Danding_QqPush] Bot 已连接: {bot.self_id}") + else: + logger.warning("[Danding_QqPush] 未找到可用的 Bot 实例") + except Exception as e: + logger.warning(f"[Danding_QqPush] 初始化 Bot 失败: {str(e)}") + + +# 插件加载时注册路由并初始化 Bot +try: + register_routes() + init_bot() + logger.info("[Danding_QqPush] 插件加载成功") +except Exception as e: + logger.error(f"[Danding_QqPush] 插件加载失败: {str(e)}") diff --git a/danding_bot/plugins/danding_qqpush/api.py b/danding_bot/plugins/danding_qqpush/api.py new file mode 100644 index 0000000..d35e88d --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/api.py @@ -0,0 +1,142 @@ +"""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 diff --git a/danding_bot/plugins/danding_qqpush/config.py b/danding_bot/plugins/danding_qqpush/config.py new file mode 100644 index 0000000..5ac1b4a --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/config.py @@ -0,0 +1,42 @@ +"""Danding_QqPush 插件配置模块""" +from pydantic import BaseModel + + +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 = [ + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", + "/usr/share/fonts/wqy-microhei/wqy-microhei.ttc", + "/usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc", + "C:/Windows/Fonts/msyh.ttc", + "C:/Windows/Fonts/simhei.ttf", + ] + """字体文件路径列表""" diff --git a/danding_bot/plugins/danding_qqpush/image_render.py b/danding_bot/plugins/danding_qqpush/image_render.py new file mode 100644 index 0000000..deb7dc4 --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/image_render.py @@ -0,0 +1,207 @@ +"""图片生成模块 - 将文本渲染为图片""" +from PIL import Image, ImageDraw, ImageFont +import io +import base64 +from typing import Tuple + + +class ImageRenderer: + """图片渲染器""" + + def __init__( + self, + width: int = 800, + font_size: int = 24, + padding: int = 30, + line_spacing: float = 1.4, + bg_color: Tuple[int, int, int] = (252, 252, 252), + text_color: Tuple[int, int, int] = (0, 0, 0), + font_paths: list = None + ): + """ + 初始化图片渲染器 + + Args: + width: 图片宽度 + font_size: 字体大小 + padding: 内边距 + line_spacing: 行距倍数 + bg_color: 背景颜色 (R, G, B) + text_color: 文本颜色 (R, G, B) + font_paths: 字体文件路径列表 + """ + self.width = width + self.font_size = font_size + self.padding = padding + self.line_spacing = line_spacing + self.bg_color = bg_color + self.text_color = text_color + self.font_paths = font_paths or [] + + # 加载字体 + self.font = self._load_font() + + def _load_font(self) -> ImageFont.FreeTypeFont: + """加载字体""" + return self._load_font_by_size(self.font_size) + + def _load_font_by_size(self, font_size: int) -> ImageFont.FreeTypeFont: + """ + 加载指定大小的字体 + + Args: + font_size: 字体大小 + + Returns: + 字体对象 + """ + for font_path in self.font_paths: + try: + font = ImageFont.truetype(font_path, font_size) + return font + except Exception: + continue + + # 如果都失败了,使用默认字体 + return ImageFont.load_default() + + def _calculate_text_dimensions(self, text: str) -> Tuple[int, int]: + """ + 计算文本的尺寸 + + Args: + text: 文本内容 + + Returns: + (宽度, 高度) + """ + # 创建一个临时图片用于计算 + test_img = Image.new('RGB', (1, 1), color=self.bg_color) + test_draw = ImageDraw.Draw(test_img) + + bbox = test_draw.textbbox((0, 0), text, font=self.font) + width = bbox[2] - bbox[0] + height = bbox[3] - bbox[1] + + return width, height + + def _smart_text_wrap(self, text: str, max_width: int) -> list: + """ + 智能文本换行 + + Args: + text: 文本内容 + max_width: 最大宽度 + + Returns: + 换行后的文本列表 + """ + lines = [] + current_line = "" + current_width = 0 + + for char in text: + char_width, _ = self._calculate_text_dimensions(char) + + if current_width + char_width <= max_width: + current_line += char + current_width += char_width + else: + if current_line: + lines.append(current_line) + current_line = char + current_width = char_width + + if current_line: + lines.append(current_line) + + return lines + + def render(self, text: str) -> bytes: + """ + 将文本渲染为图片 + + Args: + text: 文本内容(已处理换行符) + + Returns: + 图片的字节数据 + """ + if not text: + text = "(空消息)" + + # 计算有效宽度 + effective_width = self.width - (2 * self.padding) + + # 按段落处理 + all_lines = [] + for paragraph in text.split('\n'): + if not paragraph: + all_lines.append("") + continue + + wrapped_lines = self._smart_text_wrap(paragraph, effective_width) + all_lines.extend(wrapped_lines) + + # 计算行高 + _, line_height = self._calculate_text_dimensions("测试") + total_line_height = int(line_height * self.line_spacing) + + # 标题相关 + title = "蛋定助手通知您:" + title_font_size = int(self.font_size * 1.3) # 标题字体放大1.3倍 + title_font = self._load_font_by_size(title_font_size) + title_height = int(line_height * 1.5) # 标题区域高度 + divider_height = 2 # 分割线高度 + divider_spacing = 10 # 分割线与标题的间距 + + # 计算总高度(包含标题区域) + content_height = (len(all_lines) * total_line_height) + (2 * self.padding) + header_height = title_height + divider_spacing + divider_height + self.padding + total_height = content_height + header_height + height = max(total_height, 200) # 最小高度 + + # 创建图片 + image = Image.new('RGB', (self.width, height), color=self.bg_color) + draw = ImageDraw.Draw(image) + + # 绘制标题(加粗效果通过增大字体实现) + title_x = self.padding + title_y = self.padding + draw.text((title_x, title_y), title, font=title_font, fill=self.text_color) + + # 绘制分割线 + divider_y = title_y + title_height + 10 + draw.line( + [(self.padding, divider_y), (self.width - self.padding, divider_y)], + fill=(200, 200, 200), + width=divider_height + ) + + # 绘制文本内容 + y = divider_y + divider_spacing + self.padding + for line in all_lines: + if line: # 跳过空行 + draw.text((self.padding, y), line, font=self.font, fill=self.text_color) + y += total_line_height + + # 转换为字节流 + img_byte_arr = io.BytesIO() + image.save(img_byte_arr, format='PNG', quality=95) + img_byte_arr.seek(0) + + return img_byte_arr.getvalue() + + def render_to_base64(self, text: str) -> str: + """ + 将文本渲染为图片并返回 base64 编码 + + Args: + text: 文本内容 + + Returns: + base64 编码的图片数据 + """ + img_bytes = self.render(text) + base64_str = base64.b64encode(img_bytes).decode('utf-8') + return f"base64://{base64_str}" diff --git a/danding_bot/plugins/danding_qqpush/sender.py b/danding_bot/plugins/danding_qqpush/sender.py new file mode 100644 index 0000000..f8c750a --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/sender.py @@ -0,0 +1,146 @@ +"""消息发送模块 - 负责向 QQ 群发送消息""" +from typing import Optional +from nonebot import get_bots +from nonebot.adapters.onebot.v11 import Bot, Message, MessageSegment + + +class MessageSender: + """消息发送器""" + + def __init__(self): + """初始化消息发送器""" + self.bot: Optional[Bot] = None + + def set_bot(self, bot: Bot): + """ + 设置 Bot 实例 + + Args: + bot: OneBot V11 Bot 实例 + """ + self.bot = bot + + def get_bot(self) -> Optional[Bot]: + """ + 获取 Bot 实例 + + Returns: + Bot 实例,如果未设置则尝试从全局获取 + """ + if self.bot: + return self.bot + + # 尝试从全局获取 Bot + try: + bots = get_bots() + if bots: + bot = list(bots.values())[0] + self.bot = bot + return bot + except Exception: + pass + + return None + + async def send_to_group( + self, + group_id: int, + qq: int, + image_base64: str + ) -> dict: + """ + 向指定群发送消息(@用户 + 图片) + + Args: + group_id: 群号 + qq: 要 @ 的 QQ 号 + image_base64: 图片的 base64 编码(格式:base64://...) + + Returns: + 发送结果字典 + + Raises: + ValueError: Bot 未设置 + Exception: 发送失败 + """ + bot = self.get_bot() + if not bot: + raise ValueError("Bot 实例未设置,无法发送消息") + + try: + # 构造消息:@用户 + 图片 + message = Message() + message.append(MessageSegment.at(qq)) + message.append(MessageSegment.image(image_base64)) + + # 发送群消息 + result = await bot.call_api( + "send_group_msg", + group_id=group_id, + message=message + ) + + return { + "success": True, + "data": result, + "message": "消息发送成功" + } + + except Exception as e: + # 捕获异常并返回错误信息 + return { + "success": False, + "error": str(e), + "message": f"消息发送失败: {str(e)}" + } + + async def send_text_to_group( + self, + group_id: int, + qq: int, + text: str + ) -> dict: + """ + 向指定群发送纯文本消息(@用户 + 文本) + + Args: + group_id: 群号 + qq: 要 @ 的 QQ 号 + text: 文本内容 + + Returns: + 发送结果字典 + """ + bot = self.get_bot() + if not bot: + raise ValueError("Bot 实例未设置,无法发送消息") + + try: + # 构造消息:@用户 + 文本 + message = Message() + message.append(MessageSegment.at(qq)) + message.append(MessageSegment.text(text)) + + # 发送群消息 + result = await bot.call_api( + "send_group_msg", + group_id=group_id, + message=message + ) + + return { + "success": True, + "data": result, + "message": "消息发送成功" + } + + except Exception as e: + return { + "success": False, + "error": str(e), + "message": f"消息发送失败: {str(e)}" + } + + +# 全局消息发送器实例 +sender = MessageSender() diff --git a/danding_bot/plugins/danding_qqpush/text_parser.py b/danding_bot/plugins/danding_qqpush/text_parser.py new file mode 100644 index 0000000..393ab04 --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/text_parser.py @@ -0,0 +1,69 @@ +"""文本处理模块 - 处理文本换行和格式化""" +from typing import List + + +class TextParser: + """文本解析器""" + + def __init__(self, max_length: int = 2000): + """ + 初始化文本解析器 + + Args: + max_length: 最大文本长度,超过将被截断 + """ + self.max_length = max_length + + def parse(self, text: str) -> str: + """ + 解析文本,将 # 转换为换行符 + + Args: + text: 原始文本,使用 # 表示换行 + + Returns: + 处理后的文本 + """ + if not text: + return "" + + # 截断超长文本 + if len(text) > self.max_length: + text = text[:self.max_length] + text += "...(文本过长已截断)" + + # 将 # 替换为换行符 + parsed_text = text.replace("#", "\n") + + return parsed_text + + def parse_to_lines(self, text: str) -> List[str]: + """ + 解析文本并返回行列表 + + Args: + text: 原始文本 + + Returns: + 文本行列表 + """ + parsed_text = self.parse(text) + return parsed_text.split("\n") + + def validate_text(self, text: str) -> bool: + """ + 验证文本是否有效 + + Args: + text: 待验证的文本 + + Returns: + 是否有效 + """ + if not text or not isinstance(text, str): + return False + + if len(text.strip()) == 0: + return False + + return True diff --git a/danding_bot/plugins/danding_qqpush/utils.py b/danding_bot/plugins/danding_qqpush/utils.py new file mode 100644 index 0000000..64414b6 --- /dev/null +++ b/danding_bot/plugins/danding_qqpush/utils.py @@ -0,0 +1,52 @@ +"""工具函数模块""" +import secrets +import string + + +def generate_token(length: int = 16, prefix: str = "danding-") -> str: + """ + 生成随机 Token + + Args: + length: 随机部分长度 + prefix: Token 前缀 + + Returns: + 生成的 Token + """ + # 生成随机字符串(字母和数字) + alphabet = string.ascii_letters + string.digits + random_part = ''.join(secrets.choice(alphabet) for _ in range(length)) + + return f"{prefix}{random_part}" + + +def validate_token(token: str, expected_token: str) -> bool: + """ + 验证 Token 是否正确 + + Args: + token: 待验证的 Token + expected_token: 期望的 Token + + Returns: + 是否匹配 + """ + if not token or not expected_token: + return False + + return token == expected_token + + +def format_log_message(message: str, level: str = "INFO") -> str: + """ + 格式化日志消息 + + Args: + message: 原始消息 + level: 日志级别 + + Returns: + 格式化后的消息 + """ + return f"[Danding_QqPush] [{level}] {message}" diff --git a/danding_bot/plugins/onmyoji_gacha.zip b/danding_bot/plugins/onmyoji_gacha.zip deleted file mode 100644 index 058e8f77c53ef004aae51d96fcf471589c5d738a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25155 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 0: # 为每张SSR/SP处理奖励 - auto_rewards = 0 - manual_rewards = 0 - - # 这里简化处理,只处理一次奖励(因为每次抽卡都是独立的) - # 如果需要为每张SSR/SP都发放奖励,可以循环处理 - auto_success, reward_msg = await process_ssr_sp_reward(user_id) - if auto_success: - auto_rewards += 1 - else: - manual_rewards += 1 + auto_success, reward_msg = await process_ssr_sp_reward(user_id, ssr_count) msg.append(f"\n\n{reward_msg}") # 通知管理员 admin_msg = f"🎉 用户 {user_name}({user_id}) 在三连抽中抽到了 {ssr_count} 张 SSR/SP!" - if auto_rewards > 0: - admin_msg += f" 已自动发放 {auto_rewards} 张奖励!" - if manual_rewards > 0: - admin_msg += f" 需要手动发放 {manual_rewards} 张奖励!" + if auto_success: + admin_msg += f" 已自动发放 {ssr_count} 张奖励!" + else: + admin_msg += f" 需要手动发放 {ssr_count} 张奖励!" await notify_admin(bot, admin_msg) await triple_gacha_matcher.finish(msg) diff --git a/danding_bot/plugins/onmyoji_gacha/api_utils.py b/danding_bot/plugins/onmyoji_gacha/api_utils.py index ba0c8c6..348efda 100644 --- a/danding_bot/plugins/onmyoji_gacha/api_utils.py +++ b/danding_bot/plugins/onmyoji_gacha/api_utils.py @@ -74,55 +74,70 @@ async def query_qq_binding(qq: str) -> Tuple[bool, Optional[str], Optional[str]] logger.error(f"查询QQ绑定状态异常: {str(e)}") return False, None, None -async def add_user_viptime(username: str, time_class: str = "Day") -> Tuple[bool, str]: +async def add_user_viptime(username: str, time_class: str = "Day", count: int = 1) -> Tuple[bool, str]: """ 为用户添加VIP时间 Args: username: 蛋定用户名 time_class: 时间类型 (Hour/Day/Week/Month/Season/Year) + count: 添加次数(默认为1) Returns: Tuple[是否成功, 响应消息] """ try: url = f"{DD_API_HOST}bot_add_user_viptime" - data = { - "user": BOT_USER_ID, - "token": BOT_TOKEN, - "username": username, - "classes": time_class - } - response = requests.post(url=url, json=data) - logger.debug(f"添加VIP时间响应: {response}") + # 如果count大于1,需要多次调用API + success_count = 0 + last_message = "" - if response.status_code != 200: - error_msg = f"添加VIP时间失败,状态码: {response.status_code}" - logger.error(error_msg) - return False, error_msg + for i in range(count): + data = { + "user": BOT_USER_ID, + "token": BOT_TOKEN, + "username": username, + "classes": time_class + } - result = response.json() - logger.debug(f"添加VIP时间结果: {result}") + response = requests.post(url=url, json=data) + logger.debug(f"添加VIP时间响应({i+1}/{count}): {response}") + + if response.status_code != 200: + error_msg = f"添加VIP时间失败({i+1}/{count}),状态码: {response.status_code}" + logger.error(error_msg) + continue + + result = response.json() + logger.debug(f"添加VIP时间结果({i+1}/{count}): {result}") + + if result.get("code") == 200: + success_count += 1 + last_message = result.get("msg", "添加VIP时间成功") + else: + error_msg = result.get("msg", "添加VIP时间失败") + logger.error(f"添加VIP时间失败({i+1}/{count}): {error_msg}") - if result.get("code") == 200: - return True, result.get("msg", "添加VIP时间成功") + if success_count == count: + return True, f"成功添加{count}次{time_class}时长。{last_message}" + elif success_count > 0: + return False, f"仅成功添加{success_count}/{count}次{time_class}时长。{last_message}" else: - error_msg = result.get("msg", "添加VIP时间失败") - logger.error(f"添加VIP时间失败: {error_msg}") - return False, error_msg + return False, f"添加{count}次{time_class}时长全部失败。" except Exception as e: error_msg = f"添加VIP时间异常: {str(e)}" logger.error(error_msg) return False, error_msg -async def process_ssr_sp_reward(user_id: str) -> Tuple[bool, str]: +async def process_ssr_sp_reward(user_id: str, count: int = 1) -> Tuple[bool, str]: """ 处理SSR/SP奖励发放 Args: user_id: QQ用户ID + count: 奖励数量(默认为1) Returns: Tuple[是否自动发放成功, 消息内容] @@ -132,26 +147,42 @@ async def process_ssr_sp_reward(user_id: str) -> Tuple[bool, str]: if not is_bound: # 用户未绑定,返回提示信息 - msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" - f"获得奖励:蛋定助手天卡一张\n" - f"获取奖励请联系管理员,或前往蛋定云服务中绑定QQ号即可体验自动加时!") + if count == 1: + msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" + f"获得奖励:蛋定助手天卡一张\n" + f"获取奖励请联系管理员,或前往蛋定云服务中绑定QQ号即可体验自动加时!") + else: + msg = (f"🎉恭喜您抽中了{count}张SSR/SP稀有度式神!🎉\n" + f"获得奖励:蛋定助手天卡{count}张\n" + f"获取奖励请联系管理员,或前往蛋定云服务中绑定QQ号即可体验自动加时!") return False, msg else: # 用户已绑定,自动加时 - success, message = await add_user_viptime(username, "Day") + success, message = await add_user_viptime(username, "Day", count) if success: masked_username = mask_username(username) - msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" - f"🎁已自动为您的蛋定账号({masked_username})添加天卡时长!\n" - f"📅到期时间: {message.split('到期时间为:')[-1] if '到期时间为:' in message else '请查看您的账号详情'}") + if count == 1: + msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" + f"🎁已自动为您的蛋定账号({masked_username})添加天卡时长!\n" + f"📅到期时间: {message.split('到期时间为:')[-1] if '到期时间为:' in message else '请查看您的账号详情'}") + else: + msg = (f"🎉恭喜您抽中了{count}张SSR/SP稀有度式神!🎉\n" + f"🎁已自动为您的蛋定账号({masked_username})添加{count}天卡时长!\n" + f"📅到期时间: {message.split('到期时间为:')[-1] if '到期时间为:' in message else '请查看您的账号详情'}") return True, msg else: # 自动加时失败,返回错误信息和手动领取提示 - msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" - f"获得奖励:蛋定助手天卡一张\n" - f"⚠️自动加时失败: {message}\n" - f"请联系管理员手动领取奖励!") + if count == 1: + msg = (f"🎉恭喜您抽中了SSR/SP稀有度式神!🎉\n" + f"获得奖励:蛋定助手天卡一张\n" + f"⚠️自动加时失败: {message}\n" + f"请联系管理员手动领取奖励!") + else: + msg = (f"🎉恭喜您抽中了{count}张SSR/SP稀有度式神!🎉\n" + f"获得奖励:蛋定助手天卡{count}张\n" + f"⚠️自动加时失败: {message}\n" + f"请联系管理员手动领取奖励!") return False, msg async def process_achievement_reward(user_id: str, achievement_id: str) -> Tuple[bool, str]: diff --git a/data/onmyoji_gacha/gacha.db b/data/onmyoji_gacha/gacha.db index eec60b34f8ad75c0fa41a16a62be5d2cfc8f250f..9146408d315b9b91eebe11fd2a3c839086656834 100644 GIT binary patch delta 73264 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%0A$h}Eig{R zM1%vIbU2a{6<|eUASgA>I_(5nxzB`?SSza*3} zl0Zb6_}|Z;jL2E!Y=X$ifXM0fxAAgMueKX^GyHas0pm8F_Sn+_vXe zz4t%?p@0ApO^}3_&9oV2?^E|yom=%@^(<)#)~)?c-BdU1v+ud55^4J#KPDnUDhwK} zafCdj8`1))3Ko+n(aFhds{Xke3qp)J;zT>C2eKIidS*yy3q7$1G(eKfx5yK^A*f0uHKFzQa4hFY>o`0 znYLRjvbsFgsvL5lRW#_)KspVIivi8KD$xEIZuxp_#b=J{4TK{5WraMlH#qZ0|F9>QK?^bM>r+3&_vksBK$%@8{FA3*~o>S+|nSi9RR zA<^qGBVe=}rC(?y4fvt}n*nk-4M`WXm_&hw61mjN>n%Wv+ngl8os7e^NrDPLIB@B} z;RB2N|Ioj_|3?VIiqN>A0_OJZ=xaXT_jd@wdt2X%zJpEVy}yNG;HLU1_5JFV+MBgs zAkgWL%z0zE-`zdQo+y)>D zpyaY4gF+809&f>8T(w1=ShvS7<1w_Iy&iX#OnJ+uzn`CLVLu>~J2c?s@7o|0-q|nZId&bhBL-qEMG_&@FJ&0<&rAB%eP9RQM0gw<7 zC~H>$a|t;ozxD0_W*K@;0T>V6^!xn_tP&BKfIP|z6rF=%6ChrQw}`)DG=YSAlLywJ zzVUFEdQ=sdPT0V>OWjH1`*(?@L15)AoZj;7u*2{;IRrP}^dpZhnA;!Z0`?F*!<#QS zqo*To$D%23UZ95yhyAe1@FRpL@vOMhP7C_^8ax+qC-lzUp0iEnOlNj2+xCP__EJPC zcG6g|S+O)$8w8y1hgwHw_)P%&MN!xnmJD(vZ~A|Jb)haSlImLHjQ27xwOAVSQjMiC zFBPpB$8XuJMsmH*b(*gZ zI4W16sO<6006=l^y=ee6-^@K}095MC-2uSP6Vu;&GW_arh=}s_yQEi`{*E+?w-M7X zlFcLSd6fPRtr{0kJbn)!Ef1~SH;rs?ai!=rSZ2{>h6i(vg)pgrBOr-d1o}!0SCq8 z%w?iP9|(p4wgm>gna^m+I)E(PrIKbz5*Z0+E@6Mdc$}OEY?rvWXP1OocIu1L04&Ib z1`rT9(@#9QplG`1NbucUplcIp{L%TbG=5*_>7EE2-+pc^jkR=+q?w+>ccA6Z4q86k zpoXJ2o>K*WR?2hsyfagt^K{7ytx$(ewlu<8N2+zCVFsT$UD8Y=APsZv%xSbeI~(pb zqr9OtDb+tUczG89mJfJ-O3HH{kvTcuL}y2@2!^suI8A2Yz%=6Uj-~JU=SsIlIA; zDbHEcM+DEqZP(D?!O^KF4sM4NFdMh}CayR)Z8_ceO?VW;6!p-csL^IBtb7*r{lE#> z*=b<)z;_0w5P1Edfr7-j`C|Vi5DAz2HeuQSpZYHA8;yyCx7L0Pk?;+*<+X*?_o_cf z%;;OI$5$6uc2utaRpqJ5J(V*mdzb&c+%A8s`~U*qErSzqP3gO(&y_Af%)5LsZ~|Ud z94*!huNHn(__M;*g+mLo^RMTBl7BRRBf^R<$i18UdG2euRk@RJI_?P=A2f0}xuS_^59q1+nb@*iRT9eV#fNz@W z3C8Tf>l1XcgM3*bFH1Nk9Vlfc)~G`#+t-IA)Mm1|jK+!AC)i}#43Wb723~m&Y;w#o z=RiqO(^5h(LSlW&%NY^D zBaz$_xJd!YBgcWwP!liiI0WJVo#-WaY%H1@ z^2lz9A}F?Q773#XB6%W2Ho1;@*J4ql2_|_$lfG0l!o$X3b6$c-c96W$(;^c@a)fMh z|81eDQZmU-A01b9|1!n}i0tVV@W>c6!{= z$(#B-p^^2l7y(h6uURBal1Gl^(q1CVj~HrOYqJq=O%=BSt%+qARaYEo2UC#@v}Tb9 zry|8B90Sw`dIu$-jtiQyCm`YJ@D5BxzM_x^q#`9E+!*e#<*dCwpw6s4ZYN_b0cnyG zH*ou<;7s5+!F@UfXF}2Oq=>gn)?ZFjsyN?Zin*l26vKk=n<$R{GCJtQSSj*6m$y$U za;-sfq}w~O{&TKU+@hd+C5@_O)jA>}iRFj6XDad~h1?^t{u|e8HMwio7+u=Us3T3n zx`BZZz1>rhyhzd_cT24Q`1;T&mFpI|BxzJF!YJWF>8XeI7N;Vq3lk(7e-V&PXRuWf z_VFRVW3jNhYuqY=tVa@mh6q9zm4(o!Qju#Fau<2Op648+Tr-JLWOX5p>TWs~ZPJj_ zx3Oq}x46OTUyyvi(&zBPZb8E6e0hIwt2+!9zEy7?u+Bg>7H;G@cjMhye?yD!%}oQY zQNW!AsP3>G)F!!Z5tAs<93Y*EZcHn3If^o7Jv2Ao>{O(LKQ+i%1{n#X9HU&g(C8Sd zX@F3tmZH@N>i$hYIp7Y9GcY4J&i!;C2x>!T;Kv{Y{;B`3a3kaSa0brn`)%LqzQ6DL z3v&xs(_X{%<@zo4)9VM;>$P`kzp8y3_sm>dJE693tytX(e&CzcFI2Ct9xE4=ZK?dM z@+4y6URpT}M~VMW`Ni_H<;Ti*md^>VVEIAmD+rH&I%4A1i*FZyS^TyNkN-yDe-*xk zQ^i-yErUB9mfr}@;PL!}a@pW6x!>p3tidDoNYf56rh$s{wL?tO7ESqZh)FP-xO0Zj zK{xs)yh+&IECbVMpz^oFfC+EX1t1i=Z{10zL-aW_G7x-ndVF z7eAlh`{y9u{cg^))5%}ZJ|gw|y}W094o2M<@7e66_pHGTe1PghaHG&$BZ&z2;m=ll zI4yzw^=HM>Ku-Dt$^jxc80;6HTEIk$K{kQ%pAm-;6MkJeN(6@(1Aa}>&H_YQRnh`J zl4%Gx;a7rwCu|p@wT?vfidMmM1bt_}?D`yhrjMBKJ)d`WoAQh7R6A#*<;2h&b8Z+} zJ^QRBxMIvNXa@oBA%OGH&ua$(;wZv>>gRM!8v?;S``I7|;R4x~U=TloOV3AqcoLpn zp2NV~FtAgK0WkPplSLTFhQrW~9MO;aw9;!*JwX9ZSPp;~_Fj zq(Z=NS*n$>H1<}6XaO$jnecFUR|K6rT6jje4Ki_g8i1bt%hCYs5uXVGLW53!a_T%Q z$UtV9u}ih<1!=7AOC;@PLv_obXXvhrG`PDw+(a04XQZ*EPLnj#L68Q=l4o3e$-wB}9k}>p7`7Y#sA5nEjN=|WeEfbxdT%Hsh4BVVMIJnSF2Dw$z;gt?@lHs2&QIp} zp#Mc2UX+EPjgNByBcp(t;SsN(?BiG$;Q~0q9it}#<{N3tCC$hQkjC1W{`!*(=0+9F z@y&StlJgXPf|=Sgp1-coU%+$Y`Rn=|dI}GYKaNKs=QTVuD=ZzF00Ggw>7`#Dm}^Ec zymbwJWn6#iSNcZ?(h2F0{L#~^Q8+*gQZ=I#SO(Dm{XGV!fS=+ z5XSS`!eNCu`8TH@d8D|w$qN>jLfZJhGv9!)@$}4IxIWvL3qR~X`%L+9fp!A|^5F&o zIv^%9>X3vnJQdJA`y$X*J4B7-H*TRYD$IIQ?fEqpbAf`!sdXS#MCMIorUHkbAE(w` zQwMWRq|ksN{e8GNW8=hhnxArsO#Le3BgqomfCn)tLb zv1urjnf=!4-cx>@T}Ql?$nmuX%m|1%#&GfD?79P7XMxD46={c?euYGl7;8ps0vadR zna`n#(GkRD#=Q6vkYJHUK*#z*t~(8?k*_FZNUhuP-8IUbTBC7p9Y}}nA{{CY7(dRf zyGT}(MMffR6XME+Zc)&1!5>iXy#^X%0~P ze#a`0Yl~I1Xk4e=XsDn(o9K{-02{t9KhCc^z>WfvUJv;zIvpM47K=nSn@TuEi%Tg<9-y!sGJw1QBrRC_Oy6tx=0=0VCVre>Z#V%u7-3wH zFiLt|wG!X&SlwZm*Indlh1@&rbHVHuAe&=GwDG)x#`$$X6~8XmvIJDPALrLyBr}SG zLuBIpJ(91d&E{l7PT7P|^knkuVFwNRr)9*jKY~p#;pDe{((BrVjE-!9_~5o%Bus){ z?{2a=g4N1qi-gTMyDlXP2XGz+!o<+v_b%+Z+dadf9HCeBZ{f&%AW;PSJdESxbMMQ3_wOE;OGGFi!(MVATs*4MK)n` zr!<>9$!lzS2@fp=f?NyYQYeg~Sg~Owk-#Yru|!VcHF9l)Y!1HV&aQd2hmGIMvYUCw?y;O!n?c1Fo?^IOy&s zP_O?7Cqt8Hp${w+ZnwJtb$ea34g-|8`SI8*{q1)W>QsCL#YCf!cL-ALe+DNtRLW$G zu1XkdI;G0sB8!a+8 zDEV=4;m706frkbz8#roU37n%Z_5T=mhu_nGLH|Vm!oJ@*&e4JTd-a+6f5$<|8|x?6 zm(@$P*K7Y;`)2KPwJT~z;fB!vTisZF4%+E^s~1$qs&k>9?o|G+@@VDG%6XNs@b2)h zls{WO4R?iB!-JB|m8GLgyW^zf#^Q6u|5CiKctLT(o|G*7Md9|snFzTY9F+V{{!jAP z=TFS+I*US7eXM?w0u^vpMtA%oCZ1GM~vD zky*0S$CgUKe-}@+b}@X*_{8K;b7BH}L_;SvM%YIdO|{?#6dIoh7a%-j3x2?!&q0&5 z;0NUM#^m5Q_Ng!*zCdWSmT?5)Io4ls063+VaRhcX0LrLk9D$HaBCg@sn0f(9@CFQ+ zD-yZ@f*jzA1m-J8rh(^F0Uc_^_6v@8Z=ehwvB)8yEB>SFxjOqj9 z)-oQ&aBkDThaaZ}kD_#d(IKz_BjXdQ0mKQ`!gUG`KpOxL+?sBz%9taFtQ3|xr^s@q zu@$nhG|;xKjB-#A2L`aUQ!EW!r{9#csAb?E@gEB6HR6PHzUU}_Fb%K)?oW6RITv)Z zfV$fO6Yw9ham}i1`iU*`%}FL^meK=URUMMGOat5R>w6TKhHm8Fqj+qlu~2u%()iop zFke6!6Gt7vohF-hpP|3;6-)Z?t2>l4g}hu8jVB|d<7lua2raMhWooH6G6srP6M#sZkk^D zhr0cohf(Kv@r~1&kLDRSYJ;zW8^^yP4n*4Zic4KS#r_iN)6Nt-|54tggB5yPx zLuW&LkUQRGzR&7?t=^%Omjo3xW-X>9hBVrkUsoG4SQ>7e*6 zE=ls?XJoM=H%QcUC>C>eyq>J_D~_ng>Cw~V;@DUkT#SC;L5zADoWB0@;Ei1dep``8 z6VX+fg;#OFfp@XT+%ZIl1Ny7+F9zO)b8xOEDi&OU02l{j=zh@8`Hh&9gFD#6R|YtSN!o<8YXvtOt&khMXViRcx z$I^&9D3-=T9H_sF1}d3_I6!{^rnLJDFt~I|O1lFi#C|=-AN*!D9)CLh@v`2Ju~71L zToD$FYY!b)glX*UL0l2j+mZHs;3{qD3ukh2;mpJR7xo|AU+CM23unG0uF|>nx9dNv zKZby^N7m=o-m3kN+M_s2dQ@$G^_}X^s*hD~svcckP9?g{m!2x!RXVk_N3r?)qF4MTc0enN`xHJdyioW~;d8ifW~flkZ_58Y_CA;A z$MSu-*K$A3eI<8Y?#SHy?7P{2$$mBa+3a!IMVTF$UuC|Yxhr#OW)FB!TWZIH|J{44 zMfzRuVH-O7U>h_;6W4VN;_zCF?0Zk-79a(<&s2-#yF-6RCeaVxRmcSSKB`G&M(r9J zM_eGB0&S6e#~>1Uu^cF!Xo#jM$hf$@;UIajh9FU*1pBTdt33}M7910HVnC98cgn7j zy!)0&0!hC21a7iGlqW&GJF)LxAPI{zy*NPCY$d372l`3`Z6&C82PqtIV6;W*-D#td zal5JIJOOqK67)eFxoI^?y}LyUp_uIzlH7YwU|Rvny}Q70LyJWYHRa&6aWJSw?!DO) zxkVupA! zilIroJi)vpmyuq9R~z6PqFbsJ5%1O$>pkTx)yE{fnm(e#fxfk~4s3yndLYW52&-)B{qXoJ2LEuQbM>rh+ zM8$_da$o{P3z2kpoIN3q^Nj)~NcWL&2Ok{Vf)|6ggS5!GyDd51w0n8%#$nPT$+*Xm z5s=iQ08y23zP&M`=|qm3bOE!{g>H8n67iawBa^u7g`A#(l5TfKL&s=GA<4J9$hglG zmwxd0Ws7us7o_!sZiF+^K>ax34#$LY#J;SMal&1Yq1%!-bPNy6i9J8gw_Bu%%ecWp z;g=xcjzw-X7x4AQC|Z7pg~HuH$~{&n$77A*5Ca1p!T0q*HaR+A@`q6iCF$PVC=c3O zBw2SCsr?N+Lyj2CycXIg?cNpH6ek8pmEj zc#n90K)XA28bF)DRX++Er`^#~p;fGs6_T{OGs_HzYzNxueN2apZA`x{S$F4i3Xx5S z)ObySMaD_@Mu-HEGpqnnc#`f;FBx`v$%mLX`JK>rNZQ@0d<8{ZO1eZXG|sz2B@*^M zY`qmS&bvcUFo;e5NMmdgIengPPK*-ta^9H8X@oo*@84#z$m$J=tVUR670Bxo$e~F~ z%7CkZkcZwz9I^DTOT0fq^8H&C_S!ToM_pqU8Mr2q0X>I2Pb5x6(#?Ez3KH*Mub@|@ zK6TA+op#$Y>@f0c05^&iGKvV5G!S%fFB~Jx+zVv(IW`QD%^^ zctMIqx|ziyaOWp*dI`a9hiMnM^HOkh95-;*( z{OnXD9n6AkqI;Z0s8c_YQS^=-77OT^NoYuM0s5B6@&f78iev^tC_g0`N=KWV9M4FF z@+2rmUEXQwpPqQXBV(h`b`&A)vEcAeOBjuy7<3J?N$9Dm&`rwdDG8)rZV@>ggb46Y zPDQpYvWeH9lnSNJSd0ezz#7dD}F( z-?g^A_RHFnwL5F4*7mG^RQ0P*Be3k5)qN`;!&`de-&7u~oP`T#J}y5GE%hIj&o3WT z&X?Ago+*8?bZKd%R4u+-{Kw*-7FQMzDb6ZvE&NmA5ff|b4V)YO^Za%BBl7ce?;(Wj zr{tDqf0vzsSMxO@G7B-!1rC<%SYWkedFj-X!eWJEp;%rg)|bkxSvAq< z-q`TRMIGR$l?Wi1VHnh=6&H0lK)FH`qHeD315t-s-9;ABoNri@K;9LPH_%Wg?%AH0 zH7S+=G_L7zks?Vn$i(8(u_@Clkth+?mQA;v#zOj%8!!wV6qNueYfC+~X&_@MSzuym zsey8B1}$RW$(1E$E4AStYfOyduAs?btSm9cVsaVbMwTl}XIfJ@#@x0zE-QQC)>#}X z(p^(>!|OyDc!5y#Ir0LDMWut}jemkf*Gw!b6_TH@g~F)2rVQS%0uoRiGzz6MR%)%p zqEbaU;PNv<%A(R~A3eIippdepjF6B%JGVsfjsa-?sdH<|l2U(f3W-AfMnPpw*&F(f zg`!aIno`Oiea7Ut(C)l~1{p{!DpgDZWZ-QJh1JBOQb9Sr8l&jSTvJA4le}Nay?AlM z$go65Z*fT(A=yzBkoSHG5HII~(%E@MKxFehg_PxFRQm@a5WSu;*I?s=xH6Ouwi2sJ z2g&0u7MWO0I>;9-5@qLN(k)R_4fPv|1iMOlgo{bHNFfkKddDJRlgmlhW{Bhn7MbK~ z(pjvHVO`Y>8L>8}1d<5MjLUy!dN!b+|Mq#`#cjTDCV-PEKzTEPz)hs!81+XCqA25u9I6#9R}DCZHG+eJt+B-V4-1ir zNi))kNrN0sGf9=Om_)xA0nizbolCyBxZ&W;w>74*}oMqg6MWeH@&oNz`0 zU5FfTWxcOOTIqFzsVXGv1MMLLyY&CQe{KH{ z`=@dB%(?v|{d4-Z^|kuGhZEK}_nq3ee_yrUe5?Me`hTncN&UL|3H5#I#oAUVs-LcX zp>{QnTJKfMS6{9EbM>j}A5|}_9#LHaj$mWuxyoN5AoclBRL?8#z`eB3l>e-JOQ@*+ zx6G;y#rDE~*;xCHdDt@7ORq?puKE-0;bwrzby6|veWnp}YmwW;^(UXEKju zZp9hx12grn9Q`Roo9g70?sN#zd?20aWMgTFL(<8}RS1ELQKI_3wP>niV%~%Ut=00- z5M(cH#Jr)fvg>n%v1mig+VlCkB~xwDvT6}usQr>GKpob$YSwU?kMe*o0qU^!{YuOu z8AIBAO3cGFD8}0N#?m0%YTqMiw$!K{WLkpTbUDjUJBSO?zAGqHI2cA55&>)*!q%?A z0;EXq!N47H17SbMX=zsLsyM**ZailWMey1shYdW3xV>%kuHkHEbI9k257ah#*YJ5m z18_?~+XTr80s1-gbM0GH2cl8PP1`rCyEGbP$`LA}eUnx=1Bhr2ZFBiWxLwl5nfn`p z(szB1Xbo*d%webM`W#Uu+R(j9{aYvS(BKNA=47rugazncX(9AH>l{GB)`se}=W|Gu z+E-6U7b39ueCt)QG~CbAUO9dAH*6FnsS+rK+gEDUAAStkR{M%r8Z=Ms%O%bD{DEV4 zMerOJEnJ{Q8Ar*sA?HxCGUr_Y@LJ>fr76#$Lu+3`y%s)5b5e6?@N`vm@9jF(mY6zF z5QYHz9Gw73XLS%q(zkjm2caMT}MZT!I%vz4KK3+4R_mw)q$ujfq?E0CM2v5^s-$5Fo8f=Z4*`} zST#*QzGZ&NsCtoE_M~IDgW$OJin_)}?1~q}P#nR6G&U<=RK82uBSZ1LxRi z^sUKHjL-2Kqi^l<9B;yDg!W;*zCjp(h_x-TbZ|DOF@n|U5RNlM%P^iq2jY-G#v38R zjsO5lJ(&i8D7HNj0J?N$V*w#|xKtNF(|!;^cL##s=$Jwz0MNX)M}u-EM_CwPMUJFA z$AO5pXlfaeuW63(M5RPcz5@$JRh#^v@CL*MY8zE;*XNvx277%^_&rC5 zjEnYG57uTZ=5tn$h;Ms5e;Y&MzCFabcqfNMM~I6wwu!#rPx6Mez4Rvr9A`);o~f}H z#50Y`%6_<1uMdFZXS{pRu|gIKH!<6S1)13GKLRXC1K@78_F@8}b?02AVwcK8E*8mD zWkxyV41p!vyShJ%{fbTUsi3`+YZK^9aB8qE2OGPsU8qCBb91aQ{Qc~(>;Q0dS)c+* zHB8$4SQ?ij^I~Z@(bk@;I~IP+!Di=J8s9NTEsFULHp=XvN~3+4&KN(1S!n<+zX#F) zTew3TD>bac_WXeZuN=t1WBdL7FCzHWXn(!$6$HQfQr}g5ha&jZTTpaQ8;|WaTnGAi zZB^~W+T!Ym)n8Y?QN6o*T6NFLM-{*Fbmf7{*_Hh&JC)azzgwPy!}fr3wzRJFx21APw@V5 zs0k>PQShIg{)E*C8n|b`;EXn4wJ+W1j>u@UQ%_o@yKfj57vm5IL(rpC44&1gr6Q?F zwa98Jl3H9rqD7k(Kx0iB#m-TOhp?Ob{&2F!_{1BPz@LHrZO3H@D3d+9jTuJ)<1z#X z7+Qp|f-(f@^>+7-ZXc2i$mb6F5}>n!udyAMxyGP!r4peKVTcaFt*6i`i)4%fX=MoE z@{a9Md+0_ zRj%&$p@3A*3J}?(48a{mLm(=Y5#y{$$`GtcxmBs5qbsa~s}Dg^hH;Fe4H-g+lyij| z_=*B1R0tZ_R=~Il0bb6K=)f$@Zj6x;6nL951e?p?-3qu`0VzZ130!M|s3GbQbn`TD zhZkhU)Z)^E(_x3hp$qmoks_$ih)|0}ZGK>(u$qt}C@7Xp7Ksv3ir{8+bTYcthyizS zRX4-vw5ddhBg5;DHY*@?2t9!e_9GUNOUe**??96aMbpITd=%(IKvD5Sta$1WT$|x8 zf}W-bNEJd);06oC%M-d*Xt{B@VFT}OVvbpl11z|gfWnHlt3iNgKY{`&cR~<}CQ3*U z6jDkQ8BItKqKo^Q!&n<)I^@g^CvTe?1h+5DU^VfhYo)>I!!sQT#KG_C>&BVe8 z9P!*>$=d}W`^3)9y$uLQ*qNg^26C)ef_7%d(s1y$Gb@&ct$YU;O7CJBHsM#d1BieZ z;SI|)h_kVszBq6Pfc5F6dsfVmdjhfR>C|Fr*y?twk`|o)CJtmyXL{SN_Wn!xDU!;j tChX!Nsw&}kI;E87*w%LtvVzSSEdaCu^7aT>;Q|iQ2%5;p5gM@J{{gBAw)Fr2 delta 24469 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 XIFE!9>Q@t1bn%RXRn;?&e#HL(0{?nY diff --git a/test_qqpush.py b/test_qqpush.py new file mode 100644 index 0000000..67a7748 --- /dev/null +++ b/test_qqpush.py @@ -0,0 +1,49 @@ +"""Danding_QqPush 插件测试脚本""" +import requests +import json + + +def test_qqpush_api(): + """测试 QQ 推送 API""" + + # 配置 + base_url = "http://localhost:8080" # NoneBot 默认端口 + token = "danding-8HkL9xQ2" # 默认 Token + + # 构造请求数据 + data = { + "group_id": 574727392, # 替换为实际群号 + "qq": 1424473282, # 替换为实际 QQ 号 + "text": "系统告警#数据库连接失败#请立即处理#测试消息" + } + + # 发送请求 + url = f"{base_url}/danding/qqpush/{token}" + + print(f"正在测试 API: {url}") + print(f"请求数据: {json.dumps(data, ensure_ascii=False, indent=2)}") + + try: + response = requests.post( + url, + json=data, + headers={"Content-Type": "application/json"}, + timeout=10 + ) + + print(f"\n响应状态码: {response.status_code}") + print(f"响应内容: {json.dumps(response.json(), ensure_ascii=False, indent=2)}") + + if response.status_code == 200: + print("\n✅ 测试成功!") + else: + print(f"\n❌ 测试失败,状态码: {response.status_code}") + + except requests.exceptions.ConnectionError: + print("\n❌ 连接失败,请确认 NoneBot 是否已启动") + except Exception as e: + print(f"\n❌ 测试异常: {str(e)}") + + +if __name__ == "__main__": + test_qqpush_api()