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) 进行插件化改造。