# NEO Bot 开发规范与公约 写代码很简单,但写出**高性能、不炸裂、好维护**的代码需要遵守规矩。 本文档定义了 NEO Bot 项目的开发守则、编码公约、注意事项和代码规范。所有贡献者和插件开发者都**必须**遵循这些规范,确保机器人稳定运行、代码质量统一。 > 如果你觉得规范太麻烦,可以问问镀铬酸钾,他会给你一对一教学。。。但最好还是遵守规矩。 **补充阅读**: - [插件开发最佳实践](./plugin-development/best-practices.md) - 必读!写插件的基本规矩 - [项目结构](./project-structure.md) - 了解代码组织 - [核心概念](./core-concepts/architecture.md) - 理解框架设计 ## 1. 开发守则(基本原则) ### 1.1 异步优先原则 - **绝对不要阻塞事件循环**:NeoBot 采用多线程异步架构,任何同步阻塞操作都会导致整个机器人卡死。 - **禁止**:`time.sleep()`、同步 `requests`、密集 CPU 计算 - **必须**:使用 `await asyncio.sleep()`、异步 HTTP 客户端、线程池执行同步任务 - **异步任务处理**:长时间运行的任务应使用 `run_in_thread_pool` 或 `asyncio.create_task` 执行,避免阻塞主循环。 ### 1.2 资源管理原则 - **连接复用**:禁止重复创建连接和资源实例。 - HTTP 请求:使用全局 `aiohttp` session 或插件提供的 `get_session()` - 浏览器操作:必须通过 `browser_manager.get_page()` 获取页面实例 - Redis 连接:通过 `redis_manager` 单例访问 - **资源池化**:浏览器页面、数据库连接等资源必须使用框架提供的池化机制。 ### 1.3 性能优化原则 - **缓存策略**:频繁访问的外部数据必须添加缓存。 - 短期缓存(<1小时):使用 Redis 或内存缓存 - 长期缓存:考虑持久化存储 - **懒加载**:大型资源或初始化成本高的组件应延迟加载。 ### 1.4 错误处理原则 - **异常捕获**:所有插件代码都应妥善处理异常,避免插件崩溃影响机器人运行。 - **友好提示**:向用户返回清晰、友好的错误信息,避免暴露内部细节。 - **日志记录**:所有重要操作和错误都应记录日志,使用 `ModuleLogger` 进行结构化日志记录。 ### 1.5 安全性原则 - **输入验证**:所有用户输入都必须验证和清理,防止注入攻击。 - **代码执行安全**:使用沙箱环境执行用户代码,隔离系统资源。 - **权限控制**:严格遵循权限管理系统,禁止越权操作。 ### 1.6 跨平台兼容性原则 NEO Bot 需要在 **Windows 开发环境**和 **Linux 生产环境**中都能正常运行。 - **路径处理**: - 使用 `pathlib.Path` 处理文件路径,避免手动拼接字符串。 - 使用 `/` 作为路径分隔符(Python 会自动转换)。 - 禁止使用硬编码的路径分隔符(如 `\\` 或 `/`)。 - **系统依赖**: - 避免使用平台特定的系统调用。 - 如果必须使用,通过 `sys.platform` 检测平台并提供备选方案。 - **环境变量**: - 通过 `global_config` 获取配置,而不是直接读取环境变量。 - 敏感信息(如 API 密钥)必须通过配置管理。 - **文件权限**: - 在 Linux 上注意文件权限设置,确保 Bot 有读写权限。 - 临时文件应放在系统临时目录(`tempfile.gettempdir()`)。 ## 2. 公约(编码约定) ### 2.1 项目结构公约 - **插件位置**:所有插件必须放置在 `plugins/` 目录下,单个 `.py` 文件或包含 `__init__.py` 的目录。 - **模块导入**:遵循标准导入顺序:标准库 → 第三方库 → 本地模块。 - **配置访问**:通过 `global_config` 单例访问配置,禁止硬编码配置值。 ### 2.2 单例管理器使用公约 NEO Bot 的核心是**单例管理器**(`core/managers/` 目录下的类)。所有全局资源都必须通过管理器访问。 - **禁止重复创建**:严禁自己实例化管理器类,必须通过导入的单例对象访问。 - ✅ `from core.managers.redis_manager import redis_manager` - ❌ `RedisManager()` (错误!会创建新实例) - **资源池化**:浏览器页面、数据库连接等资源必须使用管理器提供的池化接口。 - ✅ `await browser_manager.get_page()` - ❌ `playwright.chromium.launch()` (错误!会创建新浏览器进程) - **数据一致性**:单例管理器确保全局数据一致性,不要绕过管理器直接操作底层资源。 ### 2.2.1 单例模式实现机制 NEO Bot 提供了两种单例模式实现方式,位于 `core/utils/singleton.py`: #### 1. Singleton 基类(继承方式) ```python from core.utils.singleton import Singleton class MyManager(Singleton): """通过继承 Singleton 基类实现单例""" def __init__(self, config: dict): """ 初始化管理器 Args: config: 配置字典 """ # 调用父类 __init__ 确保单例初始化 super().__init__() # 检查是否已经初始化(防止 __init__ 被多次调用) if hasattr(self, '_my_initialized') and self._my_initialized: return # 执行一次性初始化逻辑 self.config = config self.resource = None self._initialize_resource() # 标记为已初始化 self._my_initialized = True def _initialize_resource(self): """初始化资源(只执行一次)""" self.resource = initialize_resource(self.config) async def cleanup(self): """清理资源(单例管理器应实现清理方法)""" if self.resource: await self.resource.close() ``` **特性**: - 通过重写 `__new__` 方法确保每个类只有一个实例 - 自动处理重复初始化问题,但建议子类添加额外的初始化检查 - 使用全局字典存储实例,避免类型检查问题 - 支持带参数的 `__init__` 方法 #### 2. @singleton 装饰器(装饰器方式) ```python from core.utils.singleton import singleton @singleton class MyManager: """通过装饰器实现单例""" def __init__(self, config): self.config = config self.resource = None async def initialize(self): self.resource = await load_resource() ``` **特性**: - 将普通类转换为单例类,无需修改类继承关系 - 保持原始类的元数据(名称、文档字符串等) - 适用于无法修改基类的现有类 #### 3. 使用建议 - **新管理器类**:优先使用 **Singleton 基类继承方式**,结构更清晰 - **现有类转换**:使用 **@singleton 装饰器**,无需重构 - **线程安全**:两种方式都假设在单线程异步环境中使用,如需线程安全请自行加锁 - **导入方式**:单例类应该通过模块级别的实例变量导出,如: ```python # redis_manager.py class RedisManager(Singleton): ... redis_manager = RedisManager() # 创建并导出单例实例 ``` #### 4. 重要注意事项 - **避免循环导入**:单例类的导入应谨慎处理,避免循环依赖 - **初始化时机**:单例在第一次导入时创建,确保所需依赖已就绪 - **__init__ 调用语义**:虽然实例是单例,但 `__init__` 方法可能被多次调用(如重新导入时)。应添加额外检查确保一次性逻辑只执行一次。 - **资源清理**:单例管理器应在程序退出时清理资源,实现 `cleanup()` 方法 ### 2.3 命名公约 - **文件命名**:使用小写字母和下划线,例如 `my_plugin.py`。 - **类命名**:使用 `PascalCase`,例如 `CommandManager`。 - **函数/方法命名**:使用 `snake_case`,例如 `handle_message`。 - **常量命名**:使用 `UPPER_SNAKE_CASE`,例如 `MAX_RETRY_COUNT`。 - **变量命名**:使用 `snake_case`,具有描述性,避免单字母变量(循环变量除外)。 ### 2.4 类型提示公约 - **全面使用**:所有函数、方法、类属性都应提供类型提示。**这是强制要求**,因为框架开启了 Mypyc 编译。 - **性能优化**:类型提示不仅帮助发现 Bug,还能让 Mypyc 生成更高效的机器码。 - **返回类型**:明确指定返回类型,包括 `None`。 - **复杂类型**:使用 `typing` 模块中的泛型,如 `List[str]`、`Dict[str, Any]`。 - **可选参数**:使用 `Optional[...]` 或默认值 `= None`。 **示例**: ```python # 好的写法 async def handle(event: MessageEvent, args: list[str]) -> None: ... # 不好写法(会导致编译警告) async def handle(event, args): ... ``` ### 2.5 异常处理公约 - **自定义异常**:使用框架提供的自定义异常类,避免抛出通用的 `Exception`。 - **异常链**:保留原始异常信息,使用 `raise CustomError(...) from e`。 - **资源清理**:使用 `try...finally` 或上下文管理器确保资源释放。 ### 2.6 日志记录公约 - **模块化日志**:每个模块使用 `ModuleLogger("ModuleName")` 创建专用日志记录器。 - **日志级别**: - `DEBUG`:调试信息,详细操作记录 - `INFO`:常规操作记录 - `WARNING`:预期内的异常或潜在问题 - `ERROR`:操作失败但可恢复的错误 - `CRITICAL`:系统级错误,需要立即关注 ## 3. 注意事项(常见陷阱) ### 3.1 异步编程陷阱 - **忘记 await**:异步函数调用必须使用 `await`,否则任务不会执行。 - **阻塞循环**:在异步函数中执行同步阻塞操作会冻结整个事件循环。 - **任务泄漏**:创建的异步任务必须被妥善管理,避免内存泄漏。 ### 3.2 资源管理陷阱 - **连接泄漏**:未关闭的 HTTP 连接、数据库连接会导致资源耗尽。 - **文件句柄泄漏**:打开的文件必须显式关闭或使用上下文管理器。 - **缓存雪崩**:大量缓存同时过期可能导致系统负载激增。 ### 3.3 性能陷阱 - **N+1 查询**:避免在循环中执行数据库或 API 查询,使用批量操作。 - **内存泄漏**:大型数据结构长时间驻留内存,应定期清理。 - **重复计算**:相同的计算结果应缓存,避免重复计算。 ### 3.4 安全性陷阱 - **SQL 注入**:使用参数化查询或 ORM,禁止拼接 SQL 字符串。 - **XSS 攻击**:渲染用户输入时必须进行 HTML 转义。 - **路径遍历**:用户提供的文件路径必须进行规范化验证。 ## 4. 代码规范(详细指南) ### 4.1 文档字符串规范(强制要求) **所有代码必须包含完整的文档字符串**,这是项目质量保证的基础。缺少文档字符串的代码将在审查中被拒绝。 - **模块级文档**:每个模块顶部应有文档字符串,描述模块功能和主要接口。 - **类级文档**:每个类应有文档字符串,描述类的职责、使用方法和示例。 - **函数/方法级文档**:每个公共函数和方法必须有文档字符串,包含参数说明、返回值和异常信息。 **参数注释要求**: 1. 每个参数都必须有类型提示和简要说明 2. 返回值必须明确说明类型和含义 3. 可能抛出的异常必须列出 4. 复杂的函数应提供使用示例 **标准格式示例:** ```python def process_data(data: List[str], timeout: int = 30) -> Dict[str, Any]: """ 处理数据并返回结果。 Args: data: 待处理的数据列表 timeout: 操作超时时间,单位秒 Returns: 处理结果的字典,包含状态和详情 Raises: TimeoutError: 处理超时时抛出 ValueError: 数据格式错误时抛出 Example: >>> result = process_data(["item1", "item2"]) >>> print(result["status"]) """ ``` ### 4.2 函数设计规范 - **单一职责**:每个函数只做一件事,保持功能简洁。 - **参数数量**:函数参数不宜过多(建议 ≤5),过多时考虑使用 `dataclass` 或 `TypedDict`。 - **默认参数**:避免使用可变对象作为默认参数,使用 `None` 代替。 ### 4.3 类设计规范 - **单一职责**:每个类应有明确的单一职责。 - **组合优于继承**:优先使用组合而非继承来复用功能。 - **属性访问控制**:使用 `@property` 装饰器控制属性访问,隐藏内部实现。 ### 4.4 错误处理规范 - **错误码统一**:使用框架定义的 `ErrorCode` 枚举,避免自定义魔法数字。 - **错误响应格式**:使用 `exception_to_error_response` 生成统一错误响应。 - **用户友好消息**:错误消息应同时包含技术细节(日志)和用户友好提示(界面)。 ### 4.5 测试规范 - **测试覆盖率**:核心功能应达到 80% 以上的测试覆盖率。 - **异步测试**:使用 `pytest-asyncio` 进行异步测试。 - **测试隔离**:测试用例之间应相互独立,避免依赖执行顺序。 ## 5. 提交与协作规范 ### 5.1 Git 提交规范 - **提交信息格式**:遵循 Conventional Commits 规范 ``` ():