Compare commits

...

5 Commits

Author SHA1 Message Date
Sar
deee11d8c8
Update README.md 2025-02-26 22:04:01 +08:00
Sar
482a6332f2
Update 2025-02-26 20:58:03 +08:00
Sar
22f072f738
feat(update_lang) 2025-02-26 20:45:22 +08:00
Sar
a32a14c178
Update 2025-02-26 19:41:48 +08:00
Sar
6ae6f401ff
feat(chat_history)
[Test]Add chat history save and call functionality (#issue-1)
2025-02-26 15:18:29 +08:00
10 changed files with 239 additions and 13 deletions

View File

@ -61,8 +61,11 @@ response-prefix: "§b[AI] §r"
# Length
max-response-length: 500
# History
max-history: 5
# Language Settings
language: "en" # Default language (en or zh_cn)
language: "en" # Default language (en or zh_cn)
# Other AI Configurations
other-ai-configs:

View File

@ -3,7 +3,7 @@ plugins {
}
group = 'com'
version = '1.0'
version = '1.0.2-rc2'
repositories {
mavenCentral()

View File

@ -1,6 +1,7 @@
package com.ollamachat;
import com.google.gson.Gson;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
@ -56,3 +57,4 @@ public class AIService {
});
}
}

View File

@ -0,0 +1,27 @@
package com.ollamachat;
import org.bukkit.entity.Player;
import java.util.UUID;
public class ChatHistoryManager {
private final DatabaseManager databaseManager;
private final int maxHistory;
public ChatHistoryManager(DatabaseManager databaseManager, int maxHistory) {
this.databaseManager = databaseManager;
this.maxHistory = maxHistory;
}
public void savePlayerInfo(Player player) {
databaseManager.savePlayerInfo(player.getUniqueId(), player.getName());
}
public void saveChatHistory(UUID playerUuid, String aiModel, String prompt, String response) {
databaseManager.saveChatHistory(playerUuid, aiModel, prompt, response);
}
public String getChatHistory(UUID playerUuid, String aiModel) {
return databaseManager.getChatHistory(playerUuid, aiModel, maxHistory);
}
}

View File

@ -0,0 +1,93 @@
package com.ollamachat;
import java.sql.*;
import java.util.UUID;
public class DatabaseManager {
private Connection connection;
public DatabaseManager() {
initializeDatabase();
}
private void initializeDatabase() {
try {
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:plugins/OllamaChat/chat_history.db");
createTables();
} catch (Exception e) {
e.printStackTrace();
}
}
private void createTables() throws SQLException {
try (Statement stmt = connection.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS players (" +
"uuid TEXT PRIMARY KEY," +
"username TEXT NOT NULL)");
stmt.execute("CREATE TABLE IF NOT EXISTS chat_history (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"player_uuid TEXT NOT NULL," +
"ai_model TEXT NOT NULL," +
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," +
"prompt TEXT NOT NULL," +
"response TEXT NOT NULL," +
"FOREIGN KEY (player_uuid) REFERENCES players(uuid))");
}
}
public void savePlayerInfo(UUID uuid, String username) {
String sql = "INSERT OR REPLACE INTO players(uuid, username) VALUES(?, ?)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, uuid.toString());
pstmt.setString(2, username);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void saveChatHistory(UUID playerUuid, String aiModel, String prompt, String response) {
String sql = "INSERT INTO chat_history(player_uuid, ai_model, prompt, response) VALUES(?, ?, ?, ?)";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, playerUuid.toString());
pstmt.setString(2, aiModel);
pstmt.setString(3, prompt);
pstmt.setString(4, response);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public String getChatHistory(UUID playerUuid, String aiModel, int maxHistory) {
StringBuilder history = new StringBuilder();
String sql = "SELECT prompt, response FROM chat_history " +
"WHERE player_uuid = ? AND ai_model = ? " +
"ORDER BY timestamp DESC LIMIT ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setString(1, playerUuid.toString());
pstmt.setString(2, aiModel);
pstmt.setInt(3, maxHistory);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
history.insert(0, "User: " + rs.getString("prompt") + "\n");
history.insert(0, "AI: " + rs.getString("response") + "\n");
}
} catch (SQLException e) {
e.printStackTrace();
}
return history.toString();
}
public void close() {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}

View File

@ -1,6 +1,7 @@
package com.ollamachat;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -10,12 +11,13 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class Ollamachat extends JavaPlugin implements Listener {
@ -31,6 +33,9 @@ public class Ollamachat extends JavaPlugin implements Listener {
private Map<String, Boolean> otherAIEnabled;
private FileConfiguration langConfig;
private DatabaseManager databaseManager;
private ChatHistoryManager chatHistoryManager;
private int maxHistory;
@Override
public void onEnable() {
@ -39,6 +44,12 @@ public class Ollamachat extends JavaPlugin implements Listener {
String language = getConfig().getString("language", "en");
loadLanguageFile(language);
updateCommandUsages();
databaseManager = new DatabaseManager();
maxHistory = getConfig().getInt("max-history", 5);
chatHistoryManager = new ChatHistoryManager(databaseManager, maxHistory);
aiService = new AIService();
gson = new Gson();
@ -47,6 +58,19 @@ public class Ollamachat extends JavaPlugin implements Listener {
getCommand("aichat").setExecutor(this);
}
private void updateCommandUsages() {
String usageOllamachat = getMessage("usage-ollamachat", null);
String usageAichat = getMessage("usage-aichat", null);
getCommand("ollamachat").setUsage(usageOllamachat);
getCommand("aichat").setUsage(usageAichat);
}
@Override
public void onDisable() {
databaseManager.close();
}
private void updateConfig() {
FileConfiguration config = getConfig();
@ -59,11 +83,32 @@ public class Ollamachat extends JavaPlugin implements Listener {
if (!config.contains("other-ai-configs")) {
config.createSection("other-ai-configs");
}
if (!config.contains("max-history")) {
config.set("max-history", 5);
}
saveConfig();
}
private void reloadConfigValues() {
File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
saveDefaultConfig();
} else {
try {
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
if (!config.contains("ollama-api-url") || !config.contains("model")) {
getLogger().warning(getMessage("config-invalid", null));
configFile.delete();
saveDefaultConfig();
}
} catch (Exception e) {
getLogger().severe(getMessage("config-load-failed", Map.of("error", e.getMessage())));
configFile.delete();
saveDefaultConfig();
}
}
reloadConfig();
updateConfig();
@ -73,6 +118,7 @@ public class Ollamachat extends JavaPlugin implements Listener {
triggerPrefix = config.getString("trigger-prefix", "@bot ");
maxResponseLength = config.getInt("max-response-length", 500);
ollamaEnabled = config.getBoolean("ollama-enabled", true);
maxHistory = config.getInt("max-history", 5);
otherAIConfigs = new HashMap<>();
otherAIEnabled = new HashMap<>();
@ -117,6 +163,11 @@ public class Ollamachat extends JavaPlugin implements Listener {
return message;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
chatHistoryManager.savePlayerInfo(event.getPlayer());
}
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event) {
if (!ollamaEnabled) return;
@ -137,8 +188,15 @@ public class Ollamachat extends JavaPlugin implements Listener {
private void processOllamaQueryAsync(Player player, String prompt) {
CompletableFuture.runAsync(() -> {
try {
String responseBody = aiService.sendRequest(ollamaApiUrl, null, ollamaModel, prompt).join();
String history = chatHistoryManager.getChatHistory(player.getUniqueId(), "ollama");
String context = history + "User: " + prompt;
String responseBody = aiService.sendRequest(ollamaApiUrl, null, ollamaModel, context).join();
OllamaResponse ollamaResponse = gson.fromJson(responseBody, OllamaResponse.class);
chatHistoryManager.saveChatHistory(player.getUniqueId(), "ollama", prompt, ollamaResponse.response);
sendFormattedResponse(player, ollamaResponse.response);
} catch (Exception e) {
getLogger().severe("Error processing Ollama request: " + e.getMessage());
@ -155,9 +213,28 @@ public class Ollamachat extends JavaPlugin implements Listener {
CompletableFuture.runAsync(() -> {
try {
String history = chatHistoryManager.getChatHistory(player.getUniqueId(), aiName);
String context = history + "User: " + prompt;
AIConfig aiConfig = otherAIConfigs.get(aiName);
String responseBody = aiService.sendRequest(aiConfig.getApiUrl(), aiConfig.getApiKey(), aiConfig.getModel(), prompt).join();
sendFormattedResponse(player, responseBody);
String responseBody = aiService.sendRequest(
aiConfig.getApiUrl(),
aiConfig.getApiKey(),
aiConfig.getModel(),
context
).join();
String response = parseAIResponse(aiName, responseBody);
chatHistoryManager.saveChatHistory(
player.getUniqueId(),
aiName,
prompt,
response
);
sendFormattedResponse(player, response);
} catch (Exception e) {
getLogger().severe("Error processing " + aiName + " request: " + e.getMessage());
sendErrorMessage(player, getMessage("error-prefix", null) + "Failed to get response from " + aiName);
@ -165,6 +242,19 @@ public class Ollamachat extends JavaPlugin implements Listener {
});
}
private String parseAIResponse(String aiName, String responseBody) {
switch (aiName.toLowerCase()) {
case "openai":
JsonObject json = gson.fromJson(responseBody, JsonObject.class);
return json.getAsJsonArray("choices")
.get(0).getAsJsonObject()
.get("message").getAsJsonObject()
.get("content").getAsString();
default:
return gson.fromJson(responseBody, OllamaResponse.class).response;
}
}
private void sendFormattedResponse(Player player, String response) {
if (response.length() > maxResponseLength) {
response = response.substring(0, maxResponseLength) + "...";

View File

@ -10,6 +10,9 @@ response-prefix: "§b[AI] §r"
# Length
max-response-length: 500
# History
max-history: 5
# Language Settings
language: "en"

View File

@ -4,10 +4,15 @@ toggle-enabled: "§a{ai-name} is now enabled."
toggle-disabled: "§a{ai-name} is now disabled."
invalid-ai-name: "§cInvalid AI name. Available AIs: {ai-list}."
usage-aichat: "§cUsage: /aichat <ai-name> <prompt>"
usage-ollamachat: "§cUsage: /ollamachat <reload|toggle <ai-name>>"
player-only: "§cThis command can only be used by players."
ollama-enabled: "§aOllama is now enabled."
ollama-disabled: "§aOllama is now disabled."
# Chat interaction
response-prefix: "§b[AI] §r"
error-prefix: "§c[Error] §r"
error-prefix: "§c[Error] §r"
# Log messages
config-invalid: "§cConfig file is invalid or incomplete. Regenerating default config..."
config-load-failed: "§cFailed to load config file: {error}. Regenerating default config..."

View File

@ -4,10 +4,15 @@ toggle-enabled: "§a{ai-name} 已启用。"
toggle-disabled: "§a{ai-name} 已禁用。"
invalid-ai-name: "§c无效的 AI 名称。可用的 AI: {ai-list}。"
usage-aichat: "§c用法: /aichat <ai名称> <提示>"
usage-ollamachat: "§c用法: /ollamachat <reload|toggle <ai名称>>"
player-only: "§c该命令只能由玩家使用。"
ollama-enabled: "§aOllama 已启用。"
ollama-disabled: "§aOllama 已禁用。"
# 聊天交互
response-prefix: "§b[AI] §r"
error-prefix: "§c[错误] §r"
error-prefix: "§c[错误] §r"
# 日志消息
config-invalid: "§c配置文件无效或不完整正在重新生成默认配置..."
config-load-failed: "§c加载配置文件失败: {error},正在重新生成默认配置..."

View File

@ -1,5 +1,5 @@
name: ollamachat
version: '1.0.1'
version: '1.0.2-rc2'
main: com.ollamachat.Ollamachat
api-version: '1.21'
authors: [xwwsdd]
@ -9,13 +9,11 @@ website: https://chat.sarskin.cn/invite/iHgI6LTX
commands:
ollamachat:
description: Manage OllamaChat plugin (reload configuration or toggle AI)
usage: |
/ollamachat reload - Reload the plugin configuration
/ollamachat toggle <ai-name> - Enable or disable an AI service
usage: "{usage-ollamachat}"
permission: ollamachat.admin
aichat:
description: Interact with other AI services
usage: /aichat <ai-name> <prompt>
usage: "{usage-aichat}"
permission: ollamachat.use
permissions: