diff options
Diffstat (limited to 'docs/source/plugin_guide.md')
| -rw-r--r-- | docs/source/plugin_guide.md | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/docs/source/plugin_guide.md b/docs/source/plugin_guide.md new file mode 100644 index 0000000..7c146fe --- /dev/null +++ b/docs/source/plugin_guide.md @@ -0,0 +1,564 @@ +# 插件开发指南 + +本指南将帮助您创建自己的 ConventionalRP 插件。 + +## 目录 + +1. [插件基础](#插件基础) +2. [插件类型](#插件类型) +3. [开发流程](#开发流程) +4. [最佳实践](#最佳实践) +5. [发布插件](#发布插件) + +--- + +## 插件基础 + +### 什么是插件? + +插件是扩展 ConventionalRP 功能的独立模块。每个插件继承自基础 `Plugin` 类,并实现特定的接口。 + +### 插件生命周期 + +1. **发现** - PluginManager 扫描插件目录 +2. **加载** - 导入插件模块 +3. **初始化** - 调用 `initialize()` 方法 +4. **启用** - 调用 `on_enable()` 钩子 +5. **执行** - 调用 `process()` 方法处理数据 +6. **禁用** - 调用 `on_disable()` 钩子 + +### 基本结构 + +```python +from conventionalrp.plugins import Plugin +from typing import Any, Dict, Optional + +class MyPlugin(Plugin): + def __init__(self): + super().__init__(name="MyPlugin", version="1.0.0") + # 初始化插件状态 + self.data = {} + + def initialize(self, config: Optional[Dict[str, Any]] = None): + """初始化插件,加载配置""" + self.config = config or {} + self.logger.info(f"{self.name} initialized with config: {self.config}") + + def process(self, data: Any) -> Any: + """处理数据""" + self.logger.debug(f"Processing data: {data}") + # 实现您的处理逻辑 + return data + + def on_enable(self): + """插件启用时调用""" + super().on_enable() + # 执行启用逻辑(如打开文件、建立连接等) + + def on_disable(self): + """插件禁用时调用""" + # 执行清理逻辑(如关闭文件、断开连接等) + super().on_disable() +``` + +--- + +## 插件类型 + +### 1. 解析器插件 (ParserPlugin) + +解析器插件用于识别和解析特定格式的文本。 + +#### 接口 + +```python +from conventionalrp.plugins import ParserPlugin + +class MyParserPlugin(ParserPlugin): + def can_parse(self, text: str) -> bool: + """判断是否可以解析给定文本""" + raise NotImplementedError + + def parse(self, text: str) -> List[Dict[str, Any]]: + """解析文本并返回结果""" + raise NotImplementedError +``` + +#### 示例:CoC 规则解析器 + +```python +import re +from conventionalrp.plugins import ParserPlugin + +class CoCParserPlugin(ParserPlugin): + """Call of Cthulhu 规则解析器""" + + def __init__(self): + super().__init__("CoCParser", "1.0.0") + self.priority = 60 + + # CoC 特有的模式 + self.skill_check_pattern = re.compile( + r'\.(?:rc|san|sc)\s+(.+)', + re.IGNORECASE + ) + self.sanity_pattern = re.compile( + r'(\d+)/(\d+d\d+)', + re.IGNORECASE + ) + + def initialize(self, config=None): + self.config = config or {} + + def can_parse(self, text: str) -> bool: + """检查是否是 CoC 指令""" + return bool(self.skill_check_pattern.match(text)) + + def parse(self, text: str) -> List[Dict[str, Any]]: + """解析 CoC 指令""" + results = [] + + match = self.skill_check_pattern.match(text) + if match: + command_text = match.group(1) + + # 解析 SAN 检定 + san_match = self.sanity_pattern.search(command_text) + if san_match: + results.append({ + "type": "sanity_check", + "success_loss": san_match.group(1), + "failure_loss": san_match.group(2), + "content": text, + }) + else: + # 普通技能检定 + results.append({ + "type": "skill_check", + "skill": command_text.strip(), + "content": text, + }) + + return results +``` + +### 2. 处理器插件 (ProcessorPlugin) + +处理器插件用于转换和增强数据。 + +#### 示例:经验值追踪器 + +```python +from conventionalrp.plugins import ProcessorPlugin +import re + +class ExperienceTrackerPlugin(ProcessorPlugin): + """经验值追踪插件""" + + def __init__(self): + super().__init__("ExperienceTracker", "1.0.0") + self.character_exp = {} + self.exp_pattern = re.compile(r'获得\s*(\d+)\s*点?经验', re.IGNORECASE) + + def initialize(self, config=None): + self.config = config or {} + # 加载初始经验值 + if "initial_exp" in self.config: + self.character_exp = self.config["initial_exp"].copy() + + def process_token(self, token: Dict[str, Any]) -> Dict[str, Any]: + """处理 token 并追踪经验值""" + content = token.get("content", "") + speaker = token.get("speaker", "") + + # 检测经验值获取 + match = self.exp_pattern.search(content) + if match and speaker: + exp_gained = int(match.group(1)) + + if speaker not in self.character_exp: + self.character_exp[speaker] = 0 + + self.character_exp[speaker] += exp_gained + + # 添加经验值信息到 token + token["exp_data"] = { + "gained": exp_gained, + "total": self.character_exp[speaker], + "character": speaker, + } + + self.logger.info(f"{speaker} 获得 {exp_gained} 经验,总计 {self.character_exp[speaker]}") + + return token + + def get_leaderboard(self) -> List[Dict[str, Any]]: + """获取经验值排行榜""" + sorted_chars = sorted( + self.character_exp.items(), + key=lambda x: x[1], + reverse=True + ) + return [ + {"character": char, "exp": exp} + for char, exp in sorted_chars + ] +``` + +### 3. 渲染器插件 (RendererPlugin) + +渲染器插件用于生成特定格式的输出。 + +#### 示例:Discord Markdown 渲染器 + +```python +from conventionalrp.plugins import RendererPlugin + +class DiscordRenderer(RendererPlugin): + """Discord Markdown 渲染器""" + + def __init__(self): + super().__init__("DiscordRenderer", "1.0.0") + + def initialize(self, config=None): + self.config = config or {} + self.use_embeds = self.config.get("use_embeds", False) + + def render(self, data: Any) -> str: + """渲染为 Discord 格式""" + output = [] + + for token in data: + token_type = token.get("type", "unknown") + speaker = token.get("speaker", "") + content = token.get("content", "") + + if token_type == "dialogue": + # 使用 Discord 的粗体和引用 + output.append(f"**{speaker}:** {content}") + + elif token_type == "dice": + # 使用代码块高亮骰子结果 + result = token.get("result", "?") + output.append(f"`{content}` → **{result}**") + + elif token_type == "narration": + # 使用斜体表示旁白 + output.append(f"*{content}*") + + elif token_type == "system": + # 使用代码块表示系统消息 + output.append(f"```\n{content}\n```") + + else: + output.append(content) + + return "\n\n".join(output) +``` + +### 4. 分析器插件 (AnalyzerPlugin) + +分析器插件用于统计和分析数据。 + +#### 示例:对话分析器 + +```python +from conventionalrp.plugins import AnalyzerPlugin +from collections import Counter + +class DialogueAnalyzerPlugin(AnalyzerPlugin): + """对话分析插件""" + + def __init__(self): + super().__init__("DialogueAnalyzer", "1.0.0") + + def initialize(self, config=None): + self.config = config or {} + + def analyze(self, data: Any) -> Dict[str, Any]: + """分析对话数据""" + dialogue_count = Counter() + word_count = Counter() + total_words = 0 + + for token in data: + if token.get("type") == "dialogue": + speaker = token.get("speaker", "Unknown") + content = token.get("content", "") + + # 统计对话次数 + dialogue_count[speaker] += 1 + + # 统计词数 + words = len(content.split()) + word_count[speaker] += words + total_words += words + + # 计算参与度 + most_active = dialogue_count.most_common(1) + most_talkative = word_count.most_common(1) + + return { + "total_dialogues": sum(dialogue_count.values()), + "total_words": total_words, + "dialogue_count": dict(dialogue_count), + "word_count": dict(word_count), + "most_active_character": most_active[0][0] if most_active else None, + "most_talkative_character": most_talkative[0][0] if most_talkative else None, + "average_words_per_dialogue": total_words / sum(dialogue_count.values()) if dialogue_count else 0, + } +``` + +--- + +## 开发流程 + +### 1. 创建插件项目 + +```bash +mkdir my_plugin +cd my_plugin +touch my_plugin.py +touch config.json +touch README.md +``` + +### 2. 实现插件 + +```python +# my_plugin.py +from conventionalrp.plugins import ProcessorPlugin + +class MyPlugin(ProcessorPlugin): + # ... 实现代码 + pass +``` + +### 3. 添加配置 + +```json +{ + "name": "MyPlugin", + "version": "1.0.0", + "description": "My custom plugin", + "author": "Your Name", + "settings": { + "enable_feature_x": true, + "threshold": 10 + } +} +``` + +### 4. 测试插件 + +```python +# test_my_plugin.py +import unittest +from my_plugin import MyPlugin + +class TestMyPlugin(unittest.TestCase): + def setUp(self): + self.plugin = MyPlugin() + self.plugin.initialize() + + def test_process(self): + test_data = [{"type": "test", "content": "test"}] + result = self.plugin.process(test_data) + self.assertIsNotNone(result) + + def test_enable_disable(self): + self.assertTrue(self.plugin.enabled) + self.plugin.on_disable() + self.assertFalse(self.plugin.enabled) + +if __name__ == "__main__": + unittest.main() +``` + +### 5. 集成测试 + +```python +# integration_test.py +from conventionalrp.plugins import PluginManager +from my_plugin import MyPlugin + +manager = PluginManager() +plugin = MyPlugin() +plugin.initialize({"test": True}) +manager.register_plugin(plugin) + +# 测试数据 +test_data = [...] + +# 执行插件 +result = manager.execute_plugins(test_data) +print(result) +``` + +--- + +## 最佳实践 + +### 1. 命名约定 + +- 插件类名使用 PascalCase,如 `MyCustomPlugin` +- 文件名使用 snake_case,如 `my_custom_plugin.py` +- 配置键使用 snake_case + +### 2. 日志记录 + +```python +def process_token(self, token): + self.logger.debug(f"Processing token: {token['type']}") + + try: + result = self._do_processing(token) + self.logger.info("Processing successful") + return result + except Exception as e: + self.logger.error(f"Processing failed: {e}") + raise +``` + +### 3. 错误处理 + +```python +from conventionalrp.utils.exceptions import PluginError + +def initialize(self, config=None): + try: + self.config = config or {} + # 验证必需的配置 + if "required_key" not in self.config: + raise PluginError( + f"{self.name}: Missing required configuration 'required_key'" + ) + except Exception as e: + self.logger.error(f"Initialization failed: {e}") + raise +``` + +### 4. 性能优化 + +```python +# 使用缓存 +from functools import lru_cache + +@lru_cache(maxsize=128) +def expensive_operation(self, key: str) -> Any: + # 昂贵的计算 + return result + +# 批量处理 +def process(self, data: List[Dict]) -> List[Dict]: + # 一次性处理所有数据,而不是逐个处理 + return [self.process_token(token) for token in data] +``` + +### 5. 文档注释 + +```python +class MyPlugin(Plugin): + """ + 我的自定义插件 + + 这个插件用于... + + Attributes: + setting_x: 设置 X 的说明 + setting_y: 设置 Y 的说明 + + Example: + >>> plugin = MyPlugin() + >>> plugin.initialize({"setting_x": True}) + >>> result = plugin.process(data) + """ + + def process_token(self, token: Dict[str, Any]) -> Dict[str, Any]: + """ + 处理单个 token + + Args: + token: 输入 token + + Returns: + 处理后的 token + + Raises: + PluginError: 如果处理失败 + """ + pass +``` + +--- + +## 发布插件 + +### 1. 准备文件 + +创建以下文件: +- `README.md` - 插件说明 +- `LICENSE` - 开源许可证 +- `requirements.txt` - 依赖列表 +- `setup.py` 或 `pyproject.toml` - 打包配置 + +### 2. 打包 + +```bash +# 使用 setuptools +python setup.py sdist bdist_wheel + +# 或使用 poetry +poetry build +``` + +### 3. 发布 + +```bash +# 发布到 PyPI +twine upload dist/* + +# 或发布到 GitHub +git tag v1.0.0 +git push origin v1.0.0 +``` + +### 4. 文档 + +在 README.md 中包含: +- 插件描述 +- 安装方法 +- 使用示例 +- 配置选项 +- API 文档 +- 许可证信息 + +--- + +## 示例项目结构 + +``` +my_plugin/ +├── README.md +├── LICENSE +├── requirements.txt +├── setup.py +├── my_plugin/ +│ ├── __init__.py +│ ├── plugin.py +│ └── utils.py +├── tests/ +│ ├── test_plugin.py +│ └── test_utils.py +└── examples/ + └── example_usage.py +``` + +--- + +## 相关资源 + +- [插件系统演示](../../examples/plugin_system_demo.py) +- [示例插件](../../examples/plugins/) +- [API 文档](api.md) +- [高级使用指南](advanced_usage.md) |
