From e1f51d4a9e4e5d7102377c06be0c219a43ba1c03 Mon Sep 17 00:00:00 2001 From: cnlimiter Date: Mon, 28 Oct 2024 00:42:58 +0800 Subject: [PATCH] feat(mcbot): add local bot execution support - Add AppHandler class to handle OneBot app initialization and execution- Implement localExecute method in ConnectCommand to support local bot execution - Update BotConfig to include URL and port for bot connection - Add CompressUtils class to handle decompression of bot app files - Implement DownloadUtils class to handle file downloads - Update FileUtils to support resource directory management - Add LgrConfig class for Lagrange configuration- Update various commands to remove unnecessary config saves --- .../java/cn/evole/mods/mcbot/Constants.java | 4 +- .../mods/mcbot/api/connect/ConnectApi.java | 2 +- .../common/command/AddGroupIDCommand.java | 2 +- .../mcbot/common/command/AuthKeyCommand.java | 2 +- .../mcbot/common/command/BotIDCommand.java | 2 +- .../mcbot/common/command/ConnectCommand.java | 10 +- .../mcbot/common/command/DebugCommand.java | 2 +- .../common/command/DelGroupIDCommand.java | 2 +- .../common/command/DisconnectCommand.java | 2 +- .../common/command/ListCustomCommand.java | 2 +- .../common/command/ReConnectCommand.java | 2 +- .../mcbot/common/command/ReceiveCommand.java | 6 +- .../mcbot/common/command/ReloadConfigCmd.java | 2 +- .../mcbot/common/command/SendCommand.java | 16 +- .../mcbot/common/command/StatusCommand.java | 2 +- .../mods/mcbot/common/config/BotConfig.java | 7 +- .../mods/mcbot/common/config/LgrConfig.java | 15 ++ .../mods/mcbot/common/event/ICmdEvent.java | 1 + .../evole/mods/mcbot/util/CompressUtils.java | 197 ++++++++++++++++++ .../evole/mods/mcbot/util/DownloadUtils.java | 26 +++ .../cn/evole/mods/mcbot/util/FileUtils.java | 27 +++ .../evole/mods/mcbot/util/ResourceUtils.java | 5 + .../mods/mcbot/util/onebot/AppHandler.java | 104 +++++++++ .../onebot/{ImgUtil.java => ImgUtils.java} | 2 +- .../mods/mcbot/util/onebot/OnebotApp.java | 36 ++++ .../main/resources/config/appsettings.json | 36 ++++ gradle.properties | 2 +- 27 files changed, 488 insertions(+), 28 deletions(-) create mode 100644 common/src/main/java/cn/evole/mods/mcbot/common/config/LgrConfig.java create mode 100644 common/src/main/java/cn/evole/mods/mcbot/util/CompressUtils.java create mode 100644 common/src/main/java/cn/evole/mods/mcbot/util/DownloadUtils.java create mode 100644 common/src/main/java/cn/evole/mods/mcbot/util/onebot/AppHandler.java rename common/src/main/java/cn/evole/mods/mcbot/util/onebot/{ImgUtil.java => ImgUtils.java} (99%) create mode 100644 common/src/main/java/cn/evole/mods/mcbot/util/onebot/OnebotApp.java create mode 100644 common/src/main/resources/config/appsettings.json diff --git a/common/src/main/java/cn/evole/mods/mcbot/Constants.java b/common/src/main/java/cn/evole/mods/mcbot/Constants.java index ba822eb..9d80307 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/Constants.java +++ b/common/src/main/java/cn/evole/mods/mcbot/Constants.java @@ -23,7 +23,8 @@ public class Constants { public static final ExecutorService cqExecutor = Executors.newSingleThreadExecutor(); public static final ExecutorService commonExecutor = Executors.newFixedThreadPool(4); public static final Gson GSON = GsonUtils.getNullGson(); - public static final Path CONFIG_FOLDER = FileUtils.checkFolder(Services.PLATFORM.getGamePath().resolve("mcbot")); + public static Path CONFIG_FOLDER = FileUtils.checkFolder(Services.PLATFORM.getGamePath().resolve("mcbot")); + public static Path APP_FOLDER = FileUtils.checkFolder(CONFIG_FOLDER.resolve("app")); public static Path DATA_FOLDER = FileUtils.checkFolder(CONFIG_FOLDER.resolve("data")); public static boolean isShutdown = false; @@ -33,6 +34,7 @@ public class Constants { public static MinecraftServer SERVER = null; public static McBotCommandSource mcBotCommand = null; + public static final String LAGRANGE_URL = "https://github.com/LagrangeDev/Lagrange.Core/releases/download/nightly/"; public static void shutdown(){ cqExecutor.shutdownNow(); diff --git a/common/src/main/java/cn/evole/mods/mcbot/api/connect/ConnectApi.java b/common/src/main/java/cn/evole/mods/mcbot/api/connect/ConnectApi.java index 0536454..f1edb1e 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/api/connect/ConnectApi.java +++ b/common/src/main/java/cn/evole/mods/mcbot/api/connect/ConnectApi.java @@ -13,7 +13,7 @@ */ public class ConnectApi { public static void wsConnect() { - Constants.onebot.close();//关闭线程 + if (Constants.onebot != null) Constants.onebot.close();//关闭线程 Constants.onebot = null;//强制为null Constants.onebot = OneBotClient .create(ModConfig.get().getBotConfig().build()) diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/AddGroupIDCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/AddGroupIDCommand.java index 84a3a30..595ae61 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/AddGroupIDCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/AddGroupIDCommand.java @@ -19,7 +19,7 @@ public static int execute(CommandContext context) throws Com ModConfig.get().getCommon().addGroupId(id); context.getSource().sendSuccess(() -> Component.literal("已成功添加QQ群号:" + id + "!"), true); } - ModConfig.get().save(); + return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/AuthKeyCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/AuthKeyCommand.java index d3179fe..db158f6 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/AuthKeyCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/AuthKeyCommand.java @@ -14,7 +14,7 @@ public static int execute(CommandContext context) throws Com val id = context.getArgument("AuthKey", String.class); ModConfig.get().getBotConfig().getToken().setValueFromString(id); context.getSource().sendSuccess(() -> Component.literal("已设置框架的AuthKey为:" + id), true); - ModConfig.get().save(); + return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/BotIDCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/BotIDCommand.java index 2091a82..b2d6a15 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/BotIDCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/BotIDCommand.java @@ -13,7 +13,7 @@ public static int execute(CommandContext context) throws Com int id = context.getArgument("BotId", Integer.class); ModConfig.get().getBotConfig().getBotId().setValueFromString(String.valueOf(id)); context.getSource().sendSuccess(() -> Component.literal("已设置机器人QQ号为:" + id + "!"), true); - ModConfig.get().save(); + return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/ConnectCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/ConnectCommand.java index f762410..8f1a62d 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/ConnectCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/ConnectCommand.java @@ -4,6 +4,7 @@ import cn.evole.mods.mcbot.Constants; import cn.evole.mods.mcbot.api.connect.ConnectApi; import cn.evole.mods.mcbot.common.config.ModConfig; +import cn.evole.mods.mcbot.util.onebot.AppHandler; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import lombok.val; @@ -37,11 +38,18 @@ public static int commonExecute(CommandContext context) { return 1; } + public static int localExecute(CommandContext context) { + AppHandler.init(); + ModConfig.get().getBotConfig().getUrl().getDefaultStringValue(); + doConnect(context); + return 1; + } + public static void doConnect(CommandContext context) { if (!Constants.onebot.getWs().isOpen()) { context.getSource().sendSuccess(() -> Component.literal("▌ " + ChatFormatting.LIGHT_PURPLE + "尝试链接框架"), true); ConnectApi.wsConnect(); - ModConfig.get().save(); + } else { context.getSource().sendSuccess(() -> Component.literal("▌ " + ChatFormatting.LIGHT_PURPLE + "已存在WS连接"), true); } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/DebugCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/DebugCommand.java index 41fc007..f614739 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/DebugCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/DebugCommand.java @@ -20,7 +20,7 @@ public static int execute(CommandContext context) throws Com } else { context.getSource().sendSuccess(() -> Component.literal("已关闭开发者模式"), true); } - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/DelGroupIDCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/DelGroupIDCommand.java index 2ec843a..15995f0 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/DelGroupIDCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/DelGroupIDCommand.java @@ -17,7 +17,7 @@ public static int execute(CommandContext context) throws Com } else { context.getSource().sendSuccess(() -> Component.literal("QQ群号:" + id + "并未出现!"), true); } - ModConfig.get().save(); + return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/DisconnectCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/DisconnectCommand.java index 1559123..b6ec90e 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/DisconnectCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/DisconnectCommand.java @@ -20,7 +20,7 @@ public static int execute(CommandContext context) throws Com context.getSource().sendSuccess(() -> Component.literal("WebSocket目前未连接"), true); } ModConfig.get().getCommon().getEnable().setBooleanValue(false); - ModConfig.get().save(); + } return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/ListCustomCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/ListCustomCommand.java index ca340fc..7300e9e 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/ListCustomCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/ListCustomCommand.java @@ -15,7 +15,7 @@ public static int execute(CommandContext context) throws Com out.append(s).append("\n"); } context.getSource().sendSuccess(() -> Component.literal(out.toString()), true); - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReConnectCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReConnectCommand.java index 9d60489..9407929 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReConnectCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReConnectCommand.java @@ -16,7 +16,7 @@ public static int execute(CommandContext context) throws Com } else { context.getSource().sendSuccess(() -> Component.literal("已关闭自动重连"), true); } - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReceiveCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReceiveCommand.java index 12f24ec..c957135 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReceiveCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReceiveCommand.java @@ -16,7 +16,7 @@ public static int allExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("全局接收群消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -29,7 +29,7 @@ public static int chatExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("接收群内聊天消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -43,7 +43,7 @@ public static int cmdExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("接收群内命令消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReloadConfigCmd.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReloadConfigCmd.java index 1e133cd..826700d 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/ReloadConfigCmd.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/ReloadConfigCmd.java @@ -23,7 +23,7 @@ public static int execute(CommandContext context) throws Com } catch (Exception e) { context.getSource().sendSuccess(() -> Component.literal("重载配置失败"), true); } - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/SendCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/SendCommand.java index 8ccce7e..21f1474 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/SendCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/SendCommand.java @@ -17,7 +17,7 @@ public static int qqLeaveExecute(CommandContext context) thr } else { context.getSource().sendSuccess(() -> Component.literal("发送离开QQ群的消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -31,7 +31,7 @@ public static int qqWelcomeExecute(CommandContext context) t } else { context.getSource().sendSuccess(() -> Component.literal("发送新人加入QQ群的消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -43,7 +43,7 @@ public static int allExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("全局发送消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -56,7 +56,7 @@ public static int joinExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("发送玩家加入游戏消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -69,7 +69,7 @@ public static int leaveExecute(CommandContext context) throw } else { context.getSource().sendSuccess(() -> Component.literal("发送玩家离开游戏消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -82,7 +82,7 @@ public static int deathExecute(CommandContext context) throw } else { context.getSource().sendSuccess(() -> Component.literal("发送玩家死亡游戏消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -95,7 +95,7 @@ public static int chatExecute(CommandContext context) throws } else { context.getSource().sendSuccess(() -> Component.literal("发送玩家聊天游戏消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } @@ -108,7 +108,7 @@ public static int achievementsExecute(CommandContext context } else { context.getSource().sendSuccess(() -> Component.literal("发送玩家成就游戏消息开关已被设置为关闭"), true); } - ModConfig.get().save(); + return 1; } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/command/StatusCommand.java b/common/src/main/java/cn/evole/mods/mcbot/common/command/StatusCommand.java index 9c10a9e..16193e4 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/command/StatusCommand.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/command/StatusCommand.java @@ -54,7 +54,7 @@ public static int execute(CommandContext context) throws Com + "发送群成员进群消息状态:" + sQqWelcomeEnabled + "\n" + "发送群成员退群消息状态:" + sQqLeaveEnabled + "\n"; context.getSource().sendSuccess(() -> Component.literal(toSend), true); - ModConfig.get().save(); + return 1; } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/config/BotConfig.java b/common/src/main/java/cn/evole/mods/mcbot/common/config/BotConfig.java index 67a1759..08915d3 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/config/BotConfig.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/config/BotConfig.java @@ -18,7 +18,7 @@ @Setter public class BotConfig extends AutoInitConfigContainer.AutoInitConfigCategoryBase { public ConfigString tag = new ConfigString("config.mcbot.bot.tag", "main", "跨服支持,权限支持"); - public ConfigString url = new ConfigString("config.mcbot.bot.url", "127.0.0.1", "地址(支持域名和ipv6)"); + public ConfigString url = new ConfigString("config.mcbot.bot.url", "127.0.0.1:18082", "地址(支持域名和ipv6)"); public ConfigString token = new ConfigString("config.mcbot.bot.token", "", "鉴权"); public ConfigString botId = new ConfigString("config.mcbot.bot.botId", "0", "机器人qq"); public ConfigBoolean reconnect = new ConfigBoolean("config.mcbot.bot.reconnect", true, "自动重连"); @@ -30,7 +30,10 @@ public BotConfig() { } public cn.evole.onebot.client.core.BotConfig build() { - return new cn.evole.onebot.client.core.BotConfig(url.getStringValue(), token.getStringValue(), Long.parseLong(botId.getStringValue()), token.getStringValue().startsWith("mirai_"), reconnect.getBooleanValue(), maxReconnectAttempts.getIntegerValue()); + return new cn.evole.onebot.client.core.BotConfig( + url.getStringValue().startsWith("ws://") ? url.getStringValue() : "ws://" + url.getStringValue() + , token.getStringValue(), Long.parseLong(botId.getStringValue()), token.getStringValue().startsWith("mirai_"), + reconnect.getBooleanValue(), maxReconnectAttempts.getIntegerValue()); } } diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/config/LgrConfig.java b/common/src/main/java/cn/evole/mods/mcbot/common/config/LgrConfig.java new file mode 100644 index 0000000..d2b71d7 --- /dev/null +++ b/common/src/main/java/cn/evole/mods/mcbot/common/config/LgrConfig.java @@ -0,0 +1,15 @@ +package cn.evole.mods.mcbot.common.config; + +import com.iafenvoy.jupiter.config.AutoInitConfigContainer; + +/** + * @Project: McBot + * @Author: cnlimiter + * @CreateTime: 2024/10/28 00:12 + * @Description: + */ +public class LgrConfig extends AutoInitConfigContainer.AutoInitConfigCategoryBase{ + public LgrConfig() { + super("lgr", "config.mcbot.category.lgr"); + } +} diff --git a/common/src/main/java/cn/evole/mods/mcbot/common/event/ICmdEvent.java b/common/src/main/java/cn/evole/mods/mcbot/common/event/ICmdEvent.java index b842440..b8b4220 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/common/event/ICmdEvent.java +++ b/common/src/main/java/cn/evole/mods/mcbot/common/event/ICmdEvent.java @@ -30,6 +30,7 @@ public static void register(CommandDispatcher dispatcher) { .requires(source -> source.hasPermission(2)) .then(Commands.literal("connect") .executes(ConnectCommand::commonExecute) + .then(Commands.literal("local").executes(ConnectCommand::localExecute)) .then(Commands.argument("parameter", StringArgumentType.greedyString()) .executes(ConnectCommand::execute) ) diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/CompressUtils.java b/common/src/main/java/cn/evole/mods/mcbot/util/CompressUtils.java new file mode 100644 index 0000000..ed1cf6b --- /dev/null +++ b/common/src/main/java/cn/evole/mods/mcbot/util/CompressUtils.java @@ -0,0 +1,197 @@ +package cn.evole.mods.mcbot.util; + +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; +import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +/** + * @Project: McBot + * @Author: cnlimiter + * @CreateTime: 2024/10/27 23:29 + * @Description: + */ +public class CompressUtils { + /** + * tar拆包解压 + * + * @param decompressFilePath + * 要被解压的压缩文件 全路径 + * @param resultDirPath + * 解压文件存放绝对路径(目录) + */ + public static boolean tarDecompression(String decompressFilePath, String resultDirPath) throws IOException { + TarArchiveInputStream tais = null; + FileInputStream fis = null; + try { + File file = new File(decompressFilePath); + fis = new FileInputStream(file); + tais = new TarArchiveInputStream(fis); + TarArchiveEntry tae = null; + while ((tae = tais.getNextTarEntry()) != null) { + BufferedOutputStream bos = null; + FileOutputStream fos = null; + try { + String dir = resultDirPath + File.separator + tae.getName();// tar档中文件 + File dirFile = new File(dir); + fos = new FileOutputStream(dirFile); + bos = new BufferedOutputStream(fos); + int count; + byte[] data = new byte[1024]; + while ((count = tais.read(data, 0, 1024)) != -1) { + bos.write(data, 0, count); + } + } finally { + if (bos != null) + bos.close(); + if (fos != null) + fos.close(); + } + } + } finally { + if (tais != null) + tais.close(); + if (fis != null) + fis.close(); + } + return true; + } + /** + * 解压对.tar.gz文件至 .tar文件 + * 说明:我们一般都是对.tar.gz文件进行gzip解压; 进而获得形如.tar文件;再进行解压 + * 注:这里暂时不再深入学习,以后有闲暇时间可深入了解学习 + * + * @param compressedFilePath + * 要被解压的压缩文件 全路径 + * @param resultDirPath + * 解压文件存放绝对路径(目录) + */ + public static boolean gzipDecompression(String compressedFilePath, String resultDirPath) throws IOException { + boolean b = false; + InputStream fin = null; + BufferedInputStream in = null; + OutputStream out = null; + GzipCompressorInputStream gcis = null; + try { + Path path_my = Paths.get(resultDirPath); + out = Files.newOutputStream(path_my); + fin = Files.newInputStream(Paths.get(compressedFilePath)); + in = new BufferedInputStream(fin); + gcis = new GzipCompressorInputStream(in); + final byte[] buffer = new byte[1024]; + int n = 0; + while (-1 != (n = gcis.read(buffer))) { + out.write(buffer, 0, n); + } + b = true; + } catch (Exception e) { + e.printStackTrace(); + }finally { + if(gcis != null) + gcis.close(); + if(in != null) + in.close(); + if(fin != null) + fin.close(); + if(out != null) + out.close(); + } + return b; + } + /** + * zip解压(注:与tar类似) + * + * @param decompressFilePath + * 要被解压的压缩文件 全路径 + * @param resultDirPath + * 解压文件存放绝对路径(目录) + */ + public static boolean zipDecompression(String decompressFilePath, String resultDirPath) throws IOException { + ZipArchiveInputStream zais = null; + FileInputStream fis = null; + try { + File file = new File(decompressFilePath); + fis = new FileInputStream(file); + zais = new ZipArchiveInputStream(fis); + ZipArchiveEntry zae = null; + while ((zae = zais.getNextZipEntry()) != null) { + FileOutputStream fos = null; + BufferedOutputStream bos = null; + try { + String dir = resultDirPath + File.separator + zae.getName();// tar档中文件 + File dirFile = new File(dir); + fos = new FileOutputStream(dirFile); + bos = new BufferedOutputStream(fos); + int count; + byte[] data = new byte[1024]; + while ((count = zais.read(data, 0, 1024)) != -1) { + bos.write(data, 0, count); + } + } finally { + if (bos != null) + bos.close(); + if (fos != null) + fos.close(); + } + } + } finally { + if (zais != null) + zais.close(); + if (fis != null) + fis.close(); + } + return true; + } + + public static void tarGzipDecompression(String decompressFilePath, String resultDirPath) throws IOException { + try (InputStream fi = Files.newInputStream(Path.of(decompressFilePath)); + BufferedInputStream bi = new BufferedInputStream(fi); + GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi); + TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) { + + ArchiveEntry entry; + while ((entry = ti.getNextEntry()) != null) { + + //获取解压文件目录,并判断文件是否损坏 + Path newPath = zipSlipProtect(entry, Path.of(resultDirPath)); + + if (entry.isDirectory()) { + //创建解压文件目录 + Files.createDirectories(newPath); + } else { + //再次校验解压文件目录是否存在 + Path parent = newPath.getParent(); + if (parent != null) { + if (Files.notExists(parent)) { + Files.createDirectories(parent); + } + } + // 将解压文件输入到TarArchiveInputStream,输出到磁盘newPath目录 + Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING); + + } + } + } + } + + private static Path zipSlipProtect(ArchiveEntry entry, Path targetDir) + throws IOException { + + Path targetDirResolved = targetDir.resolve(entry.getName()); + Path normalizePath = targetDirResolved.normalize(); + + if (!normalizePath.startsWith(targetDir)) { + throw new IOException("压缩文件已被损坏: " + entry.getName()); + } + + return normalizePath; + } +} diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/DownloadUtils.java b/common/src/main/java/cn/evole/mods/mcbot/util/DownloadUtils.java new file mode 100644 index 0000000..8163a17 --- /dev/null +++ b/common/src/main/java/cn/evole/mods/mcbot/util/DownloadUtils.java @@ -0,0 +1,26 @@ +package cn.evole.mods.mcbot.util; + +import cn.evole.mods.mcbot.Constants; +import org.apache.commons.io.FileUtils; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +/** + * @Project: McBot + * @Author: cnlimiter + * @CreateTime: 2024/10/27 19:47 + * @Description: + */ +public class DownloadUtils { + + public static void download(String url, Path localFile) throws IOException, URISyntaxException { + Constants.LOGGER.info("Downloading: {} -> {}", url, localFile); + FileUtils.copyURLToFile(new URI(url).toURL(), localFile.toFile(), + (int) TimeUnit.SECONDS.toMillis(3), (int) TimeUnit.SECONDS.toMillis(33)); + Constants.LOGGER.debug("Downloaded: {} -> {}", url, localFile); + } +} diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/FileUtils.java b/common/src/main/java/cn/evole/mods/mcbot/util/FileUtils.java index 38fc9c8..f9f9549 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/util/FileUtils.java +++ b/common/src/main/java/cn/evole/mods/mcbot/util/FileUtils.java @@ -1,8 +1,11 @@ package cn.evole.mods.mcbot.util; +import cn.evole.mods.mcbot.Constants; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; /** * @Project: McBot @@ -34,4 +37,28 @@ public static Path checkFile(Path file) { return file; } } + + + private static Path resourcePackDirPath; + + public static void setResDirPath(Path path) { + safeCreateDir(path); + resourcePackDirPath = path; + } + + + private static void safeCreateDir(Path path) { + try { + if (!Files.isDirectory(path)) { + Files.createDirectories(path); + } + } catch (Exception e) { + Constants.LOGGER.warn("Cannot create dir: {}", String.valueOf(e)); + } + } + + public static Path getResPackPath(String filename) { + return resourcePackDirPath.resolve(filename); + } + } diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/ResourceUtils.java b/common/src/main/java/cn/evole/mods/mcbot/util/ResourceUtils.java index e8911e9..5c51d83 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/util/ResourceUtils.java +++ b/common/src/main/java/cn/evole/mods/mcbot/util/ResourceUtils.java @@ -5,9 +5,12 @@ import org.apache.commons.io.FileUtils; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Optional; +import java.util.concurrent.TimeUnit; /** @@ -58,4 +61,6 @@ public static Path copyResource(String resourceName, Path targetDir){ return targetFilePath; } + + } diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/onebot/AppHandler.java b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/AppHandler.java new file mode 100644 index 0000000..852e96b --- /dev/null +++ b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/AppHandler.java @@ -0,0 +1,104 @@ +package cn.evole.mods.mcbot.util.onebot; + +import cn.evole.mods.mcbot.Constants; +import cn.evole.mods.mcbot.util.CompressUtils; +import cn.evole.mods.mcbot.util.FileUtils; +import cn.evole.mods.mcbot.util.ResourceUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import static org.apache.commons.codec.binary.Hex.DEFAULT_CHARSET; + +/** + * @Project: McBot + * @Author: cnlimiter + * @CreateTime: 2024/10/27 19:57 + * @Description: + */ +public class AppHandler { + public static void init(){ + FileUtils.setResDirPath(Constants.APP_FOLDER); + final var osName = System.getProperty("os.name", ""); + var url = ""; + var fileName = ""; + if (osName.startsWith("Mac OS")) { + fileName = "Lagrange.OneBot_osx-x64_net8.0_SelfContained.tar.gz"; + } else if (osName.startsWith("Windows")) { + fileName = "Lagrange.OneBot_win-x64_net8.0_SelfContained.zip"; + } else { + fileName = "Lagrange.OneBot_linux-musl-x64_net8.0_SelfContained.tar.gz"; + } + url = Constants.LAGRANGE_URL + fileName; + var app = new OnebotApp(fileName, url); + app.download(); + //根据文件名后缀(.tar.gz/.zip)解压缩 fileName + try { + if (osName.startsWith("Mac OS")) { + CompressUtils.tarGzipDecompression(fileName, Constants.APP_FOLDER.toString()); + } else if (osName.startsWith("Windows")) { + CompressUtils.zipDecompression(fileName, Constants.APP_FOLDER.toString()); + } else { + CompressUtils.tarGzipDecompression(fileName, Constants.APP_FOLDER.toString()); + } + } catch (IOException e) { + Constants.LOGGER.error("解压失败", e); + } + ResourceUtils.copyResource("config/appsettings.json", Constants.APP_FOLDER.resolve("Lagrange.OneBot")); + if (osName.startsWith("Windows")) { + openExe(Constants.APP_FOLDER.resolve("Lagrange.OneBot").resolve("Lagrange.OneBot.exe").toFile().getAbsolutePath()); + } else { + executeShellCmd("./" + Constants.APP_FOLDER.resolve("Lagrange.OneBot").resolve("Lagrange.OneBot").toFile().getAbsolutePath()); + } + } + + + public static void openExe(String cmd) { + BufferedReader br = null; + BufferedReader brError = null; + + try { + //执行exe cmd可以为字符串(exe存放路径)也可为数组,调用exe时需要传入参数时,可以传数组调用(参数有顺序要求) + Process p = Runtime.getRuntime().exec(cmd); + String line = null; + //获得子进程的输入流。 + br = new BufferedReader(new InputStreamReader(p.getInputStream())); + //获得子进程的错误流。 + brError = new BufferedReader(new InputStreamReader(p.getErrorStream())); + while ((line = br.readLine()) != null || (line = brError.readLine()) != null) { + //输出exe输出的信息以及错误信息 + System.out.println(line); + } + } catch (Exception e) { + Constants.LOGGER.error(e.getLocalizedMessage()); + } finally { + if (br != null) { + try { + br.close(); + } catch (Exception e) { + Constants.LOGGER.error(e.getLocalizedMessage()); + } + } + } + } + + public static void executeShellCmd(String commandString) { + String[] command = {"/bin/sh", "-c", commandString}; + ProcessBuilder processBuilder = new ProcessBuilder(command); + processBuilder.redirectErrorStream(true); // 将错误输出和标准输出合并 + try { + Process process = processBuilder.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), DEFAULT_CHARSET))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } + int exitCode = process.waitFor(); // 等待进程结束 + Constants.LOGGER.error("退出码: {}", exitCode); + } catch (IOException | InterruptedException e) { + Constants.LOGGER.error("执行命令时出错 {}", e.getLocalizedMessage()); + } + } +} diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtil.java b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtils.java similarity index 99% rename from common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtil.java rename to common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtils.java index fbbd899..b2a166d 100644 --- a/common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtil.java +++ b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/ImgUtils.java @@ -14,7 +14,7 @@ * Date: 2023/4/19 20:45 * Description: */ -public class ImgUtil { +public class ImgUtils { /** * 图片URL转Base64编码 * diff --git a/common/src/main/java/cn/evole/mods/mcbot/util/onebot/OnebotApp.java b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/OnebotApp.java new file mode 100644 index 0000000..3f283d7 --- /dev/null +++ b/common/src/main/java/cn/evole/mods/mcbot/util/onebot/OnebotApp.java @@ -0,0 +1,36 @@ +package cn.evole.mods.mcbot.util.onebot; + +import cn.evole.mods.mcbot.Constants; +import cn.evole.mods.mcbot.util.DownloadUtils; +import cn.evole.mods.mcbot.util.FileUtils; +import lombok.Getter; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * @Project: McBot + * @Author: cnlimiter + * @CreateTime: 2024/10/27 19:49 + * @Description: + */ +public class OnebotApp { + @Getter + private final String filename; + private final Path filePath; + private final String url; + + public OnebotApp(String filename, String url) { + this.filename = filename; + this.url = url; + this.filePath = FileUtils.getResPackPath(filename); + } + + public void download(){ + try { + DownloadUtils.download(url, filePath); + } catch (Exception e) { + Constants.LOGGER.warn("Error while downloading: %s", e); + } + } +} diff --git a/common/src/main/resources/config/appsettings.json b/common/src/main/resources/config/appsettings.json new file mode 100644 index 0000000..6248ce0 --- /dev/null +++ b/common/src/main/resources/config/appsettings.json @@ -0,0 +1,36 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "SignServerUrl": "", + "SignProxyUrl": "", + "MusicSignServerUrl": "", + "Account": { + "Uin": 0, + "Password": "", + "Protocol": "Linux", + "AutoReconnect": true, + "GetOptimumServer": true + }, + "Message": { + "IgnoreSelf": true, + "StringPost": false + }, + "QrCode": { + "ConsoleCompatibilityMode": true + }, + "Implementations": [ + { + "Type": "ForwardWebSocket", + "Host": "*", + "Port": 18082, + "HeartBeatInterval": 5000, + "HeartBeatEnable": true, + "AccessToken": "" + } + ] +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index a0afd02..1bc0d5f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ credits= description=Adds chat linking between QQ and Minecraft and QQ commands to request server data. minecraft_version_range=[1.20, 1.21) minecraft_version=1.20.1 -mod_version_label=alpha4 +mod_version_label=alpha5 # Mappings mappings_channel = parchment