diff --git a/README.md b/README.md
index 196a7f131..bd5f1cc94 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# 🌟 MoFox_Bot
-**🚀 基于 MaiCore 的增强型 AI 智能体,功能更强大,体验更流畅**
+**🚀 基于 MaiCore 0.10.0 snapshot.5进一步开发的 AI 智能体,插件功能更强大**
@@ -36,12 +36,13 @@
## 📖 项目简介
-**MoFox_Bot** 是一个基于 [MaiCore](https://github.com/MaiM-with-u/MaiBot) `0.10.0 snapshot.5` 的增强型 fork 项目。我们保留了原项目几乎所有核心功能,并在此基础上进行了深度优化与功能扩展,致力于打造一个**更稳定、更智能、更具趣味性**的 AI 智能体。
+**MoFox_Bot** 是一个基于 [MaiCore](https://github.com/MaiM-with-u/MaiBot) `0.10.0 snapshot.5` 的 fork 项目。我们保留了原项目几乎所有核心功能,并在此基础上进行了深度优化与功能扩展,致力于打造一个**更稳定、更智能、更具趣味性**的 AI 智能体。
+
> [IMPORTANT]
> **第三方项目声明**
>
-> 本项目由 **MoFox Studio** 独立维护,为 **MaiBot 的第三方分支**,并非官方版本。所有更新与支持均由我们团队负责,与 MaiBot 官方无直接关系。
+> 本项目Fork后由 **MoFox Studio** 独立维护,为 **MaiBot 的第三方分支**,并非官方版本。所有更新与支持均由我们团队负责,后续的更新与 MaiBot 官方无直接关系。
> [WARNING]
> **迁移风险提示**
@@ -62,7 +63,7 @@
|
-### 🔧 原版功能(全部保留)
+### 🔧 MaiBot 0.10.0 snapshot.5 原版功能
- 🔌 **强大插件系统** - 全面重构的插件架构,支持完整的管理 API 和权限控制
- 💭 **实时思维系统** - 模拟人类思考过程
- 📚 **表达学习功能** - 学习群友的说话风格和表达方式
@@ -104,7 +105,7 @@
| 🖥️ 操作系统 | Windows 10/11、macOS 10.14+、Linux (Ubuntu 18.04+) |
| 🐍 Python 版本 | Python 3.11 或更高版本 |
| 💾 内存 | 建议 ≥ 4GB 可用内存 |
-| 💿 存储空间 | 建议 ≥ 2GB 可用空间 |
+| 💿 存储空间 | 建议 ≥ 4GB 可用空间 |
### 🛠️ 依赖服务
@@ -150,10 +151,12 @@
| 项目 | 描述 | 贡献 |
| ------------------------------------------ | -------------------- | ---------------- |
-| 🎯 [MaiM-with-u/MaiBot](https://github.com/MaiM-with-u/MaiBot) | 原版 MaiBot 框架 | 提供核心架构与设计 |
+| 🎯 [MaiM-with-u/MaiBot](https://github.com/Mai-with-u/MaiBot) | 原版 MaiBot 框架 | 提供核心架构与设计 |
| 🐱 [NapNeko/NapCatQQ](https://github.com/NapNeko/NapCatQQ) | 高性能 QQ 协议端 | 实现稳定通信 |
| 🌌 [internetsb/Maizone](https://github.com/internetsb/Maizone) | 魔改空间插件 | 功能借鉴与启发 |
+如果可以的话,请为这些项目也点个 ⭐️ !(尤其是MaiBot)
+
---
diff --git a/docs/OneKey-Plus b/docs/OneKey-Plus
new file mode 100644
index 000000000..ef8a4a5c2
--- /dev/null
+++ b/docs/OneKey-Plus
@@ -0,0 +1 @@
+准备放关于一键包的文档
diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py
index 9e6b19a32..92ec77391 100644
--- a/src/chat/replyer/default_generator.py
+++ b/src/chat/replyer/default_generator.py
@@ -131,6 +131,7 @@ def init_prompt():
{safety_guidelines_block}
{group_chat_reminder_block}
+- 在称呼用户时,请使用更自然的昵称或简称。对于长英文名,可使用首字母缩写;对于中文名,可提炼合适的简称。禁止直接复述复杂的用户名或输出用户名中的任何符号,让称呼更像人类习惯,注意,简称不是必须的,合理的使用。
你的回复应该是一条简短、完整且口语化的回复。
--------------------------------
@@ -213,6 +214,7 @@ If you need to use the search tool, please directly call the function "lpmm_sear
## 规则
{safety_guidelines_block}
{group_chat_reminder_block}
+- 在称呼用户时,请使用更自然的昵称或简称。对于长英文名,可使用首字母缩写;对于中文名,可提炼合适的简称。禁止直接复述复杂的用户名或输出用户名中的任何符号,让称呼更像人类习惯,注意,简称不是必须的,合理的使用。
你的回复应该是一条简短、完整且口语化的回复。
--------------------------------
diff --git a/src/config/api_ada_configs.py b/src/config/api_ada_configs.py
index bc500413a..3e58300e9 100644
--- a/src/config/api_ada_configs.py
+++ b/src/config/api_ada_configs.py
@@ -70,7 +70,7 @@ class ModelInfo(ValidatedConfigBase):
price_out: float = Field(default=0.0, ge=0, description="每M token输出价格")
force_stream_mode: bool = Field(default=False, description="是否强制使用流式输出模式")
extra_params: dict[str, Any] = Field(default_factory=dict, description="额外参数(用于API调用时的额外配置)")
- anti_truncation: bool = Field(default=False, description="是否启用反截断功能,防止模型输出被截断")
+ anti_truncation: bool = Field(default=False, alias="use_anti_truncation", description="是否启用反截断功能,防止模型输出被截断")
enable_prompt_perturbation: bool = Field(default=False, description="是否启用提示词扰动(合并了内容混淆和注意力优化)")
perturbation_strength: Literal["light", "medium", "heavy"] = Field(
default="light", description="扰动强度(light/medium/heavy)"
diff --git a/src/main.py b/src/main.py
index 62386001b..143feae51 100644
--- a/src/main.py
+++ b/src/main.py
@@ -42,7 +42,6 @@ logger = get_logger("main")
# 预定义彩蛋短语,避免在每次初始化时重新创建
EGG_PHRASES: list[tuple[str, int]] = [
("我们的代码里真的没有bug,只有'特性'。", 10),
- ("你知道吗?阿范喜欢被切成臊子😡", 10),
("你知道吗,雅诺狐的耳朵其实很好摸", 5),
("你群最高技术力————言柒姐姐!", 20),
("初墨小姐宇宙第一(不是)", 10),
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/_manifest.json b/src/plugins/built_in/siliconflow_api_index_tts/_manifest.json
new file mode 100644
index 000000000..cb67442df
--- /dev/null
+++ b/src/plugins/built_in/siliconflow_api_index_tts/_manifest.json
@@ -0,0 +1,50 @@
+{
+ "manifest_version": 1,
+ "name": "SiliconFlow IndexTTS 语音合成插件",
+ "version": "2.0.0",
+ "description": "基于SiliconFlow API的IndexTTS语音合成插件,使用IndexTeam/IndexTTS-2模型支持高质量的零样本语音克隆。",
+ "author": {
+ "name": "MoFox Studio",
+ "url": "https://github.com/MoFox-Studio"
+ },
+ "license": "GPL-v3.0-or-later",
+
+ "host_application": {
+ "min_version": "0.8.0"
+ },
+ "homepage_url": "https://docs.siliconflow.cn/cn/userguide/capabilities/text-to-speech",
+ "repository_url": "https://github.com/MoFox-Studio/MoFox-Bot",
+ "keywords": ["tts", "voice", "audio", "speech", "indextts", "voice-cloning", "siliconflow"],
+ "categories": ["Audio Tools", "Voice Assistant", "AI Tools"],
+
+ "default_locale": "zh-CN",
+ "locales_path": "_locales",
+
+ "plugin_info": {
+ "is_built_in": true,
+ "plugin_type": "audio_processor",
+ "components": [
+ {
+ "type": "action",
+ "name": "siliconflow_indextts_action",
+ "description": "使用SiliconFlow API进行IndexTTS语音合成",
+ "activation_modes": ["llm_judge", "keyword"],
+ "keywords": ["克隆语音", "模仿声音", "语音合成", "indextts", "声音克隆", "语音生成", "仿声", "变声"]
+ },
+ {
+ "type": "command",
+ "name": "siliconflow_tts_cmd",
+ "description": "SiliconFlow IndexTTS语音合成命令",
+ "command_name": "sf_tts",
+ "aliases": ["sftts", "sf语音", "硅基语音"]
+ }
+ ],
+ "features": [
+ "零样本语音克隆",
+ "情感控制语音合成",
+ "自定义参考音频",
+ "高质量音频输出",
+ "多种语音风格"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/README.md b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/README.md
new file mode 100644
index 000000000..9463003e4
--- /dev/null
+++ b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/README.md
@@ -0,0 +1,46 @@
+# 参考音频目录
+
+将您的参考音频文件放置在此目录中,用于语音克隆功能。
+
+## 音频要求
+
+- **格式**: WAV, MP3, M4A
+- **采样率**: 16kHz 或 24kHz
+- **时长**: 3-30秒(推荐5-10秒)
+- **质量**: 语音清晰,无背景噪音
+- **内容**: 自然语音,避免音乐或特效
+
+## 文件命名建议
+
+- 使用描述性的文件名,例如:
+ - `male_voice_calm.wav` - 男声平静
+ - `female_voice_cheerful.wav` - 女声活泼
+ - `child_voice_cute.wav` - 童声可爱
+ - `elderly_voice_wise.wav` - 老年声音睿智
+
+## 使用方法
+
+1. 将音频文件复制到此目录
+2. 在命令中使用文件名:
+ ```
+ /sf_tts "测试文本" --ref "your_audio.wav"
+ ```
+3. 或在配置中设置默认参考音频:
+ ```toml
+ [synthesis]
+ default_reference_audio = "your_audio.wav"
+ ```
+
+## 注意事项
+
+- 确保您有使用这些音频的合法权限
+- 音频质量会直接影响克隆效果
+- 建议定期清理不需要的音频文件
+
+## 示例音频
+
+您可以录制或收集一些不同风格的音频:
+
+- **情感类型**: 开心、悲伤、愤怒、平静、激动
+- **说话风格**: 正式、随意、播报、对话
+- **音调特点**: 低沉、清亮、温柔、有力
\ No newline at end of file
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/default.wav b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/default.wav
new file mode 100644
index 000000000..772994e46
Binary files /dev/null and b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/default.wav differ
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/refer.mp3 b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/refer.mp3
new file mode 100644
index 000000000..480f07fb0
Binary files /dev/null and b/src/plugins/built_in/siliconflow_api_index_tts/audio_reference/refer.mp3 differ
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/plugin.py b/src/plugins/built_in/siliconflow_api_index_tts/plugin.py
new file mode 100644
index 000000000..44104f94b
--- /dev/null
+++ b/src/plugins/built_in/siliconflow_api_index_tts/plugin.py
@@ -0,0 +1,449 @@
+"""
+SiliconFlow IndexTTS 语音合成插件
+基于SiliconFlow API的IndexTTS语音合成插件,支持高质量的零样本语音克隆和情感控制
+"""
+
+import os
+import base64
+import hashlib
+import asyncio
+import aiohttp
+import json
+import toml
+from typing import Tuple, Optional, Dict, Any, List, Type
+from pathlib import Path
+
+from src.plugin_system import BasePlugin, BaseAction, BaseCommand, register_plugin, ConfigField
+from src.plugin_system.base.base_action import ActionActivationType, ChatMode
+from src.common.logger import get_logger
+
+logger = get_logger("SiliconFlow-TTS")
+
+
+def get_global_siliconflow_api_key() -> Optional[str]:
+ """从全局配置文件中获取SiliconFlow API密钥"""
+ try:
+ # 读取全局model_config.toml配置文件
+ config_path = Path("config/model_config.toml")
+ if not config_path.exists():
+ logger.error("全局配置文件 config/model_config.toml 不存在")
+ return None
+
+ with open(config_path, "r", encoding="utf-8") as f:
+ model_config = toml.load(f)
+
+ # 查找SiliconFlow API提供商配置
+ api_providers = model_config.get("api_providers", [])
+ for provider in api_providers:
+ if provider.get("name") == "SiliconFlow":
+ api_key = provider.get("api_key", "")
+ if api_key:
+ logger.info("成功从全局配置读取SiliconFlow API密钥")
+ return api_key
+
+ logger.warning("在全局配置中未找到SiliconFlow API提供商或API密钥为空")
+ return None
+
+ except Exception as e:
+ logger.error(f"读取全局配置失败: {e}")
+ return None
+
+
+class SiliconFlowTTSClient:
+ """SiliconFlow TTS API客户端"""
+
+ def __init__(self, api_key: str, base_url: str = "https://api.siliconflow.cn/v1/audio/speech",
+ timeout: int = 60, max_retries: int = 3):
+ self.api_key = api_key
+ self.base_url = base_url
+ self.timeout = timeout
+ self.max_retries = max_retries
+
+ async def synthesize_speech(self, text: str, voice_id: str,
+ model: str = "IndexTeam/IndexTTS-2",
+ speed: float = 1.0, volume: float = 1.0,
+ emotion_strength: float = 1.0,
+ output_format: str = "wav") -> bytes:
+ """
+ 调用SiliconFlow API进行语音合成
+
+ Args:
+ text: 要合成的文本
+ voice_id: 预配置的语音ID
+ model: 模型名称 (默认使用IndexTeam/IndexTTS-2)
+ speed: 语速
+ volume: 音量
+ emotion_strength: 情感强度
+ output_format: 输出格式
+
+ Returns:
+ 合成的音频数据
+ """
+ headers = {
+ "Authorization": f"Bearer {self.api_key}",
+ "Content-Type": "application/json"
+ }
+
+ # 构建请求数据
+ data = {
+ "model": model,
+ "input": text,
+ "voice": voice_id,
+ "format": output_format,
+ "speed": speed
+ }
+
+ logger.info(f"使用配置的Voice ID: {voice_id}")
+
+ # 发送请求
+ for attempt in range(self.max_retries):
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ self.base_url,
+ headers=headers,
+ json=data,
+ timeout=aiohttp.ClientTimeout(total=self.timeout)
+ ) as response:
+ if response.status == 200:
+ audio_data = await response.read()
+ logger.info(f"语音合成成功,音频大小: {len(audio_data)} bytes")
+ return audio_data
+ else:
+ error_text = await response.text()
+ logger.error(f"API请求失败 (状态码: {response.status}): {error_text}")
+ if attempt == self.max_retries - 1:
+ raise Exception(f"API请求失败: {response.status} - {error_text}")
+ except asyncio.TimeoutError:
+ logger.warning(f"请求超时,尝试第 {attempt + 1}/{self.max_retries} 次")
+ if attempt == self.max_retries - 1:
+ raise Exception("请求超时")
+ except Exception as e:
+ logger.error(f"请求异常: {e}")
+ if attempt == self.max_retries - 1:
+ raise e
+ await asyncio.sleep(2 ** attempt) # 指数退避
+
+ raise Exception("所有重试都失败了")
+
+
+class SiliconFlowIndexTTSAction(BaseAction):
+ """SiliconFlow IndexTTS Action组件"""
+
+ # 激活设置
+ focus_activation_type = ActionActivationType.LLM_JUDGE
+ normal_activation_type = ActionActivationType.KEYWORD
+ mode_enable = ChatMode.ALL
+ parallel_action = False
+
+ # 动作基本信息
+ action_name = "siliconflow_indextts_action"
+ action_description = "使用SiliconFlow API进行高质量的IndexTTS语音合成,支持零样本语音克隆"
+
+ # 关键词配置
+ activation_keywords = ["克隆语音", "模仿声音", "语音合成", "indextts", "声音克隆", "语音生成", "仿声", "变声"]
+ keyword_case_sensitive = False
+
+ # 动作参数定义
+ action_parameters = {
+ "text": "需要合成语音的文本内容,必填,应当清晰流畅",
+ "speed": "语速(可选),范围0.1-3.0,默认1.0"
+ }
+
+ # 动作使用场景
+ action_require = [
+ "当用户要求语音克隆或模仿某个声音时使用",
+ "当用户明确要求进行语音合成时使用",
+ "当需要高质量语音输出时使用",
+ "当用户要求变声或仿声时使用"
+ ]
+
+ # 关联类型 - 支持语音消息
+ associated_types = ["voice"]
+
+ async def execute(self) -> Tuple[bool, str]:
+ """执行SiliconFlow IndexTTS语音合成"""
+ logger.info(f"{self.log_prefix} 执行SiliconFlow IndexTTS动作: {self.reasoning}")
+
+ # 优先从全局配置获取SiliconFlow API密钥
+ api_key = get_global_siliconflow_api_key()
+ if not api_key:
+ # 如果全局配置中没有,则从插件配置获取(兼容旧版本)
+ api_key = self.get_config("api.api_key", "")
+ if not api_key:
+ logger.error(f"{self.log_prefix} SiliconFlow API密钥未配置")
+ return False, "请在全局配置 config/model_config.toml 中配置SiliconFlow API密钥"
+
+ # 获取文本内容 - 多种来源尝试
+ text = ""
+
+ # 1. 尝试从action_data获取text参数
+ text = self.action_data.get("text", "")
+ if not text:
+ # 2. 尝试从action_data获取tts_text参数(兼容其他TTS插件)
+ text = self.action_data.get("tts_text", "")
+
+ if not text:
+ # 3. 如果没有提供具体文本,则生成一个基于reasoning的语音回复
+ if self.reasoning:
+ # 基于内心思考生成适合语音播报的内容
+ # 这里可以进行一些处理,让内心思考更适合作为语音输出
+ if "阿范" in self.reasoning and any(word in self.reasoning for word in ["想听", "语音", "声音"]):
+ # 如果reasoning表明用户想听语音,生成相应回复
+ text = "喵~阿范想听我的声音吗?那就用这个新的语音合成功能试试看吧~"
+ elif "测试" in self.reasoning:
+ text = "好吧,那就试试这个新的语音合成功能吧~"
+ else:
+ # 使用reasoning的内容,但做适当调整
+ text = self.reasoning
+ logger.info(f"{self.log_prefix} 基于reasoning生成语音内容")
+ else:
+ # 如果完全没有内容,使用默认回复
+ text = "喵~使用SiliconFlow IndexTTS测试语音合成功能~"
+ logger.info(f"{self.log_prefix} 使用默认语音内容")
+
+ # 获取其他参数
+ speed = float(self.action_data.get("speed", self.get_config("synthesis.speed", 1.0)))
+
+ try:
+ # 获取预配置的voice_id
+ voice_id = self.get_config("synthesis.voice_id", "")
+ if not voice_id or not isinstance(voice_id, str):
+ logger.error(f"{self.log_prefix} 配置中未找到有效的voice_id,请先运行upload_voice.py工具上传参考音频")
+ return False, "配置中未找到有效的voice_id"
+
+ logger.info(f"{self.log_prefix} 使用预配置的voice_id: {voice_id}")
+
+ # 创建TTS客户端
+ client = SiliconFlowTTSClient(
+ api_key=api_key,
+ base_url=self.get_config("api.base_url", "https://api.siliconflow.cn/v1/audio/speech"),
+ timeout=self.get_config("api.timeout", 60),
+ max_retries=self.get_config("api.max_retries", 3)
+ )
+
+ # 合成语音
+ audio_data = await client.synthesize_speech(
+ text=text,
+ voice_id=voice_id,
+ model=self.get_config("synthesis.model", "IndexTeam/IndexTTS-2"),
+ speed=speed,
+ output_format=self.get_config("synthesis.output_format", "wav")
+ )
+
+ # 转换为base64编码(语音消息需要base64格式)
+ audio_base64 = base64.b64encode(audio_data).decode('utf-8')
+
+ # 发送语音消息(使用voice类型,支持WAV格式的base64)
+ await self.send_custom(
+ message_type="voice",
+ content=audio_base64
+ )
+
+ # 记录动作信息
+ await self.store_action_info(
+ action_build_into_prompt=True,
+ action_prompt_display=f"已使用SiliconFlow IndexTTS生成语音: {text[:20]}...",
+ action_done=True
+ )
+
+ logger.info(f"{self.log_prefix} 语音合成成功,文本长度: {len(text)}")
+ return True, "SiliconFlow IndexTTS语音合成成功"
+
+ except Exception as e:
+ logger.error(f"{self.log_prefix} 语音合成失败: {e}")
+ return False, f"语音合成失败: {str(e)}"
+
+
+class SiliconFlowTTSCommand(BaseCommand):
+ """SiliconFlow TTS命令组件"""
+
+ command_name = "sf_tts"
+ command_description = "使用SiliconFlow IndexTTS进行语音合成"
+ command_aliases = ["sftts", "sf语音", "硅基语音"]
+
+ command_parameters = {
+ "text": {"type": str, "required": True, "description": "要合成的文本"},
+ "speed": {"type": float, "required": False, "description": "语速 (0.1-3.0)"}
+ }
+
+ async def execute(self, text: str, speed: float = 1.0) -> Tuple[bool, str]:
+ """执行TTS命令"""
+ logger.info(f"{self.log_prefix} 执行SiliconFlow TTS命令")
+
+ # 优先从全局配置获取SiliconFlow API密钥
+ api_key = get_global_siliconflow_api_key()
+ if not api_key:
+ # 如果全局配置中没有,则从插件配置获取(兼容旧版本)
+ plugin = self.get_plugin()
+ api_key = plugin.get_config("api.api_key", "")
+ if not api_key:
+ await self.send_reply("❌ SiliconFlow API密钥未配置!请在全局配置 config/model_config.toml 中设置。")
+ return False, "API密钥未配置"
+
+ try:
+ await self.send_reply("正在使用SiliconFlow IndexTTS合成语音,请稍候...")
+
+ # 使用默认参考音频 refer.mp3
+ # 通过插件文件所在目录获取audio_reference目录
+ plugin_dir = Path(__file__).parent
+ audio_dir = plugin_dir / "audio_reference"
+ reference_audio_path = audio_dir / "refer.mp3"
+
+ if not reference_audio_path.exists():
+ logger.warning(f"参考音频文件不存在: {reference_audio_path}")
+ reference_audio_path = None
+
+ # 创建TTS客户端
+ client = SiliconFlowTTSClient(
+ api_key=api_key,
+ base_url="https://api.siliconflow.cn/v1/audio/speech",
+ timeout=60,
+ max_retries=3
+ )
+
+ # 合成语音
+ audio_data = await client.synthesize_speech(
+ text=text,
+ reference_audio_path=str(reference_audio_path) if reference_audio_path else None,
+ model="IndexTeam/IndexTTS-2",
+ speed=speed,
+ output_format="wav"
+ )
+
+ # 生成临时文件名
+ text_hash = hashlib.md5(text.encode()).hexdigest()[:8]
+ filename = f"siliconflow_tts_{text_hash}.wav"
+
+ # 发送音频
+ await self.send_custom(
+ message_type="audio_file",
+ content=audio_data,
+ filename=filename
+ )
+
+ await self.send_reply("✅ 语音合成完成!")
+ return True, "命令执行成功"
+
+ except Exception as e:
+ error_msg = f"❌ 语音合成失败: {str(e)}"
+ await self.send_reply(error_msg)
+ logger.error(f"{self.log_prefix} 命令执行失败: {e}")
+ return False, str(e)
+
+
+@register_plugin
+class SiliconFlowIndexTTSPlugin(BasePlugin):
+ """SiliconFlow IndexTTS插件主类"""
+
+ plugin_name = "siliconflow_api_index_tts"
+ plugin_description = "基于SiliconFlow API的IndexTTS语音合成插件"
+ plugin_version = "2.0.0"
+ plugin_author = "MoFox Studio"
+
+ # 必需的抽象属性
+ enable_plugin: bool = True
+ dependencies: list[str] = []
+ config_file_name: str = "config.toml"
+
+ # Python依赖
+ python_dependencies = ["aiohttp>=3.8.0"]
+
+ # 配置描述
+ config_section_descriptions = {
+ "plugin": "插件基本配置",
+ "components": "组件启用配置",
+ "api": "SiliconFlow API配置",
+ "synthesis": "语音合成配置"
+ }
+
+ # 配置schema
+ config_schema = {
+ "plugin": {
+ "enabled": ConfigField(type=bool, default=False, description="是否启用插件"),
+ "config_version": ConfigField(type=str, default="2.0.0", description="配置文件版本"),
+ },
+ "components": {
+ "enable_action": ConfigField(type=bool, default=True, description="是否启用Action组件"),
+ "enable_command": ConfigField(type=bool, default=True, description="是否启用Command组件"),
+ },
+ "api": {
+ "api_key": ConfigField(type=str, default="",
+ description="SiliconFlow API密钥(可选,优先使用全局配置)"),
+ "base_url": ConfigField(type=str, default="https://api.siliconflow.cn/v1/audio/speech",
+ description="SiliconFlow TTS API地址"),
+ "timeout": ConfigField(type=int, default=60, description="API请求超时时间(秒)"),
+ "max_retries": ConfigField(type=int, default=3, description="API请求最大重试次数"),
+ },
+ "synthesis": {
+ "model": ConfigField(type=str, default="IndexTeam/IndexTTS-2",
+ description="TTS模型名称"),
+ "speed": ConfigField(type=float, default=1.0,
+ description="默认语速 (0.1-3.0)"),
+ "output_format": ConfigField(type=str, default="wav",
+ description="输出音频格式"),
+ }
+ }
+
+ def get_plugin_components(self):
+ """获取插件组件"""
+ from src.plugin_system.base.component_types import ActionInfo, CommandInfo, ComponentType
+
+ components = []
+
+ # 检查配置是否启用组件
+ if self.get_config("components.enable_action", True):
+ action_info = ActionInfo(
+ name="siliconflow_indextts_action",
+ component_type=ComponentType.ACTION,
+ description="使用SiliconFlow API进行高质量的IndexTTS语音合成",
+ activation_keywords=["克隆语音", "模仿声音", "语音合成", "indextts", "声音克隆", "语音生成", "仿声", "变声"],
+ plugin_name=self.plugin_name
+ )
+ components.append((action_info, SiliconFlowIndexTTSAction))
+
+ if self.get_config("components.enable_command", True):
+ command_info = CommandInfo(
+ name="sf_tts",
+ component_type=ComponentType.COMMAND,
+ description="使用SiliconFlow IndexTTS进行语音合成",
+ plugin_name=self.plugin_name
+ )
+ components.append((command_info, SiliconFlowTTSCommand))
+
+ return components
+
+ async def on_plugin_load(self):
+ """插件加载时的回调"""
+ logger.info("SiliconFlow IndexTTS插件已加载")
+
+ # 检查audio_reference目录
+ audio_dir = Path(self.plugin_path) / "audio_reference"
+ if not audio_dir.exists():
+ audio_dir.mkdir(parents=True, exist_ok=True)
+ logger.info(f"创建音频参考目录: {audio_dir}")
+
+ # 检查参考音频文件
+ refer_file = audio_dir / "refer.mp3"
+ if not refer_file.exists():
+ logger.warning(f"参考音频文件不存在: {refer_file}")
+ logger.info("请确保将自定义参考音频文件命名为 refer.mp3 并放置在 audio_reference 目录中")
+
+ # 检查API密钥配置(优先检查全局配置)
+ api_key = get_global_siliconflow_api_key()
+ if not api_key:
+ # 检查插件配置(兼容旧版本)
+ plugin_api_key = self.get_config("api.api_key", "")
+ if not plugin_api_key:
+ logger.warning("SiliconFlow API密钥未配置,请在全局配置 config/model_config.toml 中设置SiliconFlow API提供商")
+ else:
+ logger.info("检测到插件本地API密钥配置(建议迁移到全局配置)")
+ else:
+ logger.info("SiliconFlow API密钥配置检查通过")
+
+ # 你怎么知道我终于丢掉了我自己的脑子并使用了ai来帮我写代码的
+ # 我也不知道,反正我现在就这样干了()
+
+ async def on_plugin_unload(self):
+ """插件卸载时的回调"""
+ logger.info("SiliconFlow IndexTTS插件已卸载")
\ No newline at end of file
diff --git a/src/plugins/built_in/siliconflow_api_index_tts/upload_voice.py b/src/plugins/built_in/siliconflow_api_index_tts/upload_voice.py
new file mode 100644
index 000000000..f18987d87
--- /dev/null
+++ b/src/plugins/built_in/siliconflow_api_index_tts/upload_voice.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python3
+"""
+SiliconFlow IndexTTS Voice Upload Tool
+用于上传参考音频文件并获取voice_id的工具脚本
+"""
+
+import asyncio
+import base64
+import logging
+import sys
+from pathlib import Path
+
+import aiohttp
+import toml
+
+
+# 设置日志
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+
+class VoiceUploader:
+ """语音上传器"""
+
+ def __init__(self, api_key: str):
+ self.api_key = api_key
+ self.upload_url = "https://api.siliconflow.cn/v1/uploads/audio/voice"
+
+ async def upload_audio(self, audio_path: str) -> str:
+ """
+ 上传音频文件并获取voice_id
+
+ Args:
+ audio_path: 音频文件路径
+
+ Returns:
+ voice_id: 返回的语音ID
+ """
+ audio_path = Path(audio_path)
+ if not audio_path.exists():
+ raise FileNotFoundError(f"音频文件不存在: {audio_path}")
+
+ # 读取音频文件并转换为base64
+ with open(audio_path, "rb") as f:
+ audio_data = f.read()
+
+ audio_base64 = base64.b64encode(audio_data).decode('utf-8')
+
+ # 准备请求数据
+ request_data = {
+ "file": audio_base64,
+ "filename": audio_path
+ }
+
+ headers = {
+ "Authorization": f"Bearer {self.api_key}",
+ "Content-Type": "application/json"
+ }
+
+ logger.info(f"正在上传音频文件: {audio_path}")
+ logger.info(f"文件大小: {len(audio_data)} bytes")
+
+ async with aiohttp.ClientSession() as session:
+ async with session.post(
+ self.upload_url,
+ headers=headers,
+ json=request_data,
+ timeout=aiohttp.ClientTimeout(total=60)
+ ) as response:
+ if response.status == 200:
+ result = await response.json()
+ voice_id = result.get("id")
+ if voice_id:
+ logger.info(f"上传成功!获取到voice_id: {voice_id}")
+ return voice_id
+ else:
+ logger.error(f"上传响应中没有找到voice_id: {result}")
+ raise Exception("上传响应中没有找到voice_id")
+ else:
+ error_text = await response.text()
+ logger.error(f"上传失败 (状态码: {response.status}): {error_text}")
+ raise Exception(f"上传失败: {error_text}")
+
+
+def load_config(config_path: Path) -> dict:
+ """加载配置文件"""
+ if config_path.exists():
+ with open(config_path, 'r', encoding='utf-8') as f:
+ return toml.load(f)
+ return {}
+
+
+def save_config(config_path: Path, config: dict):
+ """保存配置文件"""
+ config_path.parent.mkdir(parents=True, exist_ok=True)
+ with open(config_path, 'w', encoding='utf-8') as f:
+ toml.dump(config, f)
+
+
+async def main():
+ """主函数"""
+ if len(sys.argv) != 2:
+ print("用法: python upload_voice.py <音频文件路径>")
+ print("示例: python upload_voice.py refer.mp3")
+ sys.exit(1)
+
+ audio_file = sys.argv[1]
+
+ # 获取插件目录
+ plugin_dir = Path(__file__).parent
+
+ # 加载全局配置获取API key
+ bot_dir = plugin_dir.parents[2] # 回到Bot目录
+ global_config_path = bot_dir / "config" / "model_config.toml"
+
+ if not global_config_path.exists():
+ logger.error(f"全局配置文件不存在: {global_config_path}")
+ logger.error("请确保Bot/config/model_config.toml文件存在并配置了SiliconFlow API密钥")
+ sys.exit(1)
+
+ global_config = load_config(global_config_path)
+
+ # 从api_providers中查找SiliconFlow的API密钥
+ api_key = None
+ api_providers = global_config.get("api_providers", [])
+ for provider in api_providers:
+ if provider.get("name") == "SiliconFlow":
+ api_key = provider.get("api_key")
+ break
+
+ if not api_key:
+ logger.error("在全局配置中未找到SiliconFlow API密钥")
+ logger.error("请在Bot/config/model_config.toml中添加SiliconFlow的api_providers配置:")
+ logger.error("[[api_providers]]")
+ logger.error("name = \"SiliconFlow\"")
+ logger.error("base_url = \"https://api.siliconflow.cn/v1\"")
+ logger.error("api_key = \"your_api_key_here\"")
+ logger.error("client_type = \"openai\"")
+ sys.exit(1)
+
+ try:
+ # 创建上传器并上传音频
+ uploader = VoiceUploader(api_key)
+ voice_id = await uploader.upload_audio(audio_file)
+
+ # 更新插件配置
+ plugin_config_path = plugin_dir / "config.toml"
+ plugin_config = load_config(plugin_config_path)
+
+ if "synthesis" not in plugin_config:
+ plugin_config["synthesis"] = {}
+
+ plugin_config["synthesis"]["voice_id"] = voice_id
+
+ save_config(plugin_config_path, plugin_config)
+
+ logger.info(f"配置已更新!voice_id已保存到: {plugin_config_path}")
+ logger.info("现在可以使用SiliconFlow IndexTTS插件了!")
+
+ except Exception as e:
+ logger.error(f"上传失败: {e}")
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ asyncio.run(main())
\ No newline at end of file
diff --git a/src/plugins/phi_plugin/README.md b/src/plugins/phi_plugin/README.md
new file mode 100644
index 000000000..7c4ec0ab9
--- /dev/null
+++ b/src/plugins/phi_plugin/README.md
@@ -0,0 +1,110 @@
+# Phi Plugin for MoFox_Bot
+
+基于MoFox_Bot插件系统的Phigros查分插件,移植自原phi-plugin项目。
+
+## 插件化进展
+
+### ✅ 已完成
+1. **基础架构搭建**
+ - 创建了完整的插件目录结构
+ - 实现了_manifest.json和config.toml配置文件
+ - 建立了MoFox_Bot插件系统兼容的基础框架
+
+2. **命令系统迁移**
+ - 实现了5个核心命令的PlusCommand适配:
+ - `phi help` - 帮助命令
+ - `phi bind` - sessionToken绑定命令
+ - `phi b30` - Best30查询命令
+ - `phi info` - 个人信息查询命令
+ - `phi score` - 单曲成绩查询命令
+
+3. **数据管理模块**
+ - 创建了PhiDataManager用于数据处理
+ - 创建了PhiDatabaseManager用于数据库操作
+ - 设计了统一的数据访问接口
+
+4. **配置与元数据**
+ - 符合MoFox_Bot规范的manifest文件
+ - 支持功能开关的配置文件
+ - 完整的插件依赖管理
+
+### 🚧 待实现
+1. **核心功能逻辑**
+ - Phigros API调用实现
+ - sessionToken验证逻辑
+ - 存档数据解析处理
+ - B30等数据计算算法
+
+2. **数据存储**
+ - 用户token数据库存储
+ - 曲库数据导入
+ - 别名系统迁移
+
+3. **图片生成**
+ - B30成绩图片生成
+ - 个人信息卡片生成
+ - 单曲成绩展示图
+
+4. **高级功能**
+ - 更多原phi-plugin命令迁移
+ - 数据缓存优化
+ - 性能监控
+
+## 目录结构
+
+```
+src/plugins/phi_plugin/
+├── __init__.py # 插件初始化
+├── plugin.py # 主插件文件
+├── _manifest.json # 插件元数据
+├── config.toml # 插件配置
+├── README.md # 本文档
+├── commands/ # 命令实现
+│ ├── __init__.py
+│ ├── phi_help.py # 帮助命令
+│ ├── phi_bind.py # 绑定命令
+│ ├── phi_b30.py # B30查询
+│ ├── phi_info.py # 信息查询
+│ └── phi_score.py # 单曲成绩
+├── utils/ # 工具模块
+│ ├── __init__.py
+│ └── data_manager.py # 数据管理器
+├── data/ # 数据文件
+└── static/ # 静态资源
+```
+
+## 使用方式
+
+### 命令列表
+- `/phi help` - 查看帮助
+- `/phi bind ` - 绑定sessionToken
+- `/phi b30` - 查询Best30成绩
+- `/phi info [1|2]` - 查询个人信息
+- `/phi score <曲名>` - 查询单曲成绩
+
+### 配置说明
+编辑 `config.toml` 文件可以调整:
+- 插件启用状态
+- API相关设置
+- 功能开关
+
+## 技术特点
+
+1. **架构兼容**:完全符合MoFox_Bot插件系统规范
+2. **命令适配**:使用PlusCommand系统,支持别名和参数解析
+3. **模块化设计**:清晰的模块分离,便于维护和扩展
+4. **异步处理**:全面使用async/await进行异步处理
+5. **错误处理**:完善的异常处理和用户提示
+
+## 开发说明
+
+目前插件已完成基础架构搭建,可以在MoFox_Bot中正常加载和注册命令。
+
+下一步开发重点:
+1. 实现Phigros API调用逻辑
+2. 完成数据库存储功能
+3. 移植原插件的核心算法
+4. 实现图片生成功能
+
+## 原始项目
+基于 [phi-plugin](https://github.com/Catrong/phi-plugin) 进行插件化改造。
|