添加热重载,优化readme
This commit is contained in:
13
README.md
13
README.md
@@ -7,6 +7,7 @@
|
|||||||
* **OneBot 11 标准支持**:完整支持 OneBot 11 的消息、通知、请求和元事件。
|
* **OneBot 11 标准支持**:完整支持 OneBot 11 的消息、通知、请求和元事件。
|
||||||
* **类型安全**:基于 `dataclasses` 的强类型事件模型,开发体验更佳。
|
* **类型安全**:基于 `dataclasses` 的强类型事件模型,开发体验更佳。
|
||||||
* **插件系统**:轻量级的装饰器风格插件系统,支持指令 (`@matcher.command`) 和事件监听 (`@matcher.on_notice`, `@matcher.on_request`)。
|
* **插件系统**:轻量级的装饰器风格插件系统,支持指令 (`@matcher.command`) 和事件监听 (`@matcher.on_notice`, `@matcher.on_request`)。
|
||||||
|
* **🔥 热重载支持**:内置文件监控,修改 `base_plugins` 下的代码自动重载,无需重启,极大提升调试效率。
|
||||||
* **异步核心**:基于 `asyncio` 和 `websockets` 的高性能异步核心。
|
* **异步核心**:基于 `asyncio` 和 `websockets` 的高性能异步核心。
|
||||||
* **自动重连**:内置 WebSocket 断线重连机制。
|
* **自动重连**:内置 WebSocket 断线重连机制。
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
NEO/
|
NEO/
|
||||||
├── base_plugins/ # 基础插件目录,新建插件文件即可自动加载
|
├── base_plugins/ # 基础插件目录,新建插件文件即可自动加载(支持热重载)
|
||||||
│ └── echo.py # 示例插件:实现 /echo 指令
|
│ └── echo.py # 示例插件:实现 /echo 指令
|
||||||
├── core/ # 核心框架代码
|
├── core/ # 核心框架代码
|
||||||
│ ├── bot.py # Bot API 封装,提供 send_group_msg 等方法
|
│ ├── bot.py # Bot API 封装,提供 send_group_msg 等方法
|
||||||
@@ -26,7 +27,7 @@ NEO/
|
|||||||
│ ├── message.py # 消息段定义 (MessageSegment)
|
│ ├── message.py # 消息段定义 (MessageSegment)
|
||||||
│ └── sender.py # 发送者定义 (Sender)
|
│ └── sender.py # 发送者定义 (Sender)
|
||||||
├── config.toml # 配置文件
|
├── config.toml # 配置文件
|
||||||
├── main.py # 启动入口
|
├── main.py # 启动入口(包含热重载监控)
|
||||||
└── requirements.txt # 项目依赖
|
└── requirements.txt # 项目依赖
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,6 +66,14 @@ python main.py
|
|||||||
|
|
||||||
## 🛠️ 开发指南
|
## 🛠️ 开发指南
|
||||||
|
|
||||||
|
### 🔥 热重载调试
|
||||||
|
|
||||||
|
项目集成了 `watchdog` 文件监控。在开发过程中,你只需要:
|
||||||
|
1. 保持 `main.py` 运行。
|
||||||
|
2. 修改或新建 `base_plugins` 目录下的 `.py` 插件文件。
|
||||||
|
3. 保存文件。
|
||||||
|
4. 控制台会自动提示 `[HotReload] 插件重载完成`,新的逻辑立即生效。
|
||||||
|
|
||||||
### 创建新插件
|
### 创建新插件
|
||||||
|
|
||||||
在 `base_plugins` 目录下创建一个新的 `.py` 文件(例如 `my_plugin.py`),框架会自动加载它。
|
在 `base_plugins` 目录下创建一个新的 `.py` 文件(例如 `my_plugin.py`),框架会自动加载它。
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
def load_all_plugins():
|
def load_all_plugins():
|
||||||
@@ -14,11 +15,17 @@ def load_all_plugins():
|
|||||||
full_module_name = f"{package_name}.{module_name}"
|
full_module_name = f"{package_name}.{module_name}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
if full_module_name in sys.modules:
|
||||||
|
importlib.reload(sys.modules[full_module_name])
|
||||||
|
action = "重载"
|
||||||
|
else:
|
||||||
importlib.import_module(full_module_name)
|
importlib.import_module(full_module_name)
|
||||||
|
action = "加载"
|
||||||
|
|
||||||
type_str = "包" if is_pkg else "文件"
|
type_str = "包" if is_pkg else "文件"
|
||||||
print(f" [{type_str}] 成功加载: {module_name}")
|
print(f" [{type_str}] 成功{action}: {module_name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" 加载插件 {module_name} 失败: {e}")
|
print(f" {action if 'action' in locals() else '加载'}插件 {module_name} 失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
load_all_plugins()
|
load_all_plugins()
|
||||||
|
|||||||
@@ -23,3 +23,18 @@ async def handle_echo(bot: Bot, event: MessageEvent, args: list[str]):
|
|||||||
reply_msg = " ".join(args)
|
reply_msg = " ".join(args)
|
||||||
|
|
||||||
await event.reply(reply_msg)
|
await event.reply(reply_msg)
|
||||||
|
|
||||||
|
@matcher.command("poke")
|
||||||
|
async def handle_poke(bot: Bot, event: MessageEvent, args: list[str]):
|
||||||
|
"""
|
||||||
|
处理 echo 指令,原样回复用户输入的内容
|
||||||
|
|
||||||
|
:param bot: Bot 实例
|
||||||
|
:param event: 消息事件对象
|
||||||
|
:param args: 指令参数列表
|
||||||
|
"""
|
||||||
|
|
||||||
|
await bot.call_api("group_poke", {
|
||||||
|
"group_id": event.group_id,
|
||||||
|
"user_id": event.user_id
|
||||||
|
})
|
||||||
65
main.py
65
main.py
@@ -2,17 +2,82 @@
|
|||||||
NEO Bot 主程序入口
|
NEO Bot 主程序入口
|
||||||
"""
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
import base_plugins # noqa: F401 别动这里是加载插件的
|
import base_plugins # noqa: F401 别动这里是加载插件的
|
||||||
from core import WS
|
from core import WS
|
||||||
|
|
||||||
|
|
||||||
|
class PluginReloadHandler(FileSystemEventHandler):
|
||||||
|
"""
|
||||||
|
文件变更处理器,用于热重载插件
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.last_reload_time = 0
|
||||||
|
self.cooldown = 1.0 # 冷却时间,防止短时间内多次重载
|
||||||
|
|
||||||
|
def on_any_event(self, event):
|
||||||
|
"""
|
||||||
|
处理所有文件事件
|
||||||
|
"""
|
||||||
|
if event.is_directory:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 只监控 py 文件
|
||||||
|
if not event.src_path.endswith(".py"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# 过滤掉一些临时文件
|
||||||
|
if "__pycache__" in event.src_path:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 简单的防抖动
|
||||||
|
current_time = time.time()
|
||||||
|
if current_time - self.last_reload_time < self.cooldown:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last_reload_time = current_time
|
||||||
|
|
||||||
|
print(f"\n[HotReload] 检测到文件变更: {event.src_path}")
|
||||||
|
print("[HotReload] 正在重载插件...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 重新扫描并加载插件
|
||||||
|
base_plugins.load_all_plugins()
|
||||||
|
print("[HotReload] 插件重载完成")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[HotReload] 重载失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
"""
|
"""
|
||||||
主函数,启动 WebSocket 连接
|
主函数,启动 WebSocket 连接
|
||||||
"""
|
"""
|
||||||
|
# 启动文件监控
|
||||||
|
# 监控 base_plugins 目录
|
||||||
|
plugin_path = os.path.join(os.path.dirname(__file__), "base_plugins")
|
||||||
|
|
||||||
|
event_handler = PluginReloadHandler()
|
||||||
|
observer = Observer()
|
||||||
|
|
||||||
|
if os.path.exists(plugin_path):
|
||||||
|
observer.schedule(event_handler, plugin_path, recursive=True)
|
||||||
|
observer.start()
|
||||||
|
print(f"[HotReload] 已启动插件热重载监控: {plugin_path}")
|
||||||
|
else:
|
||||||
|
print(f"[HotReload] 警告: 插件目录不存在 {plugin_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
bot = WS()
|
bot = WS()
|
||||||
await bot.connect()
|
await bot.connect()
|
||||||
|
finally:
|
||||||
|
if observer.is_alive():
|
||||||
|
observer.stop()
|
||||||
|
observer.join()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Reference in New Issue
Block a user