feat:初版
This commit is contained in:
84
docs/MemoryBank/01-项目概述.md
Normal file
84
docs/MemoryBank/01-项目概述.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# WechatHookBot 项目概述
|
||||
|
||||
## 项目信息
|
||||
|
||||
- **项目名称**: WechatHookBot
|
||||
- **版本**: v1.0.0
|
||||
- **创建时间**: 2025-11-12
|
||||
- **项目类型**: 微信机器人框架
|
||||
- **技术栈**: Python 3.11 (32位), asyncio, ctypes
|
||||
|
||||
## 项目简介
|
||||
|
||||
基于个微大客户版 Hook API 的微信机器人框架,通过 DLL 注入方式实现微信消息的接收和发送。
|
||||
|
||||
## 核心特性
|
||||
|
||||
1. **DLL Hook 技术**
|
||||
- 使用 Loader.dll 和 Helper.dll 注入微信进程
|
||||
- 通过 Socket 回调接收消息
|
||||
- 支持微信版本: 2.84.18.17
|
||||
|
||||
2. **插件系统**
|
||||
- 基于事件驱动的插件架构
|
||||
- 支持热重载(无需重启)
|
||||
- 装饰器风格的事件处理
|
||||
- 插件启用/禁用管理
|
||||
|
||||
3. **消息处理**
|
||||
- 支持文本、图片、视频、文件等多种消息类型
|
||||
- 群聊和私聊消息处理
|
||||
- 消息过滤(白名单/黑名单)
|
||||
- @消息识别
|
||||
|
||||
4. **AI 集成**
|
||||
- 支持自定义 AI API
|
||||
- 可切换人设(通过 txt 文件)
|
||||
- 多种触发模式(全部/提及/@/关键词)
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
WechatHookBot/
|
||||
├── bot.py # 主入口
|
||||
├── main_config.toml # 主配置文件
|
||||
├── requirements.txt # 依赖列表
|
||||
├── libs/ # DLL 文件
|
||||
│ ├── Loader.dll
|
||||
│ └── Helper.dll
|
||||
├── WechatHook/ # Hook API 封装
|
||||
│ ├── loader.py # DLL 加载器
|
||||
│ ├── client.py # API 客户端
|
||||
│ ├── callbacks.py # 回调处理
|
||||
│ └── message_types.py # 消息类型定义
|
||||
├── utils/ # 工具模块
|
||||
│ ├── hookbot.py # 机器人核心
|
||||
│ ├── plugin_manager.py # 插件管理器
|
||||
│ ├── event_manager.py # 事件管理器
|
||||
│ └── decorators.py # 装饰器
|
||||
├── plugins/ # 插件目录
|
||||
│ ├── AIChat/ # AI 聊天插件
|
||||
│ ├── ManagePlugin/ # 插件管理插件
|
||||
│ └── ExamplePlugin/ # 示例插件
|
||||
└── docs/ # 文档目录
|
||||
└── MemoryBank/ # 内存银行
|
||||
```
|
||||
|
||||
## 当前状态
|
||||
|
||||
✅ **已完成**:
|
||||
- DLL Hook 集成
|
||||
- 消息接收和发送
|
||||
- 插件系统框架
|
||||
- AI 聊天插件
|
||||
- 插件管理功能
|
||||
- 热重载支持
|
||||
|
||||
🚧 **进行中**:
|
||||
- 测试和调试
|
||||
- 文档完善
|
||||
|
||||
📋 **待开发**:
|
||||
- 更多插件功能
|
||||
- Web 管理界面
|
||||
- 数据统计功能
|
||||
137
docs/MemoryBank/02-开发日志.md
Normal file
137
docs/MemoryBank/02-开发日志.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 开发日志
|
||||
|
||||
## 2025-11-12
|
||||
|
||||
### 项目初始化
|
||||
- ✅ 创建 WechatHookBot 项目结构
|
||||
- ✅ 从 XYBot 复制 utils 和 database 模块
|
||||
- ✅ 设计技术架构文档
|
||||
|
||||
### Hook API 集成
|
||||
- ✅ 封装 NoveLoader (Loader.dll)
|
||||
- ✅ 封装 WechatHookClient (API 客户端)
|
||||
- ✅ 实现消息类型映射和格式转换
|
||||
- ✅ 实现回调处理机制
|
||||
|
||||
### 启动调试
|
||||
**问题**: 回调未触发,无法接收消息
|
||||
- ❌ 尝试1: 调整回调注册顺序 - 失败
|
||||
- ❌ 尝试2: 修改事件循环处理 - 失败
|
||||
- ✅ 解决: 添加共享内存创建 (`create_shared_memory`)
|
||||
- 关键发现: DLL 需要共享内存进行通信
|
||||
- 参考官方 Demo 第 357-365 行
|
||||
|
||||
**问题**: Socket 客户端 ID 混淆
|
||||
- ❌ 使用进程 ID 发送消息 - 失败
|
||||
- ✅ 解决: 区分进程 ID 和 Socket 客户端 ID
|
||||
- `InjectWeChat` 返回进程 ID
|
||||
- 回调中的 `client_id` 是 Socket ID (通常为 1)
|
||||
|
||||
**问题**: 登录信息获取失败
|
||||
- ❌ 使用 type=11028 - 错误
|
||||
- ✅ 解决: 实际类型是 type=11025
|
||||
- 登录信息在注入后自动推送
|
||||
- 包含 wxid, nickname, account, avatar 等字段
|
||||
|
||||
**问题**: 消息类型映射错误
|
||||
- ❌ 使用 10001-10013 - 错误
|
||||
- ✅ 解决: 实际类型是 11046-11061
|
||||
- 文本消息: 11046
|
||||
- 图片消息: 11047
|
||||
- 其他类型依次递增
|
||||
|
||||
**问题**: 群聊消息处理失败
|
||||
- ❌ 使用 `from_wxid` 判断群聊 - 错误
|
||||
- ✅ 解决: 使用 `room_wxid` 字段判断
|
||||
- 群聊消息: `room_wxid` 不为空
|
||||
- 私聊消息: `room_wxid` 为空
|
||||
- 消息内容字段: `msg` (不是 `content`)
|
||||
|
||||
### 插件开发
|
||||
|
||||
#### AIChat 插件
|
||||
- ✅ 支持自定义 API 配置
|
||||
- ✅ 支持人设切换 (txt 文件)
|
||||
- ✅ 三种触发模式: all/mention/keyword
|
||||
- ✅ 群聊/私聊分别控制
|
||||
|
||||
**问题**: 插件配置未加载
|
||||
- ❌ 使用 `on_load` 方法 - 失败
|
||||
- ✅ 解决: 使用 `async_init` 方法
|
||||
- 插件基类只支持 `async_init`
|
||||
- `on_enable` 用于定时任务注册
|
||||
|
||||
#### ManagePlugin 插件
|
||||
- ✅ 插件列表查看
|
||||
- ✅ 热重载功能
|
||||
- ✅ 启用/禁用插件
|
||||
- ✅ 权限控制(管理员)
|
||||
|
||||
**命令列表**:
|
||||
- `/插件列表` - 查看所有插件状态
|
||||
- `/重载插件 <名称>` - 热重载指定插件
|
||||
- `/启用插件 <名称>` - 启用插件
|
||||
- `/禁用插件 <名称>` - 禁用插件
|
||||
|
||||
### 依赖管理
|
||||
**最终依赖**:
|
||||
```
|
||||
loguru==0.7.3
|
||||
APScheduler==3.11.0
|
||||
aiohttp==3.9.1
|
||||
```
|
||||
|
||||
**移除的依赖**:
|
||||
- SQLAlchemy (需要 C++ 编译)
|
||||
- eventlet (msgspec 不支持 32 位)
|
||||
- Flask (不需要 WebUI)
|
||||
|
||||
### 关键技术点
|
||||
|
||||
1. **共享内存创建**
|
||||
```python
|
||||
def create_shared_memory():
|
||||
kernel32 = ctypes.WinDLL('kernel32')
|
||||
file_handle = kernel32.CreateFileMappingA(-1, None, 4, 0, 33,
|
||||
"windows_shell_global__".encode('utf-8'))
|
||||
data_address = kernel32.MapViewOfFile(file_handle, 983071, 0, 0, 0)
|
||||
key = "3101b223dca7715b0154924f0eeeee20".encode('utf-8')
|
||||
kernel32.RtlMoveMemory(data_address, key, len(key))
|
||||
```
|
||||
|
||||
2. **异步回调处理**
|
||||
```python
|
||||
# 在回调线程中使用事件循环
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self.hookbot.process_message(msg_type, data),
|
||||
self.event_loop
|
||||
)
|
||||
```
|
||||
|
||||
3. **消息格式转换**
|
||||
```python
|
||||
# 群聊判断
|
||||
room_wxid = data.get("room_wxid", "")
|
||||
if room_wxid:
|
||||
message["IsGroup"] = True
|
||||
message["FromWxid"] = room_wxid
|
||||
message["SenderWxid"] = data.get("from_wxid", "")
|
||||
```
|
||||
|
||||
### 测试结果
|
||||
|
||||
✅ **成功测试**:
|
||||
- 机器人启动和注入
|
||||
- 登录信息获取
|
||||
- 私聊消息接收
|
||||
- 群聊消息接收
|
||||
- ping-pong 测试
|
||||
- 插件管理命令
|
||||
- AI 聊天功能
|
||||
|
||||
### 下一步计划
|
||||
|
||||
1. 完善插件功能
|
||||
2. 添加更多消息类型支持
|
||||
3. 优化错误处理
|
||||
4. 编写使用文档
|
||||
234
docs/MemoryBank/03-技术要点.md
Normal file
234
docs/MemoryBank/03-技术要点.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# 技术要点
|
||||
|
||||
## 核心架构
|
||||
|
||||
### 1. DLL Hook 机制
|
||||
|
||||
**工作流程**:
|
||||
```
|
||||
1. 加载 Loader.dll
|
||||
2. 创建共享内存 (关键!)
|
||||
3. 注册 Socket 回调
|
||||
4. 调用 InjectWeChat 注入 Helper.dll
|
||||
5. 等待 Socket 连接回调
|
||||
6. 使用 Socket Client ID 发送 API 请求
|
||||
```
|
||||
|
||||
**关键代码**:
|
||||
```python
|
||||
# 共享内存创建 (必须在 Loader 之前)
|
||||
create_shared_memory()
|
||||
|
||||
# 注册回调
|
||||
add_callback_handler(self)
|
||||
loader = NoveLoader(loader_dll)
|
||||
|
||||
# 注入微信
|
||||
process_id = loader.InjectWeChat(helper_dll)
|
||||
|
||||
# 等待 Socket 连接
|
||||
# socket_client_id 从回调中获取 (通常为 1)
|
||||
|
||||
# 使用 Socket ID 发送请求
|
||||
client = WechatHookClient(loader, socket_client_id)
|
||||
```
|
||||
|
||||
### 2. 消息类型映射
|
||||
|
||||
**实际测试的消息类型**:
|
||||
```python
|
||||
MT_DEBUG_LOG = 11024 # 调试日志
|
||||
MT_USER_LOGIN = 11025 # 用户登录 (自动推送)
|
||||
MT_TEXT = 11046 # 文本消息
|
||||
MT_IMAGE = 11047 # 图片消息
|
||||
MT_VOICE = 11048 # 语音消息
|
||||
MT_VIDEO = 11049 # 视频消息
|
||||
MT_EMOJI = 11050 # 表情消息
|
||||
MT_LOCATION = 11051 # 位置消息
|
||||
MT_LINK = 11052 # 链接消息
|
||||
MT_FILE = 11053 # 文件消息
|
||||
MT_MINIAPP = 11054 # 小程序消息
|
||||
MT_CARD = 11055 # 名片消息
|
||||
MT_FRIEND_REQUEST = 11056 # 好友请求
|
||||
MT_REVOKE = 11057 # 撤回消息
|
||||
MT_SYSTEM = 11058 # 系统消息
|
||||
```
|
||||
|
||||
### 3. 消息数据结构
|
||||
|
||||
**文本消息** (type=11046):
|
||||
```json
|
||||
{
|
||||
"at_user_list": [],
|
||||
"from_wxid": "wxid_xxx",
|
||||
"is_pc": 0,
|
||||
"msg": "消息内容",
|
||||
"msgid": "123456789",
|
||||
"room_wxid": "",
|
||||
"timestamp": 1762940000,
|
||||
"to_wxid": "wxid_yyy",
|
||||
"wx_type": 1
|
||||
}
|
||||
```
|
||||
|
||||
**登录信息** (type=11025):
|
||||
```json
|
||||
{
|
||||
"account": "账号",
|
||||
"avatar": "http://...",
|
||||
"device_id": "设备ID",
|
||||
"nickname": "昵称",
|
||||
"phone": "手机号",
|
||||
"wxid": "wxid_xxx",
|
||||
"wx_user_dir": "C:\\..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 群聊消息判断
|
||||
|
||||
**关键字段**:
|
||||
- `room_wxid`: 群聊 ID,私聊时为空
|
||||
- `from_wxid`: 发送者 wxid
|
||||
- `to_wxid`: 接收者 (群聊时是群 ID)
|
||||
|
||||
**判断逻辑**:
|
||||
```python
|
||||
room_wxid = data.get("room_wxid", "")
|
||||
if room_wxid:
|
||||
# 群聊消息
|
||||
message["IsGroup"] = True
|
||||
message["FromWxid"] = room_wxid
|
||||
message["SenderWxid"] = data.get("from_wxid", "")
|
||||
else:
|
||||
# 私聊消息
|
||||
message["IsGroup"] = False
|
||||
message["FromWxid"] = data.get("from_wxid", "")
|
||||
```
|
||||
|
||||
### 5. 异步回调处理
|
||||
|
||||
**问题**: 回调在同步线程中执行,但需要调用异步方法
|
||||
|
||||
**解决方案**:
|
||||
```python
|
||||
# 在初始化时保存事件循环
|
||||
self.event_loop = asyncio.get_event_loop()
|
||||
|
||||
# 在回调中使用
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self.hookbot.process_message(msg_type, data),
|
||||
self.event_loop
|
||||
)
|
||||
```
|
||||
|
||||
### 6. 插件系统
|
||||
|
||||
**插件生命周期**:
|
||||
```python
|
||||
class MyPlugin(PluginBase):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# 初始化成员变量
|
||||
|
||||
async def async_init(self):
|
||||
# 异步初始化 (加载配置等)
|
||||
pass
|
||||
|
||||
async def on_enable(self, bot=None):
|
||||
# 启用时调用 (注册定时任务)
|
||||
pass
|
||||
|
||||
async def on_disable(self):
|
||||
# 禁用时调用 (清理资源)
|
||||
pass
|
||||
```
|
||||
|
||||
**事件处理**:
|
||||
```python
|
||||
@on_text_message()
|
||||
async def handle_message(self, bot, message: dict):
|
||||
content = message.get("Content", "")
|
||||
from_wxid = message.get("FromWxid", "")
|
||||
|
||||
# 处理消息
|
||||
await bot.send_text(from_wxid, "回复内容")
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 为什么必须使用 32 位 Python?
|
||||
**A**: Loader.dll 和 Helper.dll 是 32 位编译的,只能在 32 位 Python 中加载。
|
||||
|
||||
### Q2: 为什么需要共享内存?
|
||||
**A**: DLL 之间通过共享内存进行通信,必须在加载 Loader.dll 之前创建。
|
||||
|
||||
### Q3: 为什么 Socket Client ID 和进程 ID 不同?
|
||||
**A**:
|
||||
- `InjectWeChat` 返回的是微信进程 ID
|
||||
- 回调中的 `client_id` 是 Socket 连接 ID (从 1 开始)
|
||||
- 发送 API 请求时使用 Socket ID
|
||||
|
||||
### Q4: 如何判断是群聊还是私聊?
|
||||
**A**: 检查 `room_wxid` 字段,不为空则是群聊。
|
||||
|
||||
### Q5: 插件配置为什么没有加载?
|
||||
**A**: 使用 `async_init` 方法而不是 `on_load`。
|
||||
|
||||
### Q6: 如何热重载插件?
|
||||
**A**: 发送 `/重载插件 插件名` 命令。
|
||||
|
||||
### Q7: 为什么收不到消息?
|
||||
**A**: 检查以下几点:
|
||||
1. 共享内存是否创建成功
|
||||
2. Socket 客户端是否连接
|
||||
3. 消息类型是否在 MESSAGE_TYPE_MAP 中
|
||||
4. 消息是否被过滤 (白名单/黑名单)
|
||||
|
||||
### Q8: 如何添加新的消息类型?
|
||||
**A**:
|
||||
1. 在 `message_types.py` 中添加常量
|
||||
2. 在 `MESSAGE_TYPE_MAP` 中添加映射
|
||||
3. 在 `normalize_message` 中处理特殊字段
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. 消息处理
|
||||
- 使用 `asyncio.run_coroutine_threadsafe` 避免阻塞回调线程
|
||||
- 深拷贝消息数据避免并发问题
|
||||
|
||||
### 2. 插件管理
|
||||
- 按优先级排序事件处理器
|
||||
- 支持插件返回 False 中断处理链
|
||||
|
||||
### 3. 错误处理
|
||||
- 每个处理器独立 try-except
|
||||
- 一个插件出错不影响其他插件
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 1. 查看回调数据
|
||||
```python
|
||||
logger.info(f"[回调] 收到消息: type={msg_type}, data={msg_data}")
|
||||
```
|
||||
|
||||
### 2. 查看消息处理
|
||||
```python
|
||||
logger.info(f"收到消息: type={event_type}, from={from_wxid}, content={content}")
|
||||
```
|
||||
|
||||
### 3. 查看插件加载
|
||||
```python
|
||||
logger.info(f"插件 {plugin_name} 已加载")
|
||||
```
|
||||
|
||||
### 4. 使用调试日志
|
||||
```python
|
||||
logger.debug(f"详细信息: {variable}")
|
||||
```
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
1. **API 密钥**: 不要将 API 密钥提交到版本控制
|
||||
2. **管理员权限**: 只有管理员可以执行插件管理命令
|
||||
3. **消息过滤**: 使用白名单/黑名单控制消息处理
|
||||
4. **错误处理**: 捕获所有异常避免崩溃
|
||||
Reference in New Issue
Block a user