forked from Upstream/Ollama-Chat
Compare commits
5 Commits
main
...
feature-ch
Author | SHA1 | Date | |
---|---|---|---|
|
deee11d8c8 | ||
|
482a6332f2 | ||
|
22f072f738 | ||
|
a32a14c178 | ||
|
6ae6f401ff |
@ -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:
|
||||
|
@ -3,7 +3,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = 'com'
|
||||
version = '1.0'
|
||||
version = '1.0.2-rc2'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -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 {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
27
src/main/java/com/ollamachat/ChatHistoryManager.java
Normal file
27
src/main/java/com/ollamachat/ChatHistoryManager.java
Normal 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);
|
||||
}
|
||||
}
|
93
src/main/java/com/ollamachat/DatabaseManager.java
Normal file
93
src/main/java/com/ollamachat/DatabaseManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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) + "...";
|
||||
|
@ -10,6 +10,9 @@ response-prefix: "§b[AI] §r"
|
||||
# Length
|
||||
max-response-length: 500
|
||||
|
||||
# History
|
||||
max-history: 5
|
||||
|
||||
# Language Settings
|
||||
language: "en"
|
||||
|
||||
|
@ -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..."
|
@ -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},正在重新生成默认配置..."
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user