feat:初版

This commit is contained in:
2025-12-03 15:48:44 +08:00
commit b4df26f61d
199 changed files with 23434 additions and 0 deletions

465
docs/插件开发.md Normal file
View File

@@ -0,0 +1,465 @@
# WechatHookBot 插件开发指南
## 插件系统
WechatHookBot 的插件系统完全兼容 XYBotV2所有 XYBot 插件可以直接使用。
## 插件结构
```
plugins/
PluginName/
__init__.py # 可选,可为空
main.py # 必需,包含插件类
config.toml # 可选,插件配置
README.md # 可选,插件说明
```
## 基本插件模板
```python
from utils.plugin_base import PluginBase
from utils.decorators import *
from WechatHook import WechatHookClient
class MyPlugin(PluginBase):
description = "插件描述"
author = "作者名"
version = "1.0.0"
def __init__(self):
super().__init__()
# 同步初始化
self.data = {}
async def async_init(self):
# 异步初始化
pass
@on_text_message(priority=50)
async def handle_text(self, client: WechatHookClient, message: dict):
"""处理文本消息"""
content = message.get("Content", "")
from_wxid = message.get("FromWxid", "")
if content == "你好":
await client.send_text(from_wxid, "你好!我是机器人")
return False # 阻止后续处理
return True # 继续执行后续处理器
```
## 事件装饰器
### 消息事件
| 装饰器 | 触发条件 | 参数 |
|--------|----------|------|
| `@on_text_message` | 文本消息 | priority (0-99) |
| `@on_image_message` | 图片消息 | priority |
| `@on_voice_message` | 语音消息 | priority |
| `@on_video_message` | 视频消息 | priority |
| `@on_file_message` | 文件消息 | priority |
| `@on_at_message` | @消息 | priority |
| `@on_quote_message` | 引用消息 | priority |
| `@on_card_message` | 名片消息 | priority |
| `@on_location_message` | 位置消息 | priority |
| `@on_link_message` | 链接消息 | priority |
| `@on_miniapp_message` | 小程序消息 | priority |
| `@on_emoji_message` | 表情消息 | priority |
| `@on_revoke_message` | 撤回消息 | priority |
| `@on_friend_request` | 好友请求 | priority |
### 优先级机制
- 优先级范围0-99
- 数值越大,优先级越高
- 默认优先级50
- 按优先级从高到低执行
```python
@on_text_message(priority=80) # 高优先级
async def handle_important(self, client, message):
pass
@on_text_message(priority=20) # 低优先级
async def handle_normal(self, client, message):
pass
```
### 阻塞机制
- 返回 `False`:阻止后续处理器执行
- 返回 `True` 或不返回:继续执行
```python
@on_text_message
async def handle_sensitive(self, client, message):
if "敏感词" in message["Content"]:
await client.send_text(message["FromWxid"], "检测到敏感内容")
return False # 阻止后续执行
return True # 继续执行
```
## 定时任务
### interval - 间隔触发
```python
@schedule('interval', seconds=30)
async def periodic_task(self, client: WechatHookClient):
"""每30秒执行一次"""
pass
@schedule('interval', minutes=5)
async def five_minutes_task(self, client: WechatHookClient):
"""每5分钟执行一次"""
pass
```
### cron - 定时触发
```python
@schedule('cron', hour=8, minute=30)
async def morning_task(self, client: WechatHookClient):
"""每天早上8:30执行"""
await client.send_text("wxid_xxx", "早安!")
@schedule('cron', day_of_week='mon-fri', hour='9-17')
async def work_time_task(self, client: WechatHookClient):
"""工作日9-17点每小时执行"""
pass
```
### date - 指定时间触发
```python
@schedule('date', run_date='2024-12-31 23:59:59')
async def new_year_task(self, client: WechatHookClient):
"""在指定时间执行一次"""
pass
```
## 消息对象结构
### 文本消息
```python
{
"FromWxid": "wxid_xxx", # 发送者 wxid
"ToWxid": "wxid_yyy", # 接收者 wxid
"Content": "消息内容", # 文本内容
"MsgType": 1, # 消息类型
"IsGroup": False, # 是否群聊
"SenderWxid": "wxid_xxx", # 实际发送者(群聊时不同)
"CreateTime": 1234567890, # 创建时间戳
}
```
### 群聊消息
```python
{
"FromWxid": "123@chatroom", # 群聊 ID
"ToWxid": "wxid_bot", # 机器人 wxid
"Content": "消息内容",
"IsGroup": True, # 是否群聊
"SenderWxid": "wxid_xxx", # 实际发送者
"Ats": ["wxid_bot"], # 被@的用户列表
}
```
### 图片消息
```python
{
"FromWxid": "wxid_xxx",
"Content": "base64_image_data", # 图片 base64 数据
"MsgType": 3,
"ImagePath": "/path/to/image", # 图片路径(如果有)
}
```
## WechatHookClient API
### 发送消息
```python
# 发送文本
await client.send_text(wxid, "消息内容")
# 发送图片
await client.send_image(wxid, "/path/to/image.jpg")
# 发送文件
await client.send_file(wxid, "/path/to/file.pdf")
# 发送视频
await client.send_video(wxid, "/path/to/video.mp4")
# 发送名片
await client.send_card(wxid, card_wxid, card_nickname)
# 发送位置
await client.send_location(wxid, lat, lng, title, address)
# 发送链接
await client.send_link(wxid, title, desc, url, thumb_url)
# 发送小程序
await client.send_miniapp(wxid, appid, title, page_path, thumb_url)
# 群聊@消息
await client.send_at_message(chatroom_id, content, at_list)
# 撤回消息
await client.revoke_message(msg_id)
```
### 好友管理
```python
# 获取好友列表
friends = await client.get_friend_list()
# 获取好友信息
info = await client.get_friend_info(wxid)
# 搜索用户
result = await client.search_user(keyword)
# 添加好友
await client.add_friend(wxid, verify_msg)
# 同意好友请求
await client.accept_friend(v3, v4, scene)
# 删除好友
await client.delete_friend(wxid)
# 修改备注
await client.set_friend_remark(wxid, remark)
# 检测好友状态
status = await client.check_friend_status(wxid)
```
### 群聊管理
```python
# 获取群聊列表
chatrooms = await client.get_chatroom_list()
# 获取群成员
members = await client.get_chatroom_members(chatroom_id)
# 获取群信息
info = await client.get_chatroom_info(chatroom_id)
# 创建群聊
chatroom_id = await client.create_chatroom(member_list)
# 邀请进群
await client.invite_to_chatroom(chatroom_id, wxid_list)
# 踢出群成员
await client.remove_chatroom_member(chatroom_id, wxid_list)
# 退出群聊
await client.quit_chatroom(chatroom_id)
# 修改群名称
await client.set_chatroom_name(chatroom_id, name)
# 修改群公告
await client.set_chatroom_announcement(chatroom_id, announcement)
# 修改我的群昵称
await client.set_my_chatroom_nickname(chatroom_id, nickname)
```
## 数据库使用
### KeyvalDB - 键值存储
```python
from database.keyvalDB import KeyvalDB
keyval_db = KeyvalDB()
# 设置值
await keyval_db.set("key", "value")
# 获取值
value = await keyval_db.get("key")
# 删除值
await keyval_db.delete("key")
```
### XYBotDB - 业务数据
```python
from database.XYBotDB import XYBotDB
db = XYBotDB()
# 使用 SQLAlchemy 操作数据库
# 参考 XYBot 的数据库使用方式
```
## 配置文件
### 插件配置 (config.toml)
```toml
[basic]
enable = true
[settings]
api_key = "your_api_key"
timeout = 30
```
### 读取配置
```python
import tomllib
import os
def __init__(self):
super().__init__()
config_path = os.path.join(os.path.dirname(__file__), "config.toml")
with open(config_path, "rb") as f:
config = tomllib.load(f)
self.enable = config.get("basic", {}).get("enable", False)
self.api_key = config.get("settings", {}).get("api_key", "")
```
## 日志系统
使用 loguru 记录日志:
```python
from loguru import logger
# 不同级别的日志
logger.debug("调试信息")
logger.info("普通信息")
logger.success("成功信息")
logger.warning("警告信息")
logger.error("错误信息")
# 带参数的日志
logger.info("收到消息: {}", message)
```
## 异步编程
所有插件函数必须是异步函数:
```python
# ✅ 正确
@on_text_message
async def handle_text(self, client, message):
await client.send_text(...)
# ❌ 错误
@on_text_message
def handle_text(self, client, message): # 缺少 async
client.send_text(...) # 缺少 await
```
### 使用阻塞函数
如需使用阻塞函数,使用 `asyncio.run_in_executor`
```python
import asyncio
@on_text_message
async def handle_text(self, client, message):
# 在线程池中运行阻塞函数
result = await asyncio.to_thread(blocking_function, arg1, arg2)
```
## 完整示例
```python
from utils.plugin_base import PluginBase
from utils.decorators import *
from WechatHook import WechatHookClient
from loguru import logger
import tomllib
import os
class ExamplePlugin(PluginBase):
description = "示例插件"
author = "Your Name"
version = "1.0.0"
def __init__(self):
super().__init__()
# 读取配置
config_path = os.path.join(os.path.dirname(__file__), "config.toml")
with open(config_path, "rb") as f:
config = tomllib.load(f)
self.enable = config.get("basic", {}).get("enable", False)
self.keyword = config.get("settings", {}).get("keyword", "你好")
async def async_init(self):
logger.info("ExamplePlugin 初始化完成")
@on_text_message(priority=50)
async def handle_text(self, client: WechatHookClient, message: dict):
if not self.enable:
return
content = message.get("Content", "")
from_wxid = message.get("FromWxid", "")
if self.keyword in content:
await client.send_text(from_wxid, f"你说了关键词:{self.keyword}")
logger.info(f"触发关键词回复: {from_wxid}")
@on_at_message(priority=60)
async def handle_at(self, client: WechatHookClient, message: dict):
if not self.enable:
return
content = message.get("Content", "")
from_wxid = message.get("FromWxid", "")
await client.send_text(from_wxid, "你@了我!")
@schedule('cron', hour=9, minute=0)
async def morning_greeting(self, client: WechatHookClient):
if not self.enable:
return
# 每天早上9点发送问候
await client.send_text("wxid_xxx", "早安!新的一天开始了")
```
## 插件管理
### 禁用插件
`main_config.toml` 中添加:
```toml
[Bot]
disabled-plugins = ["ExamplePlugin", "AnotherPlugin"]
```
### 删除插件
直接删除 `plugins/` 下对应的文件夹
### 热重载
通过 WebUI 或 ManagePlugin 插件实现热重载