diff --git a/build.gradle b/build.gradle index 843f751f7d..3ea2327421 100644 --- a/build.gradle +++ b/build.gradle @@ -306,6 +306,11 @@ def sharedDeps = { installer "net.kyori:examination-api:1.3.0" installer "net.kyori:examination-string:1.3.0" // Paper end + + annotationProcessor 'org.projectlombok:lombok:1.18.30' + compileOnly 'org.projectlombok:lombok:1.18.30' + installer 'com.konghq:unirest-java-core:4.4.5' + } def sharedFmlonlyForge = { Project prj -> diff --git a/mohistlauncher/src/main/resources/libraries.txt b/mohistlauncher/src/main/resources/libraries.txt index ed961576fd..0f0e65ec1c 100644 --- a/mohistlauncher/src/main/resources/libraries.txt +++ b/mohistlauncher/src/main/resources/libraries.txt @@ -123,4 +123,5 @@ net/kyori/examination-api/1.3.0/examination-api-1.3.0.jar|b1887361d811c89ccca4db net/kyori/examination-string/1.3.0/examination-string-1.3.0.jar|9e4752ea3f53ae45e736c9d8f016f23d|13135|false org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar|40d6b9075fbd28fa10292a45a0db9457|780321|false org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar|2b3991eda121042765a5ee299556c200|328436|false -com/ibm/icu/icu4j/71.1/icu4j-71.1.jar|f59e87e2ed829e3d136a9184e147c50d|13963762|false \ No newline at end of file +com/ibm/icu/icu4j/71.1/icu4j-71.1.jar|f59e87e2ed829e3d136a9184e147c50d|13963762|false +com/konghq/unirest-java-core/4.4.5/unirest-java-core-4.4.5.jar|831b5f6f99bf75c24e22cc7d754fb133|197342|false \ No newline at end of file diff --git a/patches/minecraft/net/minecraft/server/level/ServerPlayer.java.patch b/patches/minecraft/net/minecraft/server/level/ServerPlayer.java.patch index e29bba2c44..a6900b6fca 100644 --- a/patches/minecraft/net/minecraft/server/level/ServerPlayer.java.patch +++ b/patches/minecraft/net/minecraft/server/level/ServerPlayer.java.patch @@ -1,9 +1,10 @@ --- a/net/minecraft/server/level/ServerPlayer.java +++ b/net/minecraft/server/level/ServerPlayer.java -@@ -2,19 +_,29 @@ +@@ -2,19 +_,30 @@ import com.google.common.collect.Lists; import com.google.common.net.InetAddresses; ++import com.mohistmc.ai.koukou.KouKou; +import com.mohistmc.bukkit.inventory.MohistModsInventory; +import com.mohistmc.paper.adventure.PaperAdventure; +import com.mohistmc.paper.event.packet.PlayerChunkLoadEvent; @@ -461,7 +462,6 @@ + } + + Component defaultMessage = this.m_21231_().m_19293_(); -+ String deathmessage = defaultMessage.getString(); + + keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel + PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, PaperAdventure.asAdventure(defaultMessage), defaultMessage.getString(), keepInventory); // Paper - Adventure // Mohist - Mohist PaperAdventure @@ -514,6 +514,7 @@ } else { - this.f_8924_.m_6846_().m_240416_(component, false); + this.f_8924_.m_6846_().m_240416_(ichatbasecomponent, false); ++ KouKou.sendToGroup(ichatbasecomponent.getString()); } } else { this.f_8906_.m_9829_(new ClientboundPlayerCombatKillPacket(this.m_19879_(), CommonComponents.f_237098_)); diff --git a/src/main/java/com/mohistmc/ai/koukou/AIConfig.java b/src/main/java/com/mohistmc/ai/koukou/AIConfig.java new file mode 100644 index 0000000000..156a785b52 --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/AIConfig.java @@ -0,0 +1,70 @@ +package com.mohistmc.ai.koukou; + +import com.mohistmc.plugins.config.MohistPluginConfig; +import java.io.File; +import java.util.List; + +public class AIConfig extends MohistPluginConfig { + + public static AIConfig INSTANCE; + + public AIConfig(File file) { + super(file); + } + + public static void init() { + INSTANCE = new AIConfig(new File("mohist-config", "qq.yml")); + INSTANCE.yaml.addDefault("enable", false); + INSTANCE.yaml.addDefault("debug", false); + INSTANCE.yaml.addDefault("server_name", "群消息"); + INSTANCE.yaml.addDefault("post_server", "http://0.0.0.0:3000"); + INSTANCE.yaml.addDefault("http_server.hostname", "0.0.0.0"); + INSTANCE.yaml.addDefault("http_server.port", 2025); + INSTANCE.yaml.addDefault("chat_post_group", List.of("123456789")); + INSTANCE.yaml.addDefault("command.enable", false); + INSTANCE.yaml.addDefault("command.owners", List.of("123456789")); + INSTANCE.yaml.addDefault("command.name", "执行"); + INSTANCE.save(); + } + + public boolean enable() { + return yaml.getBoolean("enable", false); + } + + public boolean debug() { + return yaml.getBoolean("debug", false); + } + + public String post_server() { + return yaml.getString("post_server", "http://0.0.0.0:3000"); + } + + public String http_server_hostname() { + return yaml.getString("http_server.hostname", "0.0.0.0"); + } + + public int http_server_port() { + return yaml.getInt("http_server.port", 2025); + } + + public List chat_post_group() { + return yaml.getStringList("chat_post_group"); + } + + public boolean command_enable() { + return yaml.getBoolean("command.enable"); + } + + public List command_owners() { + return yaml.getStringList("command.owners"); + } + + public String command_name() { + return yaml.getString("command.name", "执行"); + } + + public String server_name() { + return yaml.getString("server_name", "群消息"); + } + +} diff --git a/src/main/java/com/mohistmc/ai/koukou/ApiController.java b/src/main/java/com/mohistmc/ai/koukou/ApiController.java new file mode 100644 index 0000000000..3b9befe5fb --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/ApiController.java @@ -0,0 +1,34 @@ +package com.mohistmc.ai.koukou; + +import com.mohistmc.ai.koukou.network.MyHttpHandler; +import com.mohistmc.ai.koukou.network.event.ListenRegister; +import com.sun.net.httpserver.HttpServer; +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; +import lombok.SneakyThrows; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ApiController { + + public static ListenRegister eventBus = ListenRegister.getInstance(); + public static Logger LOGGER = LogManager.getLogger("Mohist Http Server"); + + public static void init() { + if (!AIConfig.INSTANCE.enable()) return; + ApiController api = new ApiController(); + api.start(); + eventBus.registerListener(new KouKouPostListener()); + } + + @SneakyThrows + public void start() { + var host = AIConfig.INSTANCE.http_server_hostname(); + var port = AIConfig.INSTANCE.http_server_port(); + HttpServer server = HttpServer.create(new InetSocketAddress(host, port), 0); + server.createContext("/", new MyHttpHandler()); + server.setExecutor(Executors.newFixedThreadPool(5)); + server.start(); + LOGGER.info("已部署AI服务 {}:{}", host, port); + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/KouKou.java b/src/main/java/com/mohistmc/ai/koukou/KouKou.java new file mode 100644 index 0000000000..8abde113a0 --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/KouKou.java @@ -0,0 +1,45 @@ +package com.mohistmc.ai.koukou; + +import com.mohistmc.ai.koukou.network.HttpRequestUtils; +import com.mohistmc.mjson.Json; +import java.util.HashMap; +import java.util.Objects; +import lombok.SneakyThrows; + +public class KouKou { + + public static void sendToGroup(String message) { + if (!AIConfig.INSTANCE.enable()) return; + for (String groupId : AIConfig.INSTANCE.chat_post_group()) { + send_group_msg(String.valueOf(groupId), message); + } + } + + public static void sendToGroup(String groupId, String message) { + if (!AIConfig.INSTANCE.enable()) return; + send_group_msg(String.valueOf(groupId), message); + } + + @SneakyThrows + public static void send_group_msg(String group_id, String message) { + HashMap param = new HashMap<>(); + param.put("group_id", group_id); + param.put("message", message); + var string = HttpRequestUtils.post("/send_group_msg", param); + if (string == null) { + debug("string == null"); + return; + } + debug(string); + var json = Json.read(string); + if (Objects.equals(json.asString("status"), "failed")) { + debug("发送失败"); + return; + } + debug("返回数据: " + json); + } + + public static void debug(String debug_message) { + if (AIConfig.INSTANCE.debug()) ApiController.LOGGER.info(debug_message); + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/KouKouPostListener.java b/src/main/java/com/mohistmc/ai/koukou/KouKouPostListener.java new file mode 100644 index 0000000000..b822318f7b --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/KouKouPostListener.java @@ -0,0 +1,47 @@ +package com.mohistmc.ai.koukou; + +import com.mohistmc.ai.koukou.entity.MessageRequest; +import com.mohistmc.ai.koukou.network.event.BaseListener; +import com.mohistmc.ai.koukou.network.event.HttpPostEvent; +import com.mohistmc.mjson.Json; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import lombok.SneakyThrows; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R1.command.ColouredConsoleSender; + +public class KouKouPostListener implements BaseListener { + + @SneakyThrows + public void onEvent(HttpPostEvent event) { + Json json = event.getJson(); + if (event.isQQ()) { + MessageRequest request = json.asBean(MessageRequest.class); + String t = request.getMessage_type(); + var group = request.getGroup_id(); + String msg = request.getRaw_message(); + String sender_nickname = request.getSender().getNickname(); + if (t != null && t.equals("group")) { + if (AIConfig.INSTANCE.chat_post_group().contains(String.valueOf(group))) { + if (AIConfig.INSTANCE.command_enable() && AIConfig.INSTANCE.command_owners().contains(String.valueOf(request.getUser_id()))) { + String label = AIConfig.INSTANCE.command_name(); + if (msg.startsWith(label)) { + String cmd = msg.replace(label + " ", ""); + Bukkit.dispatchCommand(new ColouredConsoleSender().group(group), cmd); + return; + } + } + msg = msg.replaceAll("§\\S", ""); + msg = msg.replace("__color__", "§"); + String pattern = "\\[CQ:.*?]"; + Pattern r = Pattern.compile(pattern); + Matcher m = r.matcher(msg); + if (!m.find()) { + Bukkit.broadcastMessage("[%s] <%s>: %s".formatted(AIConfig.INSTANCE.server_name(), sender_nickname, msg)); + } + + } + } + } + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/entity/MessageRequest.java b/src/main/java/com/mohistmc/ai/koukou/entity/MessageRequest.java new file mode 100644 index 0000000000..843f6beec0 --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/entity/MessageRequest.java @@ -0,0 +1,76 @@ +package com.mohistmc.ai.koukou.entity; + +import com.mohistmc.mjson.ToJson; +import lombok.Data; + +@Data +public class MessageRequest { + @ToJson + private long self_id; + @ToJson + private long user_id; + @ToJson + private long time; + @ToJson + private long message_id; + @ToJson + private long real_id; + @ToJson + private String message_type; + @ToJson + private Sender sender; + @ToJson + private String raw_message; + @ToJson + private long font; + @ToJson + private String sub_type; + @ToJson + private Message[] message; + @ToJson + private String message_format; + @ToJson + private String post_type; + @ToJson + private long group_id; + @ToJson + private String card_new; + + @lombok.Data + public static class Message { + @ToJson + private Data data; + @ToJson + private String type; + } + + @lombok.Data + public static class Data { + @ToJson + private String text; + @ToJson + private String id; + @ToJson + private String file; + @ToJson + private String file_id; + @ToJson + private String url; + @ToJson + private String file_size; + } + + @lombok.Data + public static class Sender { + @ToJson + private long user_id; + @ToJson + private String nickname; + @ToJson + private String card; + @ToJson + private String role; + } +} + + diff --git a/src/main/java/com/mohistmc/ai/koukou/network/HttpRequestUtils.java b/src/main/java/com/mohistmc/ai/koukou/network/HttpRequestUtils.java new file mode 100644 index 0000000000..4d4303d81e --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/HttpRequestUtils.java @@ -0,0 +1,23 @@ +package com.mohistmc.ai.koukou.network; + +import com.mohistmc.ai.koukou.AIConfig; +import com.mohistmc.mjson.Json; +import java.util.Map; +import kong.unirest.core.HttpResponse; +import kong.unirest.core.Unirest; +import lombok.SneakyThrows; + +public class HttpRequestUtils { + + @SneakyThrows + public static String post(String path, Map body) { + var json = Json.read(body); + String url = AIConfig.INSTANCE.post_server() + path; + HttpResponse response = Unirest.post(url) + .header("User-Agent", "Mohist") + .header("Content-Type", "application/json") + .body(json.toString()) + .asString(); + return response.getBody(); + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/network/MyHttpHandler.java b/src/main/java/com/mohistmc/ai/koukou/network/MyHttpHandler.java new file mode 100644 index 0000000000..2f14a9d01c --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/MyHttpHandler.java @@ -0,0 +1,58 @@ +package com.mohistmc.ai.koukou.network; + +import com.mohistmc.ai.koukou.ApiController; +import com.mohistmc.ai.koukou.network.event.HttpPostEvent; +import com.mohistmc.mjson.Json; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import lombok.SneakyThrows; + +public class MyHttpHandler implements HttpHandler { + @Override + public void handle(HttpExchange t) throws IOException { + String requestMethod = t.getRequestMethod(); + String requestPath = t.getRequestURI().getPath(); + RequestPath p = RequestPath.as(requestPath); + if (requestMethod.equalsIgnoreCase("POST")) { + t.getResponseHeaders().add("Content-Type", "application/json; charset=UTF-8"); + if (p.isUnknown()) { + close(t); + } else { + post(t, p); + } + } else { + close(t); + } + } + + @SneakyThrows + private void close(HttpExchange t) { + t.sendResponseHeaders(404, 0); + OutputStream os = t.getResponseBody(); + os.close(); + } + + @SneakyThrows + private void post(HttpExchange t, RequestPath path) { + StringBuilder requestBody = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(t.getRequestBody(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + requestBody.append(line); + } + } + Json json = Json.read(requestBody.toString()); + HttpPostEvent event = new HttpPostEvent(this, json, path); + ApiController.eventBus.onEvent(event); + byte[] responseBytes = json.toString().getBytes(StandardCharsets.UTF_8); + t.sendResponseHeaders(200, responseBytes.length); + OutputStream os = t.getResponseBody(); + os.write(responseBytes); + os.close(); + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/network/RequestPath.java b/src/main/java/com/mohistmc/ai/koukou/network/RequestPath.java new file mode 100644 index 0000000000..5b9cfc3709 --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/RequestPath.java @@ -0,0 +1,36 @@ +package com.mohistmc.ai.koukou.network; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +@Getter +public enum RequestPath { + DEFAULT("/"), + QQ("/qq"), + UNKNOWN(null); + + private static final Map parse = new HashMap<>(); + + static { + for (RequestPath r : RequestPath.values()) { + if (r.path != null) { + parse.put(r.path, r); + } + } + } + + final String path; + + RequestPath(String path) { + this.path = path; + } + + public static RequestPath as(String path) { + return parse.getOrDefault(path, UNKNOWN); + } + + public boolean isUnknown() { + return this == UNKNOWN; + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/network/event/BaseListener.java b/src/main/java/com/mohistmc/ai/koukou/network/event/BaseListener.java new file mode 100644 index 0000000000..e4e2074f2c --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/event/BaseListener.java @@ -0,0 +1,7 @@ +package com.mohistmc.ai.koukou.network.event; + +import java.util.EventListener; + +public interface BaseListener extends EventListener { + void onEvent(HttpPostEvent e); +} diff --git a/src/main/java/com/mohistmc/ai/koukou/network/event/HttpPostEvent.java b/src/main/java/com/mohistmc/ai/koukou/network/event/HttpPostEvent.java new file mode 100644 index 0000000000..27d3b7b84c --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/event/HttpPostEvent.java @@ -0,0 +1,23 @@ +package com.mohistmc.ai.koukou.network.event; + +import com.mohistmc.ai.koukou.network.RequestPath; +import com.mohistmc.mjson.Json; +import java.util.EventObject; +import lombok.Getter; + +@Getter +public class HttpPostEvent extends EventObject { + + private final Json json; + private final RequestPath requestPath; + + public HttpPostEvent(Object source, Json json, RequestPath requestPath) { + super(source); + this.json = json; + this.requestPath = requestPath; + } + + public boolean isQQ() { + return requestPath == RequestPath.QQ; + } +} diff --git a/src/main/java/com/mohistmc/ai/koukou/network/event/ListenRegister.java b/src/main/java/com/mohistmc/ai/koukou/network/event/ListenRegister.java new file mode 100644 index 0000000000..7dfe4a32ca --- /dev/null +++ b/src/main/java/com/mohistmc/ai/koukou/network/event/ListenRegister.java @@ -0,0 +1,48 @@ +package com.mohistmc.ai.koukou.network.event; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class ListenRegister { + + private static ListenRegister listenerRegister; + private final List lstListener; + private final Lock lock = new ReentrantLock(); + + private ListenRegister() { + lstListener = new ArrayList<>(); + } + + public static ListenRegister getInstance() { + synchronized (ListenRegister.class) { + if (null == listenerRegister) { + listenerRegister = new ListenRegister(); + } + return listenerRegister; + } + } + + public void registerListener(BaseListener listenter) { + try { + boolean lockSuccess = lock.tryLock(5, TimeUnit.SECONDS); + if (lockSuccess) { + lstListener.add(listenter); + } + + } catch (Exception ignored) { + + } finally { + lock.unlock(); + } + } + + public void onEvent(HttpPostEvent baseEvent) { + for (BaseListener listenter : lstListener) { + listenter.onEvent(baseEvent); + } + } + +} diff --git a/src/main/java/com/mohistmc/plugins/MohistPlugin.java b/src/main/java/com/mohistmc/plugins/MohistPlugin.java index 93a45bc85d..fe24548c96 100644 --- a/src/main/java/com/mohistmc/plugins/MohistPlugin.java +++ b/src/main/java/com/mohistmc/plugins/MohistPlugin.java @@ -1,6 +1,8 @@ package com.mohistmc.plugins; import com.mohistmc.MohistConfig; +import com.mohistmc.ai.koukou.AIConfig; +import com.mohistmc.ai.koukou.ApiController; import com.mohistmc.plugins.back.BackCommands; import com.mohistmc.plugins.back.BackConfig; import com.mohistmc.plugins.ban.BanConfig; @@ -48,6 +50,7 @@ public static void init(Server server) { BackConfig.init(); WarpsConfig.init(); BanConfig.init(); + AIConfig.init(); File out = new File("libraries/com/mohistmc/cache", "libPath.txt"); if (out.exists()) { String data = null; @@ -67,6 +70,7 @@ public static void init(Server server) { } } EntityClear.start(); + ApiController.init(); } public static void registerCommands(Map map) { diff --git a/src/main/java/org/bukkit/craftbukkit/v1_20_R1/command/ColouredConsoleSender.java b/src/main/java/org/bukkit/craftbukkit/v1_20_R1/command/ColouredConsoleSender.java index b0008033ef..ad779b755c 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_20_R1/command/ColouredConsoleSender.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_20_R1/command/ColouredConsoleSender.java @@ -1,5 +1,7 @@ package org.bukkit.craftbukkit.v1_20_R1.command; +import com.mohistmc.ai.koukou.KouKou; +import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; @@ -21,8 +23,9 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender { private static final String RGB_STRING = String.valueOf(ANSI_ESC_CHAR) + "[38;2;%d;%d;%dm"; private static final Pattern RBG_TRANSLATE = Pattern.compile(String.valueOf(ChatColor.COLOR_CHAR) + "x(" + String.valueOf(ChatColor.COLOR_CHAR) + "[A-F0-9]){6}", Pattern.CASE_INSENSITIVE); private static final Logger LOGGER = LogManager.getLogger("Console"); + private final AtomicLong contactID = new AtomicLong(-1); - protected ColouredConsoleSender() { + public ColouredConsoleSender() { super(); replacements.put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString()); @@ -49,10 +52,20 @@ protected ColouredConsoleSender() { replacements.put(ChatColor.RESET, Ansi.ansi().a(Attribute.RESET).toString()); } + public CraftConsoleCommandSender group(long group) { + contactID.set(group); + return this; + } + + @Override public void sendMessage(String message) { if (!this.conversationTracker.isConversingModaly()) { - LOGGER.info(message); + if (contactID.get() != -1) { + KouKou.sendToGroup(String.valueOf(contactID.getAndSet(-1)), message.replaceAll("§\\S", "")); + return; + } + LOGGER.info(convertRGBColors(message)); } }