diff --git a/build.gradle b/build.gradle index 3cd8e83..958ef0d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'com' -version = '2.6' +version = '2.7' repositories { mavenCentral() @@ -24,12 +24,22 @@ repositories { dependencies { compileOnly "org.spigotmc:spigot-api:1.13-R0.1-SNAPSHOT" implementation 'org.bstats:bstats-bukkit:3.0.2' - implementation 'org.jodd:jodd-http:6.3.0' + implementation('org.jodd:jodd-http:6.3.0') { + exclude group: 'org.jodd', module: 'jodd-core' + exclude group: 'org.jodd', module: 'jodd-bean' + } implementation 'org.json:json:20231013' } shadowJar { archiveFileName = "MineChatGPT-${project.version}.jar" + minimize() + mergeServiceFiles() + + exclude 'META-INF/**' + exclude '**/*.properties' + exclude '**/jodd/http/upload/**' + relocate 'jodd', 'com.ddaodan.shaded.jodd' relocate 'org.json', 'com.ddaodan.minechatgpt.libs.org.json' relocate 'org.bstats', 'com.ddaodan.minechatgpt.libs.org.bstats' diff --git a/changelog.md b/changelog.md index 5bf56f4..b4e439b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,11 @@ # 更新日志 +## 2.7 +- 多语言支持 +- 配置文件添加双语注释 +- 新增负载均衡,可轮询或随机选择API key +- 优化构建配置,插件体积减小 +- 项目规范化 +在此感谢Trae,以上功能都是靠它才能实现的! ## 2.6 - 添加角色功能 - 移除prompt功能 diff --git a/readme.md b/readme.md index f7af321..2d03495 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,9 @@ # MineChatGPT ![minechatgpt](https://socialify.git.ci/ddaodan/minechatgpt/image?description=1&descriptionEditable=%E5%9C%A8Minecraft%E4%B8%AD%E4%B8%8EChatGPT%E4%BA%A4%E6%B5%81&font=Inter&issues=1&language=1&name=1&pattern=Solid&stargazers=1&theme=Auto) -![modrinth-gallery](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/documentation/modrinth-gallery_vector.svg) ![spigot](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/supported/spigot_vector.svg) ![paper](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/supported/paper_vector.svg) ![java8](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/built-with/java8_vector.svg) -![GitHub Release](https://img.shields.io/github/v/release/ddaodan/minechatgpt?label=version) ![bStats Servers](https://img.shields.io/bstats/servers/22635) ![bStats Players](https://img.shields.io/bstats/players/22635) ![Modrinth Downloads](https://img.shields.io/modrinth/dt/Op2X2eDG?logo=modrinth) ![Spiget Downloads](https://img.shields.io/spiget/downloads/118963?logo=spigotmc) +[![modrinth-gallery](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/documentation/modrinth-gallery_vector.svg)](https://modrinth.com/plugin/minechatgpt) [![spigot](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/supported/spigot_vector.svg)](https://www.spigotmc.org/resources/minechatgpt.118963/) ![paper](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/supported/paper_vector.svg) ![java8](https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy-minimal/built-with/java8_vector.svg) +![GitHub Release](https://img.shields.io/github/v/release/ddaodan/minechatgpt?label=version) ![bStats Servers](https://img.shields.io/bstats/servers/22635) ![bStats Players](https://img.shields.io/bstats/players/22635) [![Modrinth Downloads](https://img.shields.io/modrinth/dt/Op2X2eDG?logo=modrinth)](https://modrinth.com/plugin/minechatgpt/versions) [![Spiget Downloads](https://img.shields.io/spiget/downloads/118963?logo=spigotmc)](https://www.spigotmc.org/resources/minechatgpt.118963/) 在Minecraft中与ChatGPT交流 -理论支持全版本,欢迎测试 所有的代码都是ChatGPT写的哦 @@ -12,6 +11,7 @@ - OpenAPI格式 - 自定义模型 - ChatGPT反代 +- 负载均衡 - 指令补全 - 上下文对话 - 多角色 @@ -20,98 +20,52 @@ ## 安装 1. 下载插件,放在plugins文件夹中 2. 重启服务器 -> 为兼容更多版本,插件没有规定Bukkit API version,因此在较高版本加载插件时,控制台可能会出现以下错误信息,这属于正常现象。 -> ``` -> [Server thread/WARN]: Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug! -> [Server thread/WARN]: Legacy plugin MineChatGPT v1.0 does not specify an api-version. -> ``` -3. 打开配置文件`config.yml`,修改以下两项设置: -```yaml -# API 相关设置 -api: - # 你的 OpenAI API key,用于身份验证 - # 获取 API key 的方法:访问 https://platform.openai.com/account/api-keys 并创建一个新的 API key - key: "sk-your_openai_api_key" - # OpenAI API 的基础 URL,用于构建请求 - base_url: "https://api.openai.com/v1" -``` + > 为兼容更多版本,插件没有规定Bukkit API version,因此在较高版本加载插件时,控制台可能会出现以下错误信息,这属于正常现象。 + > ``` + > [Server thread/WARN]: Initializing Legacy Material Support. Unless you have legacy plugins and/or data this is a bug! + > [Server thread/WARN]: Legacy plugin MineChatGPT v1.0 does not specify an api-version. + > ``` +3. 打开配置文件`config.yml`,修改以下设置: + ```yaml + # ====================================================== + # API Configuration + # API 设置 + # ====================================================== + api: + # Your OpenAI API keys, used for authentication + # To obtain an API key, visit https://platform.openai.com/account/api-keys and create a new API key + # 你的 OpenAI API key,用于身份验证 + # 获取 API key 的方法:访问 https://platform.openai.com/account/api-keys 并创建一个新的 API key + keys: + - "sk-your_openai_api_key_1" + # You can add multiple API keys below + # 可以添加多个API key + # - "sk-your_openai_api_key_2" + # - "sk-your_openai_api_key_3" + + # API key selection method: "round_robin" or "random" + # Round Robin: Use each API key in turn + # Random: Randomly select an API key + # API key 选择方法:"round_robin"(轮询)或 "random"(随机) + # 轮询:依次使用每个API key + # 随机:随机选择一个API key + selection_method: "round_robin" + + # The base URL for the OpenAI API, used to construct requests + # If you cannot access the official API, you can use a proxy service + # OpenAI API 的基础 URL,用于构建请求 + # 如果你无法访问官方API,可以使用代理服务 + base_url: "https://api.openai.com/v1" + ``` 4. 在控制台中输入`/chatgpt reload`重新加载配置文件 ## 截图 - 服务端截图(Spigot 1.20.1) -![](https://i.ddaodan.cn/images/CWindowssystem32cmd.exe_20240712406.png) +![](https://i.ddaodan.cc/images/CWindowssystem32cmd.exe_20240712406.png) - 插件截图 -![](https://i.ddaodan.cn/images/Minecraft_1.20.1_-__20240712407.png) +![](https://i.ddaodan.cc/images/Minecraft_1.20.1_-__20240712407.png) - 对话截图(使用FastGPT训练的自定义知识库) -![](https://i.ddaodan.cn/images/Minecraft_1.20.1_-__20240712408.png) -## 配置文件`config.yml` -```yaml -# API 相关设置 -api: - # 你的 OpenAI API key,用于身份验证 - # 获取 API key 的方法:访问 https://platform.openai.com/account/api-keys 并创建一个新的 API key - key: "sk-your_openai_api_key" - # OpenAI API 的基础 URL,用于构建请求 - base_url: "https://api.openai.com/v1" -# 支持的模型列表 -models: - # OpenAI ChatGPT - - "gpt-3.5-turbo" - - "gpt-3.5-turbo-instruct" - - "gpt-4" - - "gpt-4-turbo" - - "gpt-4-turbo-preview" - - "gpt-4o" - - "gpt-4o-mini" - # Google Gemini - # - "gemini-pro" - # - "gemini-1.5-pro" - # Anthropic Claude - # - "claude-3-opus" - # - "claude-3-5-sonnet" - # 以及更多... -# 默认使用的模型 -default_model: "gpt-3.5-turbo" -# 连续对话设置 -conversation: - # 连续对话开关 - context_enabled: false - # 最大历史记录保留数量 - max_history_size: 10 -# 角色设置 -characters: - # 格式: - # 角色名称: "角色提示词" - ChatGPT: "You are a helpful assistant." -# 消息相关设置 -messages: - reload: "&a已重新加载配置文件!" - clear: "&a对话历史已清空!" - help: "&e===== MineChatGPT 帮助 =====" - help_ask: "&e/chatgpt - 向ChatGPT提问" - help_reload: "&e/chatgpt reload - 重新加载配置文件" - help_model: "&e/chatgpt model - 切换至其他模型" - help_modellist: "&e/chatgpt modellist - 可用的模型列表" - help_context: "&e/chatgpt context - 切换连续对话模式" - help_clear: "&e/chatgpt clear - 清空对话历史" - help_character: "&e/chatgpt character [character_name] - 列出或切换角色" - context_toggle: "&e连续对话模式已%s。" - context_toggle_enabled: "&a开启" - context_toggle_disabled: "&c关闭" - current_model_info: "&e当前模型:%s,输入 /chatgpt model 来切换模型。" - model_switch: "&a已切换至模型 %s" - chatgpt_error: "&c无法联系ChatGPT。" - chatgpt_response: "&b%s: %s" - question: "&b你: %s" - character_switched: "&a已切换至角色: %s" - available_characters: "&e可用的角色列表:" - invalid_character: "&c无效的角色。使用 /chatgpt character 查看所有可用的角色。" - invalid_model: "&c模型无效。使用 /chatgpt modellist 查看可用模型。" - available_models: "&e可用模型列表:" - no_permission: "&c你没有权限使用这个指令。需要的权限:%s" -# 如果你不知道这是什么,请不要动 -debug: false -``` +![](https://i.ddaodan.cc/images/Minecraft_1.20.1_-__20240712408.png) ## 指令与权限 |指令|权限|描述| @@ -142,7 +96,8 @@ debug: false ### 提问后控制台有`connect timeout` `connect reset`等类似的提示 检查`config.yml`中的`base_url`能否正常访问。如果你无法连接到OpenAI官方的API地址,可以考虑使用其他反代。 ### 我可以添加其他模型吗? -可以,只要模型支持OpenAI的API,就可以使用。 +可以,只要模型支持OpenAI的API,就可以使用。 +不推荐使用推理模型,因为推理模型的响应时间较长,在没有流式响应的情况下,玩家会认为插件出现了问题。 ### 我没有ChatGPT的账号,可以用吗? 可以,目前有很多代理网站,可以很轻松地使用,而且还支持其他模型,费用通常来说也会比官方便宜。如果你愿意,也可以使用我的代理,目前仅在我的QQ群:226385797中提供。 ### 是否会支持Folia diff --git a/src/main/java/com/ddaodan/MineChatGPT/CommandHandler.java b/src/main/java/com/ddaodan/MineChatGPT/CommandHandler.java index 0ebc35b..ff6bd07 100644 --- a/src/main/java/com/ddaodan/MineChatGPT/CommandHandler.java +++ b/src/main/java/com/ddaodan/MineChatGPT/CommandHandler.java @@ -1,231 +1,52 @@ package com.ddaodan.MineChatGPT; +import com.ddaodan.MineChatGPT.service.ApiService; +import com.ddaodan.MineChatGPT.service.CommandService; +import com.ddaodan.MineChatGPT.service.UserSessionManager; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.List; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.nio.charset.StandardCharsets; - -import jodd.http.HttpRequest; -import jodd.http.HttpResponse; public class CommandHandler implements CommandExecutor { private final Main plugin; private final ConfigManager configManager; - private static final Logger logger = Logger.getLogger(CommandHandler.class.getName()); - private final Map userContexts; - private final Map userContextEnabled; + private final CommandService commandService; + private final UserSessionManager sessionManager; public CommandHandler(Main plugin, ConfigManager configManager) { this.plugin = plugin; this.configManager = configManager; - this.userContexts = new HashMap<>(); - this.userContextEnabled = new HashMap<>(); + this.sessionManager = new UserSessionManager(configManager); + ApiService apiService = new ApiService(configManager); + this.commandService = new CommandService(configManager, apiService, sessionManager); } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { String userId = sender.getName(); - if (!userContexts.containsKey(userId)) { - userContexts.put(userId, new ConversationContext(configManager.getMaxHistorySize())); - userContextEnabled.put(userId, configManager.isContextEnabled()); - } - - ConversationContext conversationContext = userContexts.get(userId); - boolean contextEnabled = userContextEnabled.get(userId); - if (command.getName().equalsIgnoreCase("chatgpt")) { if (args.length == 0) { - sendHelpMessage(sender); + commandService.sendHelpMessage(sender); return true; } + String subCommand = args[0]; if (subCommand.equalsIgnoreCase("reload")) { - if (!sender.hasPermission("minechatgpt.reload")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.reload")); - return true; - } - configManager.reloadConfig(); - sender.sendMessage(configManager.getReloadMessage()); - return true; + return commandService.handleReloadCommand(sender); } else if (subCommand.equalsIgnoreCase("model")) { - if (!sender.hasPermission("minechatgpt.model")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.model")); - return true; - } - if (args.length < 2) { - String currentModel = configManager.getCurrentModel(); - sender.sendMessage(configManager.getCurrentModelInfoMessage().replace("%s", currentModel)); - return true; - } - String model = args[1]; - List models = configManager.getModels(); - if (models.contains(model)) { - configManager.setCurrentModel(model); - sender.sendMessage(configManager.getModelSwitchMessage().replace("%s", model)); - } else { - sender.sendMessage(configManager.getInvalidModelMessage()); - } - return true; + return commandService.handleModelCommand(sender, args); } else if (subCommand.equalsIgnoreCase("modellist")) { - if (!sender.hasPermission("minechatgpt.modellist")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.modellist")); - return true; - } - List models = configManager.getModels(); - sender.sendMessage(configManager.getAvailableModelsMessage()); - for (String model : models) { - sender.sendMessage("- " + model); - } - return true; - } else if (args.length > 0 && args[0].equalsIgnoreCase("context")) { - contextEnabled = !contextEnabled; - userContextEnabled.put(userId, contextEnabled); - String status = contextEnabled ? configManager.getContextToggleEnabledMessage() : configManager.getContextToggleDisabledMessage(); - sender.sendMessage(configManager.getContextToggleMessage().replace("%s", status)); - return true; + return commandService.handleModelListCommand(sender); + } else if (subCommand.equalsIgnoreCase("context")) { + return commandService.handleContextCommand(sender, userId); } else if (subCommand.equalsIgnoreCase("clear")) { - if (!sender.hasPermission("minechatgpt.clear")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.clear")); - return true; - } - conversationContext.clearHistory(); - sender.sendMessage(configManager.getClearMessage()); - return true; + return commandService.handleClearCommand(sender, userId); } else if (subCommand.equalsIgnoreCase("character")) { - if (!sender.hasPermission("minechatgpt.character")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.character")); - return true; - } - Map characters = configManager.getCharacters(); - if (args.length < 2) { - sender.sendMessage(configManager.getAvailableCharactersMessage()); - for (String character : characters.keySet()) { - sender.sendMessage("- " + character); - } - return true; - } - String character = args[1]; - if (characters.containsKey(character)) { - configManager.setCurrentCharacter(userId, character); - sender.sendMessage(configManager.getCharacterSwitchedMessage().replace("%s", character)); - } else { - sender.sendMessage(configManager.getInvalidCharacterMessage()); - } - return true; + return commandService.handleCharacterCommand(sender, args, userId); } else { - if (!sender.hasPermission("minechatgpt.use")) { - sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.use")); - return true; - } - String question = String.join(" ", args); - if (contextEnabled) { - conversationContext.addMessage(question); - } - sender.sendMessage(configManager.getQuestionMessage().replace("%s", question)); - askChatGPT(sender, question, conversationContext, contextEnabled, userId); - return true; + return commandService.handleAskCommand(sender, args, userId); } } return false; } - - private void askChatGPT(CommandSender sender, String question, ConversationContext conversationContext, boolean contextEnabled, String userId) { - String utf8Question = convertToUTF8(question); - JSONObject json = new JSONObject(); - json.put("model", configManager.getDefaultModel()); - JSONArray messages = new JSONArray(); - // 添加自定义 prompt - String currentCharacter = configManager.getCurrentCharacter(userId); - String customPrompt = configManager.getCharacters().get(currentCharacter); - if (customPrompt != null && !customPrompt.isEmpty()) { - JSONObject promptMessage = new JSONObject(); - promptMessage.put("role", "system"); - promptMessage.put("content", customPrompt); - messages.put(promptMessage); - } - JSONObject message = new JSONObject(); - message.put("role", "user"); - message.put("content", utf8Question); - messages.put(message); - if (contextEnabled) { - String history = conversationContext.getConversationHistory(); - if (!history.isEmpty()) { - JSONObject historyMessage = new JSONObject(); - historyMessage.put("role", "system"); - historyMessage.put("content", history); - messages.put(historyMessage); - } - } - json.put("messages", messages); - json.put("model", configManager.getCurrentModel()); - if (configManager.isDebugMode()) { - logger.info("Built request: " + json.toString()); - } - - HttpRequest request = HttpRequest.post(configManager.getBaseUrl() + "/chat/completions") - .header("Content-Type", "application/json; charset=UTF-8") - .header("Authorization", "Bearer " + configManager.getApiKey()) - .bodyText(json.toString()); - - if (configManager.isDebugMode()) { - logger.info("Sending request to ChatGPT: " + request.toString()); - } - - //HttpResponse response = request.send(); - CompletableFuture.supplyAsync(() -> request.send()) - .thenAccept(response -> { - if (configManager.isDebugMode()) { - logger.info("Received response from ChatGPT: " + response.toString()); - } - if (response.statusCode() == 200) { - String responseBody = response.bodyText(); - String utf8ResponseBody = new String(responseBody.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); - JSONObject jsonResponse = new JSONObject(utf8ResponseBody); - String answer = jsonResponse.getJSONArray("choices").getJSONObject(0).getJSONObject("message").getString("content"); - sender.sendMessage(configManager.getChatGPTResponseMessage().replaceFirst("%s", currentCharacter).replaceFirst("%s", answer)); - if (contextEnabled) { - conversationContext.addMessage(answer); // 仅在启用上下文时添加AI响应到历史记录 - } - } else { - String errorBody = response.bodyText(); - logger.log(Level.SEVERE, "Failed to get a response from ChatGPT: " + errorBody); - sender.sendMessage(configManager.getChatGPTErrorMessage()); - } - }) - .exceptionally(e -> { - logger.log(Level.SEVERE, "Exception occurred while processing request: " + e.getMessage(), e); - sender.sendMessage(configManager.getChatGPTErrorMessage()); - return null; - }); - } - - private String convertToUTF8(String input) { - try { - // 尝试将输入字符串转换为 UTF-8 编码 - byte[] bytes = input.getBytes(StandardCharsets.UTF_8); - return new String(bytes, StandardCharsets.UTF_8); - } catch (Exception e) { - logger.severe("Failed to convert input to UTF-8: " + e.getMessage()); - return input; // 如果转换失败,返回原始输入 - } - } - private void sendHelpMessage(CommandSender sender) { - sender.sendMessage(configManager.getHelpMessage()); - sender.sendMessage(configManager.getHelpAskMessage()); - sender.sendMessage(configManager.getHelpReloadMessage()); - sender.sendMessage(configManager.getHelpModelMessage()); - sender.sendMessage(configManager.getHelpModelListMessage()); - sender.sendMessage(configManager.getHelpContextMessage()); - sender.sendMessage(configManager.getHelpClearMessage()); - sender.sendMessage(configManager.getHelpCharacterMessage()); - } } \ No newline at end of file diff --git a/src/main/java/com/ddaodan/MineChatGPT/ConfigManager.java b/src/main/java/com/ddaodan/MineChatGPT/ConfigManager.java index 8891774..aa3e8ab 100644 --- a/src/main/java/com/ddaodan/MineChatGPT/ConfigManager.java +++ b/src/main/java/com/ddaodan/MineChatGPT/ConfigManager.java @@ -11,10 +11,14 @@ public class ConfigManager { private final Main plugin; private FileConfiguration config; private String currentModel; + private LanguageManager languageManager; public ConfigManager(Main plugin) { this.plugin = plugin; reloadConfig(); + // 获取语言设置 + String language = config.getString("language", "en"); + this.languageManager = new LanguageManager(plugin, language); } public boolean isDebugMode() { return config.getBoolean("debug", false); @@ -23,6 +27,12 @@ public class ConfigManager { plugin.reloadConfig(); config = plugin.getConfig(); currentModel = config.getString("default_model"); + + // 重新加载语言文件 + if (languageManager != null) { + String language = config.getString("language", "en"); + languageManager.setLanguage(language); + } } private String translateColorCodes(String message) { @@ -34,8 +44,31 @@ public class ConfigManager { public void setCurrentModel(String model) { currentModel = model; } + private int currentKeyIndex = 0; + public String getApiKey() { - return config.getString("api.key"); + List keys = config.getStringList("api.keys"); + if (keys.isEmpty()) { + // 向后兼容:如果没有找到keys列表,尝试使用旧的单一key配置 + String legacyKey = config.getString("api.key"); + if (legacyKey != null && !legacyKey.isEmpty()) { + return legacyKey; + } + return ""; + } + + String selectionMethod = config.getString("api.selection_method", "round_robin"); + + if ("random".equalsIgnoreCase(selectionMethod)) { + // 随机选择一个key + int randomIndex = (int) (Math.random() * keys.size()); + return keys.get(randomIndex); + } else { + // 默认使用轮询方式 + String key = keys.get(currentKeyIndex); + currentKeyIndex = (currentKeyIndex + 1) % keys.size(); + return key; + } } public String getBaseUrl() { return config.getString("api.base_url"); @@ -44,58 +77,58 @@ public class ConfigManager { return config.getString("default_model"); } public String getReloadMessage() { - return translateColorCodes(config.getString("messages.reload")); + return languageManager.getMessage("reload"); } public List getModels() { return config.getStringList("models"); } public String getHelpMessage() { - return translateColorCodes(config.getString("messages.help")); + return languageManager.getMessage("help"); } public String getHelpAskMessage() { - return translateColorCodes(config.getString("messages.help_ask")); + return languageManager.getMessage("help_ask"); } public String getHelpReloadMessage() { - return translateColorCodes(config.getString("messages.help_reload")); + return languageManager.getMessage("help_reload"); } public String getHelpModelMessage() { - return translateColorCodes(config.getString("messages.help_model")); + return languageManager.getMessage("help_model"); } public String getHelpModelListMessage() { - return translateColorCodes(config.getString("messages.help_modellist")); + return languageManager.getMessage("help_modellist"); } public String getHelpContextMessage() { - return translateColorCodes(config.getString("messages.help_context", "&e/chatgpt context - Toggle context mode.")); + return languageManager.getMessage("help_context"); } public String getHelpClearMessage() { - return translateColorCodes(config.getString("messages.help_clear", "&e/chatgpt clear - Clear conversation history.")); + return languageManager.getMessage("help_clear"); } public String getHelpCharacterMessage() { - return translateColorCodes(config.getString("messages.help_character", "&e/chatgpt character [character_name] - List or switch to a character.")); + return languageManager.getMessage("help_character"); } public String getModelSwitchMessage() { - return translateColorCodes(config.getString("messages.model_switch")); + return languageManager.getMessage("model_switch"); } public String getChatGPTErrorMessage() { - return translateColorCodes(config.getString("messages.chatgpt_error")); + return languageManager.getMessage("chatgpt_error"); } public String getChatGPTResponseMessage() { - return translateColorCodes(config.getString("messages.chatgpt_response", "&b%s: %s")); + return languageManager.getMessage("chatgpt_response", "&b%s: %s"); } public String getQuestionMessage() { - return translateColorCodes(config.getString("messages.question")); + return languageManager.getMessage("question"); } public String getInvalidModelMessage() { - return translateColorCodes(config.getString("messages.invalid_model")); + return languageManager.getMessage("invalid_model"); } public String getAvailableModelsMessage() { - return translateColorCodes(config.getString("messages.available_models")); + return languageManager.getMessage("available_models"); } public String getNoPermissionMessage() { - return translateColorCodes(config.getString("messages.no_permission")); + return languageManager.getMessage("no_permission"); } public String getCurrentModelInfoMessage() { - return translateColorCodes(config.getString("messages.current_model_info")); + return languageManager.getMessage("current_model_info"); } public int getMaxHistorySize() { return config.getInt("conversation.max_history_size", 10); @@ -104,25 +137,25 @@ public class ConfigManager { return config.getBoolean("conversation.context_enabled", false); } public String getContextToggleMessage() { - return translateColorCodes(config.getString("messages.context_toggle", "&eContext is now %s.")); + return languageManager.getMessage("context_toggle", "&eContext is now %s."); } public String getContextToggleEnabledMessage() { - return translateColorCodes(config.getString("messages.context_toggle_enabled", "&aenabled")); + return languageManager.getMessage("context_toggle_enabled", "&aenabled"); } public String getContextToggleDisabledMessage() { - return translateColorCodes(config.getString("messages.context_toggle_disabled", "&edisabled")); + return languageManager.getMessage("context_toggle_disabled", "&edisabled"); } public String getClearMessage() { - return translateColorCodes(config.getString("messages.clear", "&aConversation history has been cleared.")); + return languageManager.getMessage("clear", "&aConversation history has been cleared."); } public String getCharacterSwitchedMessage() { - return translateColorCodes(config.getString("messages.character_switched", "&aSwitched to character: %s")); + return languageManager.getMessage("character_switched", "&aSwitched to character: %s"); } public String getAvailableCharactersMessage() { - return translateColorCodes(config.getString("messages.available_characters", "&eAvailable characters:")); + return languageManager.getMessage("available_characters", "&eAvailable characters:"); } public String getInvalidCharacterMessage() { - return translateColorCodes(config.getString("messages.invalid_character", "&cInvalid character. Use /chatgpt character to list available characters.")); + return languageManager.getMessage("invalid_character", "&cInvalid character. Use /chatgpt character to list available characters."); } public Map getCharacters() { Map characters = new HashMap<>(); diff --git a/src/main/java/com/ddaodan/MineChatGPT/LanguageManager.java b/src/main/java/com/ddaodan/MineChatGPT/LanguageManager.java new file mode 100644 index 0000000..6bae836 --- /dev/null +++ b/src/main/java/com/ddaodan/MineChatGPT/LanguageManager.java @@ -0,0 +1,65 @@ +package com.ddaodan.MineChatGPT; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class LanguageManager { + private final Main plugin; + private FileConfiguration langConfig; + private String currentLanguage; + private File langFile; + + public LanguageManager(Main plugin, String language) { + this.plugin = plugin; + this.currentLanguage = language; + loadLanguage(); + } + + public void loadLanguage() { + langFile = new File(plugin.getDataFolder(), "lang" + File.separator + currentLanguage + ".yml"); + + // 如果语言文件不存在,创建默认语言文件 + if (!langFile.exists()) { + langFile.getParentFile().mkdirs(); + plugin.saveResource("lang/" + currentLanguage + ".yml", false); + } + + langConfig = YamlConfiguration.loadConfiguration(langFile); + + // 设置默认值,以防语言文件中缺少某些键 + InputStream defaultLangStream = plugin.getResource("lang/" + currentLanguage + ".yml"); + if (defaultLangStream != null) { + YamlConfiguration defaultLang = YamlConfiguration.loadConfiguration( + new InputStreamReader(defaultLangStream, StandardCharsets.UTF_8)); + langConfig.setDefaults(defaultLang); + } + } + + public void setLanguage(String language) { + this.currentLanguage = language; + loadLanguage(); + } + + public String getCurrentLanguage() { + return currentLanguage; + } + + private String translateColorCodes(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } + + public String getMessage(String path) { + return translateColorCodes(langConfig.getString("messages." + path, "Missing message: " + path)); + } + + public String getMessage(String path, String defaultValue) { + return translateColorCodes(langConfig.getString("messages." + path, defaultValue)); + } +} \ No newline at end of file diff --git a/src/main/java/com/ddaodan/MineChatGPT/Main.java b/src/main/java/com/ddaodan/MineChatGPT/Main.java index 896b1a9..185bc86 100644 --- a/src/main/java/com/ddaodan/MineChatGPT/Main.java +++ b/src/main/java/com/ddaodan/MineChatGPT/Main.java @@ -9,11 +9,15 @@ public final class Main extends JavaPlugin { private ConfigManager configManager; private CommandHandler commandHandler; private MineChatGPTTabCompleter tabCompleter; + private LanguageManager languageManager; @Override public void onEnable() { saveDefaultConfig(); configManager = new ConfigManager(this); + // 初始化语言管理器 + String language = getConfig().getString("language", "en"); + languageManager = new LanguageManager(this, language); commandHandler = new CommandHandler(this, configManager); tabCompleter = new MineChatGPTTabCompleter(configManager); Objects.requireNonNull(getCommand("chatgpt")).setExecutor(commandHandler); diff --git a/src/main/java/com/ddaodan/MineChatGPT/service/ApiService.java b/src/main/java/com/ddaodan/MineChatGPT/service/ApiService.java new file mode 100644 index 0000000..34612f5 --- /dev/null +++ b/src/main/java/com/ddaodan/MineChatGPT/service/ApiService.java @@ -0,0 +1,155 @@ +package com.ddaodan.MineChatGPT.service; + +import com.ddaodan.MineChatGPT.ConfigManager; +import com.ddaodan.MineChatGPT.ConversationContext; +import org.bukkit.command.CommandSender; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jodd.http.HttpRequest; +import jodd.http.HttpResponse; + +/** + * API服务类,负责处理与OpenAI API的通信 + */ +public class ApiService { + private final ConfigManager configManager; + private static final Logger logger = Logger.getLogger(ApiService.class.getName()); + + public ApiService(ConfigManager configManager) { + this.configManager = configManager; + } + + /** + * 向ChatGPT发送请求 + * + * @param sender 命令发送者 + * @param question 问题内容 + * @param conversationContext 对话上下文 + * @param contextEnabled 是否启用上下文 + * @param userId 用户ID + */ + public void askChatGPT(CommandSender sender, String question, ConversationContext conversationContext, boolean contextEnabled, String userId) { + String utf8Question = convertToUTF8(question); + JSONObject json = new JSONObject(); + json.put("model", configManager.getCurrentModel()); + JSONArray messages = new JSONArray(); + + // 添加自定义 prompt + String currentCharacter = configManager.getCurrentCharacter(userId); + String customPrompt = configManager.getCharacters().get(currentCharacter); + if (customPrompt != null && !customPrompt.isEmpty()) { + JSONObject promptMessage = new JSONObject(); + promptMessage.put("role", "system"); + promptMessage.put("content", customPrompt); + messages.put(promptMessage); + } + + JSONObject message = new JSONObject(); + message.put("role", "user"); + message.put("content", utf8Question); + messages.put(message); + + if (contextEnabled) { + String history = conversationContext.getConversationHistory(); + if (!history.isEmpty()) { + JSONObject historyMessage = new JSONObject(); + historyMessage.put("role", "system"); + historyMessage.put("content", history); + messages.put(historyMessage); + } + } + + json.put("messages", messages); + + if (configManager.isDebugMode()) { + logger.info("Built request: " + json.toString()); + } + + HttpRequest request = HttpRequest.post(configManager.getBaseUrl() + "/chat/completions") + .header("Content-Type", "application/json; charset=UTF-8") + .header("Authorization", "Bearer " + configManager.getApiKey()) + .bodyText(json.toString()); + + if (configManager.isDebugMode()) { + logger.info("Sending request to ChatGPT: " + request.toString()); + } + + CompletableFuture.supplyAsync(() -> request.send()) + .thenAccept(response -> { + if (configManager.isDebugMode()) { + logger.info("Received response from ChatGPT: " + response.toString()); + } + if (response.statusCode() == 200) { + processSuccessResponse(response, sender, conversationContext, contextEnabled, currentCharacter); + } else { + processErrorResponse(response, sender); + } + }) + .exceptionally(e -> { + logger.log(Level.SEVERE, "Exception occurred while processing request: " + e.getMessage(), e); + sender.sendMessage(configManager.getChatGPTErrorMessage()); + return null; + }); + } + + /** + * 处理成功的API响应 + * + * @param response HTTP响应 + * @param sender 命令发送者 + * @param conversationContext 对话上下文 + * @param contextEnabled 是否启用上下文 + * @param currentCharacter 当前角色 + */ + private void processSuccessResponse(HttpResponse response, CommandSender sender, + ConversationContext conversationContext, + boolean contextEnabled, String currentCharacter) { + String responseBody = response.bodyText(); + String utf8ResponseBody = new String(responseBody.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + try { + JSONObject jsonResponse = new JSONObject(utf8ResponseBody); + String answer = jsonResponse.getJSONArray("choices").getJSONObject(0).getJSONObject("message").getString("content"); + sender.sendMessage(configManager.getChatGPTResponseMessage().replaceFirst("%s", currentCharacter).replaceFirst("%s", answer)); + if (contextEnabled) { + conversationContext.addMessage(answer); // 仅在启用上下文时添加AI响应到历史记录 + } + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to parse ChatGPT response: " + e.getMessage(), e); + sender.sendMessage(configManager.getChatGPTErrorMessage()); + } + } + + /** + * 处理错误的API响应 + * + * @param response HTTP响应 + * @param sender 命令发送者 + */ + private void processErrorResponse(HttpResponse response, CommandSender sender) { + String errorBody = response.bodyText(); + logger.log(Level.SEVERE, "Failed to get a response from ChatGPT: " + errorBody); + sender.sendMessage(configManager.getChatGPTErrorMessage()); + } + + /** + * 将字符串转换为UTF-8编码 + * + * @param input 输入字符串 + * @return UTF-8编码的字符串 + */ + private String convertToUTF8(String input) { + try { + byte[] bytes = input.getBytes(StandardCharsets.UTF_8); + return new String(bytes, StandardCharsets.UTF_8); + } catch (Exception e) { + logger.severe("Failed to convert input to UTF-8: " + e.getMessage()); + return input; // 如果转换失败,返回原始输入 + } + } +} \ No newline at end of file diff --git a/src/main/java/com/ddaodan/MineChatGPT/service/CommandService.java b/src/main/java/com/ddaodan/MineChatGPT/service/CommandService.java new file mode 100644 index 0000000..08f7cda --- /dev/null +++ b/src/main/java/com/ddaodan/MineChatGPT/service/CommandService.java @@ -0,0 +1,191 @@ +package com.ddaodan.MineChatGPT.service; + +import com.ddaodan.MineChatGPT.ConfigManager; +import com.ddaodan.MineChatGPT.ConversationContext; +import org.bukkit.command.CommandSender; + +import java.util.List; +import java.util.Map; + +/** + * 命令处理服务类,负责处理各种命令的业务逻辑 + */ +public class CommandService { + private final ConfigManager configManager; + private final ApiService apiService; + private final UserSessionManager sessionManager; + + public CommandService(ConfigManager configManager, ApiService apiService, UserSessionManager sessionManager) { + this.configManager = configManager; + this.apiService = apiService; + this.sessionManager = sessionManager; + } + + /** + * 处理重载配置命令 + * + * @param sender 命令发送者 + * @return 是否成功处理 + */ + public boolean handleReloadCommand(CommandSender sender) { + if (!sender.hasPermission("minechatgpt.reload")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.reload")); + return true; + } + configManager.reloadConfig(); + sender.sendMessage(configManager.getReloadMessage()); + return true; + } + + /** + * 处理模型切换命令 + * + * @param sender 命令发送者 + * @param args 命令参数 + * @return 是否成功处理 + */ + public boolean handleModelCommand(CommandSender sender, String[] args) { + if (!sender.hasPermission("minechatgpt.model")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.model")); + return true; + } + if (args.length < 2) { + String currentModel = configManager.getCurrentModel(); + sender.sendMessage(configManager.getCurrentModelInfoMessage().replace("%s", currentModel)); + return true; + } + String model = args[1]; + List models = configManager.getModels(); + if (models.contains(model)) { + configManager.setCurrentModel(model); + sender.sendMessage(configManager.getModelSwitchMessage().replace("%s", model)); + } else { + sender.sendMessage(configManager.getInvalidModelMessage()); + } + return true; + } + + /** + * 处理模型列表命令 + * + * @param sender 命令发送者 + * @return 是否成功处理 + */ + public boolean handleModelListCommand(CommandSender sender) { + if (!sender.hasPermission("minechatgpt.modellist")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.modellist")); + return true; + } + List models = configManager.getModels(); + sender.sendMessage(configManager.getAvailableModelsMessage()); + for (String model : models) { + sender.sendMessage("- " + model); + } + return true; + } + + /** + * 处理上下文切换命令 + * + * @param sender 命令发送者 + * @param userId 用户ID + * @return 是否成功处理 + */ + public boolean handleContextCommand(CommandSender sender, String userId) { + boolean contextEnabled = !sessionManager.isContextEnabled(userId); + sessionManager.setContextEnabled(userId, contextEnabled); + String status = contextEnabled ? configManager.getContextToggleEnabledMessage() : configManager.getContextToggleDisabledMessage(); + sender.sendMessage(configManager.getContextToggleMessage().replace("%s", status)); + return true; + } + + /** + * 处理清除历史记录命令 + * + * @param sender 命令发送者 + * @param userId 用户ID + * @return 是否成功处理 + */ + public boolean handleClearCommand(CommandSender sender, String userId) { + if (!sender.hasPermission("minechatgpt.clear")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.clear")); + return true; + } + sessionManager.clearConversationHistory(userId); + sender.sendMessage(configManager.getClearMessage()); + return true; + } + + /** + * 处理角色切换命令 + * + * @param sender 命令发送者 + * @param args 命令参数 + * @param userId 用户ID + * @return 是否成功处理 + */ + public boolean handleCharacterCommand(CommandSender sender, String[] args, String userId) { + if (!sender.hasPermission("minechatgpt.character")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.character")); + return true; + } + Map characters = configManager.getCharacters(); + if (args.length < 2) { + sender.sendMessage(configManager.getAvailableCharactersMessage()); + for (String character : characters.keySet()) { + sender.sendMessage("- " + character); + } + return true; + } + String character = args[1]; + if (characters.containsKey(character)) { + sessionManager.setCurrentCharacter(userId, character); + sender.sendMessage(configManager.getCharacterSwitchedMessage().replace("%s", character)); + } else { + sender.sendMessage(configManager.getInvalidCharacterMessage()); + } + return true; + } + + /** + * 处理向ChatGPT提问的命令 + * + * @param sender 命令发送者 + * @param args 命令参数 + * @param userId 用户ID + * @return 是否成功处理 + */ + public boolean handleAskCommand(CommandSender sender, String[] args, String userId) { + if (!sender.hasPermission("minechatgpt.use")) { + sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.use")); + return true; + } + String question = String.join(" ", args); + ConversationContext conversationContext = sessionManager.getConversationContext(userId); + boolean contextEnabled = sessionManager.isContextEnabled(userId); + + if (contextEnabled) { + conversationContext.addMessage(question); + } + + sender.sendMessage(configManager.getQuestionMessage().replace("%s", question)); + apiService.askChatGPT(sender, question, conversationContext, contextEnabled, userId); + return true; + } + + /** + * 发送帮助信息 + * + * @param sender 命令发送者 + */ + public void sendHelpMessage(CommandSender sender) { + sender.sendMessage(configManager.getHelpMessage()); + sender.sendMessage(configManager.getHelpAskMessage()); + sender.sendMessage(configManager.getHelpReloadMessage()); + sender.sendMessage(configManager.getHelpModelMessage()); + sender.sendMessage(configManager.getHelpModelListMessage()); + sender.sendMessage(configManager.getHelpContextMessage()); + sender.sendMessage(configManager.getHelpClearMessage()); + sender.sendMessage(configManager.getHelpCharacterMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/com/ddaodan/MineChatGPT/service/UserSessionManager.java b/src/main/java/com/ddaodan/MineChatGPT/service/UserSessionManager.java new file mode 100644 index 0000000..b7653e6 --- /dev/null +++ b/src/main/java/com/ddaodan/MineChatGPT/service/UserSessionManager.java @@ -0,0 +1,95 @@ +package com.ddaodan.MineChatGPT.service; + +import com.ddaodan.MineChatGPT.ConfigManager; +import com.ddaodan.MineChatGPT.ConversationContext; + +import java.util.HashMap; +import java.util.Map; + +/** + * 用户会话管理类,负责管理用户的会话状态和上下文 + */ +public class UserSessionManager { + private final ConfigManager configManager; + private final Map userContexts; + private final Map userContextEnabled; + private final Map userCurrentCharacter; + + public UserSessionManager(ConfigManager configManager) { + this.configManager = configManager; + this.userContexts = new HashMap<>(); + this.userContextEnabled = new HashMap<>(); + this.userCurrentCharacter = new HashMap<>(); + } + + /** + * 获取用户的对话上下文 + * + * @param userId 用户ID + * @return 对话上下文 + */ + public ConversationContext getConversationContext(String userId) { + if (!userContexts.containsKey(userId)) { + userContexts.put(userId, new ConversationContext(configManager.getMaxHistorySize())); + userContextEnabled.put(userId, configManager.isContextEnabled()); + } + return userContexts.get(userId); + } + + /** + * 判断用户是否启用了上下文 + * + * @param userId 用户ID + * @return 是否启用上下文 + */ + public boolean isContextEnabled(String userId) { + if (!userContextEnabled.containsKey(userId)) { + userContextEnabled.put(userId, configManager.isContextEnabled()); + } + return userContextEnabled.get(userId); + } + + /** + * 设置用户的上下文启用状态 + * + * @param userId 用户ID + * @param enabled 是否启用 + */ + public void setContextEnabled(String userId, boolean enabled) { + userContextEnabled.put(userId, enabled); + } + + /** + * 清除用户的对话历史 + * + * @param userId 用户ID + */ + public void clearConversationHistory(String userId) { + ConversationContext context = getConversationContext(userId); + context.clearHistory(); + } + + /** + * 获取用户当前使用的角色 + * + * @param userId 用户ID + * @return 角色名称 + */ + public String getCurrentCharacter(String userId) { + if (!userCurrentCharacter.containsKey(userId)) { + userCurrentCharacter.put(userId, configManager.getCurrentCharacter(userId)); + } + return userCurrentCharacter.get(userId); + } + + /** + * 设置用户当前使用的角色 + * + * @param userId 用户ID + * @param character 角色名称 + */ + public void setCurrentCharacter(String userId, String character) { + userCurrentCharacter.put(userId, character); + configManager.setCurrentCharacter(userId, character); + } +} \ No newline at end of file diff --git a/src/main/java/com/ddaodan/MineChatGPT/util/ChatUtils.java b/src/main/java/com/ddaodan/MineChatGPT/util/ChatUtils.java new file mode 100644 index 0000000..d48bdbd --- /dev/null +++ b/src/main/java/com/ddaodan/MineChatGPT/util/ChatUtils.java @@ -0,0 +1,9 @@ +package com.ddaodan.MineChatGPT.util; + +import org.bukkit.ChatColor; + +public class ChatUtils { + public static String translateColorCodes(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b7b77d1..53992f2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,65 +1,104 @@ -# OpenAI API key setting +# ====================================================== +# Language Settings +# 语言设置 +# ====================================================== +# Available languages: en, zh +# 可用语言:en(英文), zh(中文) +language: "en" +# ====================================================== +# API Configuration +# API 设置 +# ====================================================== api: - # Your OpenAI API key, used for authentication + # Your OpenAI API keys, used for authentication # To obtain an API key, visit https://platform.openai.com/account/api-keys and create a new API key - key: "sk-your_openai_api_key" + # 你的 OpenAI API key,用于身份验证 + # 获取 API key 的方法:访问 https://platform.openai.com/account/api-keys 并创建一个新的 API key + keys: + - "sk-your_openai_api_key_1" + # You can add multiple API keys below + # 可以添加多个API key + # - "sk-your_openai_api_key_2" + # - "sk-your_openai_api_key_3" + + # API key selection method: "round_robin" or "random" + # Round Robin: Use each API key in turn + # Random: Randomly select an API key + # API key 选择方法:"round_robin"(轮询)或 "random"(随机) + # 轮询:依次使用每个API key + # 随机:随机选择一个API key + selection_method: "round_robin" + # The base URL for the OpenAI API, used to construct requests + # If you cannot access the official API, you can use a proxy service + # OpenAI API 的基础 URL,用于构建请求 + # 如果你无法访问官方API,可以使用代理服务 base_url: "https://api.openai.com/v1" +# ====================================================== +# Model Configuration +# 模型设置 +# ====================================================== # List of supported models +# 支持的模型列表 models: # OpenAI ChatGPT - - "gpt-3.5-turbo" - - "gpt-3.5-turbo-instruct" - "gpt-4" - "gpt-4-turbo" - "gpt-4-turbo-preview" - "gpt-4o" - "gpt-4o-mini" - # Google Gemini - # - "gemini-pro" - # - "gemini-1.5-pro" # Anthropic Claude # - "claude-3-opus" # - "claude-3-5-sonnet" + # Deepseek + # - "deepseek-coder" + # - "deepseek-reasoner" # And more... + # 以及更多... + # The default model to use -default_model: "gpt-3.5-turbo" -# Conversation setting +# 默认使用的模型 +default_model: "gpt-4o-mini" +# ====================================================== +# Conversation Settings +# 对话设置 +# ====================================================== conversation: # Continuous conversation switch + # When enabled, the plugin will remember the conversation history + # 连续对话开关 + # 启用时,插件将记住对话历史 context_enabled: false + # Maximum number of historical records retained + # Increasing this value will consume more memory + # 最大历史记录保留数量 + # 增加此值将消耗更多内存 max_history_size: 10 -# Characters setting +# ====================================================== +# Character Settings +# 角色设置 +# ====================================================== characters: # Format: # Character Name: "Character Prompt" + # The prompt will be sent to the AI as a system message + # 格式: + # 角色名称: "角色提示词" + # 提示词将作为系统消息发送给AI + + # Default character ChatGPT: "You are a helpful assistant." -# Message settings -messages: - reload: "&aConfiguration reloaded successfully!" - clear: "&aConversation history has been cleared!" - help: "&e===== MineChatGPT Help =====" - help_ask: "&e/chatgpt - Ask ChatGPT a question." - help_reload: "&e/chatgpt reload - Reload the configuration file." - help_model: "&e/chatgpt model - Switch to a different model." - help_modellist: "&e/chatgpt modellist - List available models." - help_context: "&e/chatgpt context - Toggle context mode." - help_clear: "&e/chatgpt clear - Clear conversation history." - help_character: "&e/chatgpt character [character_name] - List or switch to a character." - context_toggle: "&eContext is now %s." - context_toggle_enabled: "&aenabled" - context_toggle_disabled: "&cdisabled" - current_model_info: "&eCurrent model: %s. Use /chatgpt model to switch models." - model_switch: "&aModel switched to %s" - chatgpt_error: "&cFailed to contact ChatGPT." - chatgpt_response: "&b%s: %s" - question: "&bYou: %s" - character_switched: "&aSwitched to character: %s" - available_characters: "&eAvailable characters:" - invalid_character: "&cInvalid character. Use /chatgpt character to list available characters." - invalid_model: "&cInvalid model. Use /chatgpt modellist to see available models." - available_models: "&eAvailable models:" - no_permission: "&cYou do not have permission to use this command. Required permission: %s" + + # You can add more characters below + # 你可以在下方添加更多角色 + # Example: + # Minecraft: "You are a Minecraft expert who helps players with game mechanics and building ideas." + +# ====================================================== +# Other Settings +# 其他设置 +# ====================================================== # If you don't know what this is, don't change it +# 如果你不知道这是什么,请不要动 debug: false \ No newline at end of file diff --git a/src/main/resources/lang/en.yml b/src/main/resources/lang/en.yml new file mode 100644 index 0000000..a390cf7 --- /dev/null +++ b/src/main/resources/lang/en.yml @@ -0,0 +1,27 @@ +# English language file for MineChatGPT + +messages: + reload: "&aConfiguration reloaded successfully!" + clear: "&aConversation history has been cleared!" + help: "&e===== MineChatGPT Help =====" + help_ask: "&e/chatgpt - Ask ChatGPT a question." + help_reload: "&e/chatgpt reload - Reload the configuration file." + help_model: "&e/chatgpt model - Switch to a different model." + help_modellist: "&e/chatgpt modellist - List available models." + help_context: "&e/chatgpt context - Toggle context mode." + help_clear: "&e/chatgpt clear - Clear conversation history." + help_character: "&e/chatgpt character [character_name] - List or switch to a character." + context_toggle: "&eContext is now %s." + context_toggle_enabled: "&aenabled" + context_toggle_disabled: "&cdisabled" + current_model_info: "&eCurrent model: %s. Use /chatgpt model to switch models." + model_switch: "&aModel switched to %s" + chatgpt_error: "&cFailed to contact ChatGPT." + chatgpt_response: "&b%s: %s" + question: "&bYou: %s" + character_switched: "&aSwitched to character: %s" + available_characters: "&eAvailable characters:" + invalid_character: "&cInvalid character. Use /chatgpt character to list available characters." + invalid_model: "&cInvalid model. Use /chatgpt modellist to see available models." + available_models: "&eAvailable models:" + no_permission: "&cYou do not have permission to use this command. Required permission: %s" \ No newline at end of file diff --git a/src/main/resources/config_zh.yml b/src/main/resources/lang/zh.yml similarity index 56% rename from src/main/resources/config_zh.yml rename to src/main/resources/lang/zh.yml index 666d686..0df8c19 100644 --- a/src/main/resources/config_zh.yml +++ b/src/main/resources/lang/zh.yml @@ -1,41 +1,5 @@ -# API 相关设置 -api: - # 你的 OpenAI API key,用于身份验证 - # 获取 API key 的方法:访问 https://platform.openai.com/account/api-keys 并创建一个新的 API key - key: "sk-your_openai_api_key" - # OpenAI API 的基础 URL,用于构建请求 - base_url: "https://api.openai.com/v1" -# 支持的模型列表 -models: - # OpenAI ChatGPT - - "gpt-3.5-turbo" - - "gpt-3.5-turbo-instruct" - - "gpt-4" - - "gpt-4-turbo" - - "gpt-4-turbo-preview" - - "gpt-4o" - - "gpt-4o-mini" - # Google Gemini - # - "gemini-pro" - # - "gemini-1.5-pro" - # Anthropic Claude - # - "claude-3-opus" - # - "claude-3-5-sonnet" - # 以及更多... -# 默认使用的模型 -default_model: "gpt-3.5-turbo" -# 连续对话设置 -conversation: - # 连续对话开关 - context_enabled: false - # 最大历史记录保留数量 - max_history_size: 10 -# 角色设置 -characters: - # 格式: - # 角色名称: "角色提示词" - ChatGPT: "You are a helpful assistant." -# 消息相关设置 +# 中文语言文件 - MineChatGPT + messages: reload: "&a已重新加载配置文件!" clear: "&a对话历史已清空!" @@ -60,6 +24,4 @@ messages: invalid_character: "&c无效的角色。使用 /chatgpt character 查看所有可用的角色。" invalid_model: "&c模型无效。使用 /chatgpt modellist 查看可用模型。" available_models: "&e可用模型列表:" - no_permission: "&c你没有权限使用这个指令。需要的权限:%s" -# 如果你不知道这是什么,请不要动 -debug: false \ No newline at end of file + no_permission: "&c你没有权限使用这个指令。需要的权限:%s" \ No newline at end of file