chore: 清理测试脚本并更新插件文档

- 删除过时的测试脚本,包括测试配置、路由、API、积分、签到等文件
- 更新 PLUGINS.md 文档,重新组织插件结构,提供更清晰的功能说明和权限要求
- 改进文档格式,增加表格和详细说明,便于用户理解各插件功能
This commit is contained in:
2026-04-10 21:13:46 +08:00
parent b86cc009e9
commit f1f7d4cd1e
7 changed files with 127 additions and 516 deletions

View File

@@ -2,174 +2,145 @@
## 项目概述 ## 项目概述
蛋定助手是一个基于NoneBot2框架开发的QQ机器人提供多种功能插件包括AI聊天、管理API、自动撤回消息等。该机器人主要面向特定用户群体提供游戏辅助和社群管理功能 蛋定助手是一个基于 NoneBot2 框架开发的 QQ 机器人,提供多种功能插件,包括 AI 聊天、游戏辅助、积分系统、社群管理
## 插件总览 ## 插件总览
| 插件名称 | 描述 | 权限要求 | | 插件名称 | 描述 | 触发方式 | 权限要求 |
|---------|------|---------| |---------|------|---------|---------|
| chatai | AI聊天功能,对接DeepSeek | 所有用户 | | [chatai](#1-chatai---ai-聊天) | AI 聊天DeepSeek),支持图文回复 | `*` 开头消息 | 所有用户 |
| auto_recall | 消息自动撤回 | 系统自动执行 | | [auto_recall](#2-auto_recall---自动撤回) | 自动撤回机器人发送的消息 | 自动执行 | 系统自动 |
| damo_balance | 大漠账户余额查询 | 特定用户 | | [auto_friend_accept](#3-auto_friend_accept---自动接受好友) | 自动同意好友请求并发送欢迎语 | 自动执行 | 系统自动 |
| danding_api | 蛋定助手管理API | 超级用户 | | [welcome_plugin](#4-welcome_plugin---入群欢迎) | 新成员入群欢迎并发送帮助菜单 | 自动执行 | 特定群 (621016172) |
| danding_help | 帮助信息 | 特定群用户 | | [danding_qqpush](#5-danding_qqpush---消息推送) | 通过 HTTP API 向指定群推送图文通知 | HTTP POST | 接口 Token 验证 |
| command_list | 命令列表管理 | 系统使用 | | [danding_points](#6-danding_points---积分系统核心) | 积分系统数据库与 API 核心 | API 调用 | 系统内部 |
| [danding_points_query](#7-danding_points_query---积分查询) | 查询余额、排行及交易记录 | 命令触发 | 所有用户 |
| [group_horse_racing](#8-group_horse_racing---群赛马游戏) | 多人赛马游戏,支持下注与积分奖惩 | 命令触发 | 允许的群聊 |
| [onmyoji_gacha](#9-onmyoji_gacha---阴阳师抽卡模拟) | 阴阳师主题抽卡,包含成就与卡密奖励 | 命令触发 | 允许的群聊/用户 |
| [damo_balance](#10-damo_balance---大漠账户查询) | 查询大漠平台账户余额 | 命令触发 | 特定用户 |
| [danding_api](#11-danding_api---管理-api) | 管理员操作:卡密管理、加时、在线查询 | 命令触发 | 超级用户 |
| [danding_help](#12-danding_help---帮助菜单) | 提供教程、下载及功能指引 | 命令触发 | 特定群 (621016172) |
| [command_list](#13-command_list---指令列表) | 获取系统支持的所有指令列表 | 命令触发 | 所有用户 |
---
## 详细插件文档 ## 详细插件文档
### 1. chatai - AI聊天插件 ### 1. chatai - AI 聊天
#### 功能描述 基于 DeepSeek AI 的聊天功能,支持将回复转换为图片形式,并在一定时间后自动撤回。
基于DeepSeek AI的聊天功能支持将AI回复转换为图片形式并在一定时间后自动撤回。
#### 使用方法 - **使用方法**: 发送以 `*` 开头的消息。
- 发送以 `*` 开头的消息触发AI回复 - **配置项**: `DEEPSEEK_TOKEN` (必填)。
- AI回复会自动转为图片显示 - **特性**: AI 回复会自动转为图片显示,默认 120 秒后撤回。
- 回复会在120秒后自动撤回
#### 配置项 ### 2. auto_recall - 自动撤回
```env
DEEPSEEK_TOKEN=你的DeepSeek API密钥
```
#### 技术实现 监控机器人发出的消息,并在指定时间后自动撤回,保持聊天环境整洁。
- 使用OpenAI客户端连接DeepSeek API
- 使用Pyppeteer将Markdown转为图片
- 内置Chrome浏览器实例管理
#### 示例 - **配置项**:
用户: *你好,请介绍一下自己 - `RECALL_DELAY`: 普通消息撤回延迟(默认 110s
AI: [图片形式回复] 👋 你好呀我是蛋定助手一个活泼可爱的AI助手😊 很高兴认识你!有什么我能帮到你的吗?✨ - `QQPUSH_RECALL_DELAY`: 推送消息撤回延迟(默认 3600s
### 3. auto_friend_accept - 自动接受好友
自动处理好友请求,提升用户接入效率。
- **配置项**:
- `auto_accept_enabled`: 是否开启自动接受。
- `auto_reply_message`: 接受后的欢迎语。
### 4. welcome_plugin - 入群欢迎
针对特定群聊的新成员欢迎功能。
- **触发场景**: 新成员加入群 `621016172`
- **功能**: 随机发送欢迎语,并附带帮助菜单图片。
### 5. danding_qqpush - 消息推送
提供外部系统向 QQ 推送通知的 HTTP 接口。
- **接口**: `POST /danding/qqpush/{token}`
- **功能**: 自动将长文本转换为图片,支持 `@用户` 和换行符 `#`
- **配置**: `DANDING_QQPUSH_TOKEN`
### 6. danding_points - 积分系统核心
为其他插件提供积分存储与结算的基础设施。
- **功能**: 数据库管理SQLite、余额增减、排行榜计算、交易日志记录。
- **数据库路径**: `data/danding_points/points.db`
### 7. danding_points_query - 积分查询
用户通过命令与积分系统交互。
- **主要命令**:
- `我的积分`: 查看个人余额。
- `积分查询 @用户`: 查看他人余额。
- `积分排行`: 查看前 10 名。
- `积分历史查询`: 查看最近 5 条变动记录。
- `积分帮助`: 获取指令指引。
### 8. group_horse_racing - 群赛马游戏
集成积分系统的多人互动游戏。
- **主要命令**:
- `/赛马报名 [马名]`: 参加比赛。
- `/赛马下注 <序号> <金额>`: 对马匹下注。
- `/赛马开赛`: 开始比赛(至少 2 人)。
- **积分逻辑**: 参赛奖励 50 分,冠军奖励 200 分,支持下注赔率结算。
### 9. onmyoji_gacha - 阴阳师抽卡模拟
高度还原的抽卡模拟,包含成就系统。
- **主要命令**:
- `抽卡`: 执行单抽。
- `三连抽`: 执行三连抽。
- `我的抽卡`: 查看个人统计。
- `查询成就`: 查看已解锁成就及进度(非酋、勤勤恳恳系列)。
- `抽卡介绍`: 查看详细机制与奖励说明。
- **特性**: 抽中 SSR/SP 可获得“蛋定助手”卡密奖励(需联系管理员领取)。包含每日抽卡签到积分奖励。
### 10. damo_balance - 大漠账户查询
查询大漠平台账户余额。
- **命令**: `大漠余额``余额查询`
- **限制**: 仅特定用户可用,需输入验证码。
### 11. danding_api - 管理 API
供超级用户使用的后台管理功能。
- **主要命令**:
- `在线人数`: 查询当前活跃用户。
- `生成卡密 <类型>`: 生成天/周/月卡。
- `用户加时 <用户名> <类型>`: 直接为特定用户增加时长。
### 12. danding_help - 帮助菜单
系统的官方指引手册。
- **主要命令**: `帮助``下载``正式版如何运行` 等。
- **限制**: 仅在特定群 `621016172` 可用。
### 13. command_list - 指令列表
快速查阅所有可用指令。
- **命令**: `指令列表`
--- ---
### 2. auto_recall - 自动撤回插件 ## 常见问题 (FAQ)
#### 功能描述 - **Q: 为什么某些命令没反应?**
监控所有发出的消息,并在指定时间后自动撤回,保持聊天环境整洁。 A: 部分插件(如 `danding_help`)限制了特定群聊使用;管理指令需要配置 `SUPERUSERS`
- **Q: 积分有什么用?**
#### 使用方法 A: 目前主要用于赛马下注及展示排名。
- 无需手动调用,插件会自动监控并撤回消息 - **Q: 抽卡奖励如何领取?**
A: 抽中 SSR/SP 或解锁特定成就后,请截屏联系管理员。
#### 配置项
```env
RECALL_DELAY=110 # 撤回延迟时间,单位为秒
```
#### 技术实现
- 使用NoneBot的API拦截功能
- 异步定时任务管理
- 错误处理与日志记录
---
### 3. damo_balance - 大漠账户余额查询
#### 功能描述
查询大漠平台账户余额,需要验证码验证。
#### 使用方法
- 命令:`大漠余额``余额查询`
- 需要验证码验证
#### 权限要求
- 仅特定用户(ID:1424473282)可使用
---
### 4. danding_api - 蛋定助手管理API
#### 功能描述
提供管理员操作接口,包括在线人数查询、卡密管理和用户时长管理功能。
#### 使用方法
主要命令:
- `在线人数`:查询当前在线用户数
- `添加卡密 [类型] [卡密]`:添加指定类型的卡密
- `生成卡密 [类型]`:生成新卡密
- `用户加时 [用户名] [类型]`:为指定用户增加使用时长
#### 卡密类型
- 天卡/day/Day/DAY/天
- 周卡/week/Week/WEEK/周
- 月卡/month/Month/MONTH/月
#### 权限要求
- 仅超级用户可使用
#### 配置项
```env
SUPERUSERS=["1424473282"] # 超级用户ID列表
```
#### 示例
```
在线人数
> 当前在线用户数: 42
添加卡密 天卡 ABCD1234
> 添加卡密成功:天卡 ABCD1234
生成卡密 周卡
> 生成卡密成功:周卡 XYZ789ABC
用户加时 test_user 月卡
> 用户加时成功test_user 增加了30天
```
---
### 5. danding_help - 帮助信息
#### 功能描述
提供各种帮助信息和指南,支持图片形式的教程和指引。
#### 使用方法
主要命令:
- `帮助`:显示帮助菜单
- `下载`:显示下载信息
- `公益版`/`正式版`:显示版本信息
- `正式版御魂双开`:显示双开教程
- `正式版如何运行`:显示运行教程
- `正式版内测计划`:显示内测信息
#### 权限要求
- 仅在特定群(621016172)可用
#### 技术实现
- 使用图片回复提供直观的教程
- 文本与图片混合响应
---
### 6. command_list - 命令列表管理
#### 功能描述
管理系统命令列表,提供命令过滤和权限控制。
#### 使用方法
- 系统内部使用,不直接暴露给用户
## 常见问题
### Q1: 如何启动蛋定助手?
A1: 使用`nb run`命令启动,确保已安装所有依赖。
### Q2: 机器人回复后自动撤回的时间可以修改吗?
A2: 可以,在`.env`文件中修改`RECALL_DELAY`的值(单位为秒)。
### Q3: 如何成为超级用户?
A3: 在`.env`文件的`SUPERUSERS`列表中添加您的QQ号。
### Q4: AI聊天功能如何配置
A4: 需要在`.env`文件中设置`DEEPSEEK_TOKEN`填入您的DeepSeek API密钥。
### Q5: 为什么帮助命令在某些群不可用?
A5: 帮助命令仅在特定群(621016172)内可用,这是一种权限控制机制。
## 技术支持
如有问题,可以:
1. 在群内@机器人并提问
2. 访问帮助文档https://www.danding.icu
3. 联系超级用户获取支持

View File

@@ -1,73 +0,0 @@
#!/usr/bin/env python3
"""
测试 onmyoji_gacha Web API
"""
import requests
import json
# API 配置
BASE_URL = "http://localhost:8080"
API_BASE = f"{BASE_URL}/onmyoji_gacha/api"
ADMIN_TOKEN = "onmyoji_admin_token_2024"
# 请求头
headers = {
"Authorization": f"Bearer {ADMIN_TOKEN}",
"Content-Type": "application/json"
}
def test_api():
print("🧪 测试 onmyoji_gacha Web API")
print("=" * 50)
# 测试每日统计
print("\n📊 测试每日统计 API...")
try:
response = requests.get(f"{API_BASE}/stats/daily", headers=headers)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
else:
print(f"错误: {response.text}")
except Exception as e:
print(f"请求失败: {e}")
# 测试排行榜
print("\n🏆 测试排行榜 API...")
try:
response = requests.get(f"{API_BASE}/stats/rank", headers=headers)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
else:
print(f"错误: {response.text}")
except Exception as e:
print(f"请求失败: {e}")
# 测试用户统计使用示例用户ID
print("\n👤 测试用户统计 API...")
try:
test_user_id = "123456789" # 示例用户ID
response = requests.get(f"{API_BASE}/stats/user/{test_user_id}", headers=headers)
print(f"状态码: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"响应: {json.dumps(data, indent=2, ensure_ascii=False)}")
else:
print(f"错误: {response.text}")
except Exception as e:
print(f"请求失败: {e}")
# 测试无令牌访问
print("\n🔒 测试无令牌访问...")
try:
response = requests.get(f"{API_BASE}/stats/daily")
print(f"状态码: {response.status_code}")
print(f"响应: {response.text}")
except Exception as e:
print(f"请求失败: {e}")
if __name__ == "__main__":
test_api()

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env python3
import os
import sys
# Print environment variables
print("=== Environment Variables ===")
for key in os.environ:
if "GROUP_HORSE_RACING" in key:
print(f"{key}={os.environ[key]}")
print("\n=== Loading Config ===")
from danding_bot.plugins.group_horse_racing.config import Config
config = Config()
print(f"TEST_MODE: {config.TEST_MODE} (type: {type(config.TEST_MODE).__name__})")
print(f"TESTERS: {config.TESTERS} (type: {type(config.TESTERS).__name__})")
print(f"TEST_GROUPS: {config.TEST_GROUPS} (type: {type(config.TEST_GROUPS).__name__})")
print(f"ALLOWED_GROUPS: {config.ALLOWED_GROUPS} (type: {type(config.ALLOWED_GROUPS).__name__})")
print("\n=== Testing Permission Check ===")
test_user_id = 1424473282
print(f"Test user_id: {test_user_id}")
print(f"Is in TESTERS: {test_user_id in config.TESTERS}")
print(f"TEST_MODE enabled: {config.TEST_MODE}")
print(f"Should have access: {config.TEST_MODE and test_user_id in config.TESTERS}")

View File

@@ -1,71 +0,0 @@
#!/usr/bin/env python3
"""Manual test script for danding_points plugin."""
import asyncio
import sys
from pathlib import Path
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from danding_bot.plugins.danding_points import points_api
async def test_basic_operations():
"""Test basic points operations."""
print("Testing basic operations...")
# Test 1: Get balance for non-existent user
balance = await points_api.get_balance("test_user_1")
assert balance == 0, f"Expected 0, got {balance}"
print("✓ Non-existent user returns 0 balance")
# Test 2: Add points (auto-create user)
success, new_balance = await points_api.add_points(
"test_user_1", 100, "test_source", "test reason"
)
assert success and new_balance == 100, f"Add failed: {success}, {new_balance}"
print("✓ Add points works and auto-creates user")
# Test 3: Get balance after add
balance = await points_api.get_balance("test_user_1")
assert balance == 100, f"Expected 100, got {balance}"
print("✓ Balance updated correctly")
# Test 4: Spend points
success, new_balance = await points_api.spend_points(
"test_user_1", 30, "test_source", "spend reason"
)
assert success and new_balance == 70, f"Spend failed: {success}, {new_balance}"
print("✓ Spend points works")
# Test 5: Spend more than balance (should fail)
success, new_balance = await points_api.spend_points(
"test_user_1", 100, "test_source", "should fail"
)
assert not success, "Should fail when spending more than balance"
print("✓ Spend fails when insufficient balance")
# Test 6: Set points
success, new_balance = await points_api.set_points(
"test_user_1", 50, "test_source", "set reason"
)
assert success and new_balance == 50, f"Set failed: {success}, {new_balance}"
print("✓ Set points works")
# Test 7: Get transactions
transactions = await points_api.get_transactions("test_user_1", limit=10)
assert len(transactions) > 0, "Should have transactions"
print(f"✓ Get transactions works ({len(transactions)} transactions)")
# Test 8: Get ranking
ranking = await points_api.get_ranking(limit=10)
assert len(ranking) > 0, "Should have ranking entries"
assert "rank" in ranking[0], "Ranking should have rank field"
print(f"✓ Get ranking works ({len(ranking)} entries)")
print("\n✅ All tests passed!")
if __name__ == "__main__":
asyncio.run(test_basic_operations())

View File

@@ -1,95 +0,0 @@
import tempfile
import unittest
import importlib.util
import sys
import types
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent
def ensure_package(package_name: str, package_path: Path) -> None:
if package_name in sys.modules:
return
package = types.ModuleType(package_name)
package.__path__ = [str(package_path)]
sys.modules[package_name] = package
def load_module(module_name: str, relative_path: str):
module_path = PROJECT_ROOT / relative_path
spec = importlib.util.spec_from_file_location(module_name, module_path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
ensure_package("danding_bot", PROJECT_ROOT / "danding_bot")
ensure_package("danding_bot.plugins", PROJECT_ROOT / "danding_bot/plugins")
ensure_package(
"danding_bot.plugins.onmyoji_gacha",
PROJECT_ROOT / "danding_bot/plugins/onmyoji_gacha",
)
load_module(
"danding_bot.plugins.onmyoji_gacha.config",
"danding_bot/plugins/onmyoji_gacha/config.py",
)
data_manager_module = load_module(
"danding_bot.plugins.onmyoji_gacha.data_manager",
"danding_bot/plugins/onmyoji_gacha/data_manager.py",
)
utils = load_module(
"danding_bot.plugins.onmyoji_gacha.utils",
"danding_bot/plugins/onmyoji_gacha/utils.py",
)
class DataManagerSignInTests(unittest.TestCase):
def setUp(self) -> None:
self.temp_dir = tempfile.TemporaryDirectory()
base_path = Path(self.temp_dir.name)
self.original_db_file = data_manager_module.config.DB_FILE
self.original_img_dir = data_manager_module.config.SHIKIGAMI_IMG_DIR
data_manager_module.config.DB_FILE = str(base_path / "gacha.db")
data_manager_module.config.SHIKIGAMI_IMG_DIR = str(base_path / "images")
Path(data_manager_module.config.SHIKIGAMI_IMG_DIR).mkdir(parents=True, exist_ok=True)
self.manager = data_manager_module.DataManager()
def tearDown(self) -> None:
data_manager_module.config.DB_FILE = self.original_db_file
data_manager_module.config.SHIKIGAMI_IMG_DIR = self.original_img_dir
self.temp_dir.cleanup()
def test_record_sign_in_is_idempotent_for_same_day(self) -> None:
user_id = "10001"
self.assertFalse(self.manager.has_signed_in_today(user_id))
self.assertTrue(self.manager.record_sign_in(user_id, 88))
self.assertTrue(self.manager.has_signed_in_today(user_id))
self.assertFalse(self.manager.record_sign_in(user_id, 99))
class UtilsSignInTests(unittest.TestCase):
def test_get_luck_description_uses_document_ranges(self) -> None:
self.assertEqual(utils.get_luck_description(1), ("非酋", "😭"))
self.assertEqual(utils.get_luck_description(11), ("一般", "😐"))
self.assertEqual(utils.get_luck_description(31), ("小欧", "😊"))
self.assertEqual(utils.get_luck_description(61), ("大欧", "🎉"))
self.assertEqual(utils.get_luck_description(91), ("欧皇", "👑"))
def test_format_sign_in_message_matches_required_format(self) -> None:
message = utils.format_sign_in_message("10001", "小夏", 87, 1247)
expected = (
"@小夏 📅 每日签到成功!\n"
"🎁 获得积分87\n"
"🎉 今日运气:大欧\n"
"💰 当前积分1247"
)
self.assertEqual(message, expected)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,49 +0,0 @@
"""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()

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env python3
"""
测试 onmyoji_gacha Web API 路由是否正确注册
"""
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'danding_bot'))
# 设置环境变量
os.environ.setdefault("ENVIRONMENT", "dev")
try:
# 模拟 NoneBot 环境
from nonebot import get_driver
from nonebot import init
# 初始化 NoneBot
init()
# 尝试获取驱动
driver = get_driver()
print(f"✅ 成功获取驱动: {type(driver)}")
# 检查是否有 server_app 属性
if hasattr(driver, 'server_app'):
print(f"✅ 找到 server_app: {type(driver.server_app)}")
# 检查路由是否已注册
app = driver.server_app
routes = app.routes
print(f"✅ 当前注册的路由数量: {len(routes)}")
# 查找我们的路由
onmyoji_routes = [route for route in routes if hasattr(route, 'path') and '/onmyoji_gacha' in str(route.path)]
print(f"✅ 找到 onmyoji_gacha 路由: {len(onmyoji_routes)}")
for route in onmyoji_routes:
print(f" - {route.path} ({route.methods if hasattr(route, 'methods') else 'N/A'})")
else:
print("❌ 驱动没有 server_app 属性")
except Exception as e:
print(f"❌ 测试失败: {e}")
import traceback
traceback.print_exc()