From 64686d2f47a4224fae812de78f7678ea53e9fcb0 Mon Sep 17 00:00:00 2001 From: wzp Date: Mon, 26 Aug 2024 18:34:30 +0800 Subject: [PATCH] feat: adding some feature --- .idea/fileTemplates/includes/Version.txt | 2 +- .idea/inspectionProfiles/Project_Default.xml | 10 + build.gradle.kts | 2 +- src/main/java/cn/wzpmc/Main.java | 11 +- .../arguments/MessagePartArgument.java | 26 +++ .../commands/arguments/UserIdArguments.java | 50 +++++ src/main/java/cn/wzpmc/api/entities/Ops.java | 55 ++++++ .../message/json/parts/MarkdownMessage.java | 31 +++ .../api/message/json/parts/PartType.java | 7 +- src/main/java/cn/wzpmc/api/user/IBot.java | 67 +++++++ .../api/user/group/GroupCommandSender.java | 7 +- .../java/cn/wzpmc/api/utils/CqCodeUtils.java | 148 ++++++++++++++ .../cn/wzpmc/api/utils/IncreasbleHashMap.java | 4 +- .../cn/wzpmc/api/utils/IncreasbleMap.java | 8 +- .../wzpmc/builtin/commands/DeOpCommand.java | 77 ++++++++ .../wzpmc/builtin/commands/HelpCommand.java | 58 ++++++ .../cn/wzpmc/builtin/commands/OpCommand.java | 183 ++++++++++++++++++ .../{ => builtin}/commands/StopCommand.java | 15 +- .../builtin/event/CommandEventHandler.java | 45 +++++ .../java/cn/wzpmc/console/MyBotConsole.java | 4 - .../commands/exceptions/CqCodeException.java | 24 +++ .../cn/wzpmc/entities/user/bot/MyBot.java | 113 +++++++++++ .../java/cn/wzpmc/network/PacketHandler.java | 5 +- .../java/cn/wzpmc/plugins/CommandManager.java | 3 + src/main/java/cn/wzpmc/utils/JsonUtils.java | 6 + .../java/cn/wzpmc/utils/ReflectionUtils.java | 5 +- .../utils/json/message/JsonMessageReader.java | 55 +----- .../utils/json/user/FriendUserReader.java | 35 ++++ .../utils/json/user/GroupUserReader.java | 36 ++++ src/test/java/TestCqUtils.java | 29 +++ 30 files changed, 1049 insertions(+), 72 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 src/main/java/cn/wzpmc/api/commands/arguments/MessagePartArgument.java create mode 100644 src/main/java/cn/wzpmc/api/commands/arguments/UserIdArguments.java create mode 100644 src/main/java/cn/wzpmc/api/entities/Ops.java create mode 100644 src/main/java/cn/wzpmc/api/message/json/parts/MarkdownMessage.java create mode 100644 src/main/java/cn/wzpmc/api/utils/CqCodeUtils.java create mode 100644 src/main/java/cn/wzpmc/builtin/commands/DeOpCommand.java create mode 100644 src/main/java/cn/wzpmc/builtin/commands/HelpCommand.java create mode 100644 src/main/java/cn/wzpmc/builtin/commands/OpCommand.java rename src/main/java/cn/wzpmc/{ => builtin}/commands/StopCommand.java (57%) create mode 100644 src/main/java/cn/wzpmc/builtin/event/CommandEventHandler.java create mode 100644 src/main/java/cn/wzpmc/console/commands/exceptions/CqCodeException.java create mode 100644 src/main/java/cn/wzpmc/utils/json/user/FriendUserReader.java create mode 100644 src/main/java/cn/wzpmc/utils/json/user/GroupUserReader.java create mode 100644 src/test/java/TestCqUtils.java diff --git a/.idea/fileTemplates/includes/Version.txt b/.idea/fileTemplates/includes/Version.txt index afaf360..e6d5cb8 100644 --- a/.idea/fileTemplates/includes/Version.txt +++ b/.idea/fileTemplates/includes/Version.txt @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.0.2 \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..e5cd645 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 8048d28..601fce8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach val projectName = rootProject.name val groupName by extra("cn.wzpmc") val projectArtifactId by extra("my-bot") -val projectVersion by extra("1.0.0") +val projectVersion by extra("1.0.1") plugins { id("java") diff --git a/src/main/java/cn/wzpmc/Main.java b/src/main/java/cn/wzpmc/Main.java index 9c66221..d89c481 100644 --- a/src/main/java/cn/wzpmc/Main.java +++ b/src/main/java/cn/wzpmc/Main.java @@ -3,7 +3,11 @@ package cn.wzpmc; import cn.wzpmc.api.api.IMainApi; import cn.wzpmc.api.api.actions.message.get.GetLoginInfoAction; import cn.wzpmc.api.plugins.BasePlugin; -import cn.wzpmc.commands.StopCommand; +import cn.wzpmc.builtin.commands.DeOpCommand; +import cn.wzpmc.builtin.commands.HelpCommand; +import cn.wzpmc.builtin.commands.OpCommand; +import cn.wzpmc.builtin.commands.StopCommand; +import cn.wzpmc.builtin.event.CommandEventHandler; import cn.wzpmc.configuration.Configuration; import cn.wzpmc.console.MyBotConsole; import cn.wzpmc.entities.user.bot.MyBot; @@ -87,8 +91,12 @@ public class Main { } load.onLoad(); } + // 注册内置指令 CommandManager commandManager = myBot.getCommandManager(); commandManager.registerCommand(new StopCommand(myBot)); + commandManager.registerCommand(new OpCommand()); + commandManager.registerCommand(new HelpCommand()); + commandManager.registerCommand(new DeOpCommand()); } public static WebSocketConnectionHandler createConnection(MyBot myBot, URI uri){ WebSocketConnectionHandler webSocketConnectionHandler = new WebSocketConnectionHandler(myBot); @@ -120,6 +128,7 @@ public class Main { IMainApi mainApi = myBot.getMainApi(); // 获取Bot消息 mainApi.doApiCall(new GetLoginInfoAction()); + myBot.registerEventHandler(new CommandEventHandler()); startConsole(myBot, webSocketConnectionHandler); } } \ No newline at end of file diff --git a/src/main/java/cn/wzpmc/api/commands/arguments/MessagePartArgument.java b/src/main/java/cn/wzpmc/api/commands/arguments/MessagePartArgument.java new file mode 100644 index 0000000..4016ad7 --- /dev/null +++ b/src/main/java/cn/wzpmc/api/commands/arguments/MessagePartArgument.java @@ -0,0 +1,26 @@ +package cn.wzpmc.api.commands.arguments; + +import cn.wzpmc.api.message.json.JsonMessagePart; +import cn.wzpmc.api.utils.CqCodeUtils; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +/** + * 消息段参数类型 + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 01:18 + */ +public class MessagePartArgument implements ArgumentType { + @Override + public JsonMessagePart parse(StringReader stringReader) throws CommandSyntaxException { + String s = stringReader.readStringUntil(' '); + if (!CqCodeUtils.isCQ(s)){ + throw new CommandSyntaxException(new SimpleCommandExceptionType(new LiteralMessage("MessagePart")), new LiteralMessage("Cannot read message part")); + } + return CqCodeUtils.parseToPart(s); + } +} diff --git a/src/main/java/cn/wzpmc/api/commands/arguments/UserIdArguments.java b/src/main/java/cn/wzpmc/api/commands/arguments/UserIdArguments.java new file mode 100644 index 0000000..5759c3e --- /dev/null +++ b/src/main/java/cn/wzpmc/api/commands/arguments/UserIdArguments.java @@ -0,0 +1,50 @@ +package cn.wzpmc.api.commands.arguments; + +import cn.wzpmc.api.message.json.JsonMessagePart; +import cn.wzpmc.api.message.json.parts.At; +import cn.wzpmc.api.utils.CqCodeUtils; +import cn.wzpmc.console.commands.exceptions.CqCodeException; +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.exceptions.BuiltInExceptions; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +/** + * 用户ID消息类型 + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 21:02 + */ +public class UserIdArguments implements ArgumentType { + @Override + public Long parse(StringReader stringReader) throws CommandSyntaxException { + StringBuilder builder = new StringBuilder(); + char read = stringReader.read(); + if (read == '[') { + builder.append('['); + while (stringReader.canRead()) { + read = stringReader.read(); + builder.append(read); + if (read == ']') { + break; + } + } + String s = builder.toString(); + if (CqCodeUtils.isCQ(s)) { + JsonMessagePart jsonMessagePart = CqCodeUtils.parseToPart(s); + if (jsonMessagePart instanceof At){ + return ((At) jsonMessagePart).getQq(); + } + throw new CqCodeException(); + } + } else { + String s = stringReader.readUnquotedString(); + String s1 = read + s; + try { + return Long.parseLong(s1); + }catch (NumberFormatException ignored) {} + } + throw new CommandSyntaxException(new BuiltInExceptions().readerInvalidLong(), new LiteralMessage("Cannot read long from user id")); + } +} diff --git a/src/main/java/cn/wzpmc/api/entities/Ops.java b/src/main/java/cn/wzpmc/api/entities/Ops.java new file mode 100644 index 0000000..79adf2c --- /dev/null +++ b/src/main/java/cn/wzpmc/api/entities/Ops.java @@ -0,0 +1,55 @@ +package cn.wzpmc.api.entities; + +import lombok.Getter; + +import java.util.*; + +/** + * 分群op列表 + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 20:16 + */ +@Getter +public final class Ops { + /** + * BOT总管理员 + * @since 2024/8/25 20:17 v1.0.0 + */ + private final Set admins = new HashSet<>(); + /** + * 群内管理员 + * @since 2024/8/25 20:17 v1.0.0 + */ + private final Map> groupAdmins = new HashMap<>(); + + /** + * 判断此用户在群中是否为管理 + * @author wzp + * @since 2024/8/25 20:20 v1.0.0 + * @param groupId 群ID + * @param id 用户ID + * @return 是否管理 + */ + public boolean isAdmin(Long groupId, Long id){ + if (admins.contains(id)){ + return true; + } + List longs = groupAdmins.get(groupId.toString()); + if (longs == null){ + return false; + } + return longs.contains(id); + } + + /** + * 判断此用户是否为管理 + * @author wzp + * @since 2024/8/25 20:20 v1.0.0 + * @param id 用户ID + * @return 是否管理 + */ + public boolean isAdmin(Long id) { + return admins.contains(id); + } +} diff --git a/src/main/java/cn/wzpmc/api/message/json/parts/MarkdownMessage.java b/src/main/java/cn/wzpmc/api/message/json/parts/MarkdownMessage.java new file mode 100644 index 0000000..17f1d86 --- /dev/null +++ b/src/main/java/cn/wzpmc/api/message/json/parts/MarkdownMessage.java @@ -0,0 +1,31 @@ +package cn.wzpmc.api.message.json.parts; + +import cn.wzpmc.api.message.json.JsonMessagePart; +import com.alibaba.fastjson2.JSONObject; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * Markdown格式消息 + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 15:19 + */ +@Data +@AllArgsConstructor +public class MarkdownMessage implements JsonMessagePart { + /** + * Markdown格式数据 + * @since 2024/8/25 15:20 v1.0.0 + */ + private String data; + @Override + public PartType getPartType() { + return PartType.MARKDOWN; + } + + @Override + public JSONObject getData() { + return JSONObject.of("data", data); + } +} diff --git a/src/main/java/cn/wzpmc/api/message/json/parts/PartType.java b/src/main/java/cn/wzpmc/api/message/json/parts/PartType.java index 7d6735e..915caf2 100644 --- a/src/main/java/cn/wzpmc/api/message/json/parts/PartType.java +++ b/src/main/java/cn/wzpmc/api/message/json/parts/PartType.java @@ -113,7 +113,12 @@ public enum PartType { * JSON消息 * @since 2024/8/23 21:40 v0.0.5-dev */ - JSON(CustomJSONMessage.class); + JSON(CustomJSONMessage.class), + /** + * Markdown消息 + * @since 2024/8/25 15:20 v1.0.0 + */ + MARKDOWN(MarkdownMessage.class); public final Class clazz; PartType(Class clazz){ this.clazz = clazz; diff --git a/src/main/java/cn/wzpmc/api/user/IBot.java b/src/main/java/cn/wzpmc/api/user/IBot.java index 2bca0d1..1d98eb5 100644 --- a/src/main/java/cn/wzpmc/api/user/IBot.java +++ b/src/main/java/cn/wzpmc/api/user/IBot.java @@ -1,6 +1,7 @@ package cn.wzpmc.api.user; import cn.wzpmc.api.api.IMainApi; +import cn.wzpmc.api.entities.Ops; import cn.wzpmc.api.events.Event; import cn.wzpmc.api.plugins.ICommandManager; import cn.wzpmc.api.plugins.configuration.IConfiguration; @@ -105,4 +106,70 @@ public abstract class IBot extends MessageSender implements CommandSender { } throw new RuntimeException(new IllegalAccessException("Shouldn't set id after initialized")); } + + /** + * 获取OP列表 + * @author wzp + * @since 2024/8/25 14:11 v1.0.0 + * @return OP列表 + */ + abstract public Ops getOps(); + + /** + * 添加一个OP用户 + * @author wzp + * @since 2024/8/25 14:12 v1.0.0 + * @param userId 用户ID + */ + abstract public void addOp(Long userId); + + /** + * 为一个群添加OP用户 + * @author wzp + * @since 2024/8/25 20:21 v1.0.0 + * @param groupId 群ID + * @param userId 用户ID + */ + abstract public void addOp(Long groupId, Long userId); + + /** + * 移除一个用户的OP身份 + * @author wzp + * @since 2024/8/25 20:23 v1.0.0 + * @param userId 用户ID + * @return 是否移除 + */ + abstract public boolean removeOp(Long userId); + /** + * 移除一个用户在群内的OP身份 + * @author wzp + * @since 2024/8/25 20:23 v1.0.0 + * @param groupId 群ID + * @param userId 用户ID + * @return 是否移除 + */ + abstract public boolean removeOp(Long groupId, Long userId); + + /** + * 检查用户是否为OP + * @author wzp + * @since 2024/8/25 14:10 v1.0.0 + * @param userId 用户ID + * @return 是否为OP + */ + public boolean isBotOp(Long userId){ + return getOps().isAdmin(userId); + } + + /** + * 检查用户在群内是否为OP + * @author wzp + * @since 2024/8/25 20:22 v1.0.0 + * @param groupId 群ID + * @param userId 用户ID + * @return 是否为OP + */ + public boolean isBotOp(Long groupId, Long userId) { + return this.getOps().isAdmin(groupId, userId); + } } diff --git a/src/main/java/cn/wzpmc/api/user/group/GroupCommandSender.java b/src/main/java/cn/wzpmc/api/user/group/GroupCommandSender.java index 2c37c5d..5f3c473 100644 --- a/src/main/java/cn/wzpmc/api/user/group/GroupCommandSender.java +++ b/src/main/java/cn/wzpmc/api/user/group/GroupCommandSender.java @@ -12,6 +12,7 @@ import cn.wzpmc.api.user.CommandSender; import cn.wzpmc.api.user.IBot; import cn.wzpmc.api.user.Sex; import cn.wzpmc.api.user.permission.Permissions; +import cn.wzpmc.entities.user.bot.MyBot; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -42,7 +43,7 @@ public class GroupCommandSender extends GroupUser implements CommandSender { JsonMessage jsonMessage = new JsonMessage(); List messageParts = jsonMessage.getMessageParts(); messageParts.add(new At(this.getId())); - messageParts.add(StringMessage.text(messageComponent.toMessageString())); + messageParts.add(StringMessage.text(" " + messageComponent.toMessageString())); SendGroupMessageAction sendGroupMessageAction = new SendGroupMessageAction(this.groupId, jsonMessage); mainApi.doApiCall(sendGroupMessageAction); } @@ -68,6 +69,10 @@ public class GroupCommandSender extends GroupUser implements CommandSender { String level = sender.getLevel(); GroupUserRole role = sender.getRole(); String title = sender.getTitle(); + IBot instance = MyBot.getInstance(); + if (!permissions.isAdmin() && instance.isBotOp(eventGroupId, id)) { + permissions = Permissions.ADMIN; + } return new GroupCommandSender(id, name, permissions, nickname, sex, age, card, area, level, role, title, eventGroupId); } } diff --git a/src/main/java/cn/wzpmc/api/utils/CqCodeUtils.java b/src/main/java/cn/wzpmc/api/utils/CqCodeUtils.java new file mode 100644 index 0000000..7108d80 --- /dev/null +++ b/src/main/java/cn/wzpmc/api/utils/CqCodeUtils.java @@ -0,0 +1,148 @@ +package cn.wzpmc.api.utils; + +import cn.wzpmc.api.message.json.JsonMessagePart; +import cn.wzpmc.api.message.json.parts.PartType; +import cn.wzpmc.api.message.json.parts.music.MusicType; +import cn.wzpmc.api.message.json.parts.node.CustomNode; +import cn.wzpmc.api.message.json.parts.node.SingleNode; +import cn.wzpmc.api.message.json.parts.poke.Poke; +import cn.wzpmc.api.message.json.parts.poke.PokeType; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.SneakyThrows; +import lombok.extern.log4j.Log4j2; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * CQ码相关工具类 + * @author wzp + * @version 1.0.0 + * @since 2024/8/26 14:06 + */ +@Log4j2 +public class CqCodeUtils { + /** + * 判断一个字符串是否为CQ码 + * @author wzp + * @since 2024/8/26 14:08 v1.0.0 + * @param word 一个词(不包含空格) + * @return 是否为CQ码 + */ + public static boolean isCQ(String word){ + return word.startsWith("[") && word.endsWith("]"); + } + + /** + * 解析CQ码为Map + * @author wzp + * @since 2024/8/26 14:08 v1.0.0 + * @param word 一个词(不包含空格) + * @return 解析后的Map + */ + public static Map parse(String word){ + StringBuilder key = new StringBuilder(); + boolean keyDone = false; + StringBuilder value = new StringBuilder(); + Map result = new HashMap<>(); + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (c == '[') continue; + if (c == ']') { + result.put(key.toString(), value.toString()); + } + if (keyDone) { + if (c == ','){ + result.put(key.toString(), value.toString()); + key = new StringBuilder(); + value = new StringBuilder(); + keyDone = false; + continue; + } + value.append(c); + continue; + } + if (c == ':' || c == '='){ + keyDone = true; + continue; + } + key.append(c); + } + return result; + } + + /** + * 将文本转化为JsonMessagePart + * @author wzp + * @since 2024/8/26 14:34 v1.0.0 + * @param word 一个词(不包含空格) + * @return 消息文本段 + */ + @SneakyThrows + public static JsonMessagePart parseToPart(String word) { + Map parse = parse(word); + JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(parse)); + Object cq = jsonObject.get("CQ"); + JSONObject jsonObj = JSONObject.of("type", cq, "data", jsonObject); + return parsePart(jsonObj); + } + /** + * 将json解析为单个消息段 + * @author wzp + * @since 2024/8/26 14:40 v1.0.0 + * @param jsonObject json数据 + * @return 消息段 + * @throws InvocationTargetException 调用构造方法失败时抛出 + * @throws InstantiationException 实例化错误时抛出 + * @throws IllegalAccessException 实例化错误时抛出 + * @throws NoSuchMethodException 找不到构造方法时抛出 + */ + public static JsonMessagePart parsePart(JSONObject jsonObject) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { + PartType type = jsonObject.getObject("type", PartType.class); + Class clazz = type.clazz; + JSONObject dataObject = jsonObject.getJSONObject("data"); + if (type.equals(PartType.POKE)){ + Integer pokeType = dataObject.getInteger("type"); + Integer pokeId = dataObject.getInteger("id"); + for (PokeType value : PokeType.values()) { + if (value.type.equals(pokeType) && value.id.equals(pokeId)){ + return new Poke(value); + } + } + log.warn("无法识别的戳一戳类型:type: {}, id: {}", pokeType, pokeId); + return new Poke(); + } + if (type.equals(PartType.NODE)){ + if (jsonObject.containsKey("content")){ + clazz = CustomNode.class; + }else{ + clazz = SingleNode.class; + } + } + if (type.equals(PartType.MUSIC)){ + MusicType musicType = dataObject.getObject("type", MusicType.class); + dataObject.put("musicType", musicType); + clazz = musicType.clazz; + } + Constructor declaredConstructor = clazz.getDeclaredConstructor(); + JsonMessagePart resultPart = declaredConstructor.newInstance(); + for (Field declaredField : clazz.getDeclaredFields()) { + JSONField annotation = declaredField.getAnnotation(JSONField.class); + String name = Objects.isNull(annotation) ? declaredField.getName() : annotation.name(); + if (!dataObject.containsKey(name)) { + continue; + } + declaredField.setAccessible(true); + Class thisFieldClass = declaredField.getType(); + Object value = dataObject.getObject(name, thisFieldClass); + declaredField.set(resultPart, value); + } + return resultPart; + } +} diff --git a/src/main/java/cn/wzpmc/api/utils/IncreasbleHashMap.java b/src/main/java/cn/wzpmc/api/utils/IncreasbleHashMap.java index 3bc30bc..e81bf0e 100644 --- a/src/main/java/cn/wzpmc/api/utils/IncreasbleHashMap.java +++ b/src/main/java/cn/wzpmc/api/utils/IncreasbleHashMap.java @@ -11,7 +11,7 @@ import java.util.List; * @version 0.0.4-dev * @since 2024/8/16 00:02 */ -public class IncreasbleHashMap extends HashMap> implements IncreasbleMap{ +public class IncreasbleHashMap extends HashMap> implements IncreasbleMap>{ @Override public void add(K key, V value) { List newArrayList = super.getOrDefault(key, new ArrayList<>()); @@ -41,7 +41,7 @@ public class IncreasbleHashMap extends HashMap> implements Incr } @Override - public void addAll(IncreasbleMap increasbleMap) { + public void addAll(IncreasbleMap> increasbleMap) { for (Entry> entry : increasbleMap.entrySet()) { this.addAll(entry.getKey(), entry.getValue()); } diff --git a/src/main/java/cn/wzpmc/api/utils/IncreasbleMap.java b/src/main/java/cn/wzpmc/api/utils/IncreasbleMap.java index ef13bf2..3a823ec 100644 --- a/src/main/java/cn/wzpmc/api/utils/IncreasbleMap.java +++ b/src/main/java/cn/wzpmc/api/utils/IncreasbleMap.java @@ -1,7 +1,6 @@ package cn.wzpmc.api.utils; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -9,8 +8,11 @@ import java.util.Map; * @author wzp * @version 0.0.4-dev * @since 2024/8/15 23:57 + * @param 集合类型 + * @param key类型 + * @param value类型 */ -public interface IncreasbleMap extends Map> { +public interface IncreasbleMap> extends Map { /** * 向一个Key中添加元素 * @author wzp @@ -45,7 +47,7 @@ public interface IncreasbleMap extends Map> { * @since 2024/8/16 00:35 v0.0.4-dev * @param increasbleMap 另一个表 */ - void addAll(IncreasbleMap increasbleMap); + void addAll(IncreasbleMap increasbleMap); /** * 将所有value添加到此key中 diff --git a/src/main/java/cn/wzpmc/builtin/commands/DeOpCommand.java b/src/main/java/cn/wzpmc/builtin/commands/DeOpCommand.java new file mode 100644 index 0000000..8a11b4c --- /dev/null +++ b/src/main/java/cn/wzpmc/builtin/commands/DeOpCommand.java @@ -0,0 +1,77 @@ +package cn.wzpmc.builtin.commands; + +import cn.wzpmc.api.commands.BrigadierCommand; +import cn.wzpmc.api.commands.arguments.UserIdArguments; +import cn.wzpmc.api.entities.Ops; +import cn.wzpmc.api.message.StringMessage; +import cn.wzpmc.api.user.CommandSender; +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.group.GroupCommandSender; +import cn.wzpmc.entities.user.bot.MyBot; +import com.mojang.brigadier.arguments.LongArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/26 16:54 + */ +public class DeOpCommand implements BrigadierCommand { + private final IBot instance; + public DeOpCommand(){ + this.instance = MyBot.getInstance(); + } + @Override + public LiteralArgumentBuilder getCommandNode() { + return LiteralArgumentBuilder. + literal("deop"). + requires(CommandSender::isAdmin). + then(RequiredArgumentBuilder.argument("user", new UserIdArguments()). + executes(e -> { + Ops ops = instance.getOps(); + CommandSender source = e.getSource(); + Long targetId = e.getArgument("user", Long.class); + if (source instanceof GroupCommandSender){ + if (ops.isAdmin(source.getId())){ + instance.removeOp(targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "移除总OP权限")); + }else{ + Long groupId = ((GroupCommandSender) source).getGroupId(); + instance.removeOp(groupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "移除群:" + groupId + "的OP权限")); + } + return 0; + } + instance.removeOp(targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "移除总OP权限")); + return 0; + }). + then(LiteralArgumentBuilder.literal("group") + .then(RequiredArgumentBuilder.argument("group", LongArgumentType.longArg()). + executes(e -> { + Long targetGroupId = e.getArgument("group", Long.class); + Long targetId = e.getArgument("user", Long.class); + CommandSender source = e.getSource(); + if (source instanceof GroupCommandSender){ + if (!instance.isBotOp(targetGroupId, source.getId())) { + source.sendMessage(StringMessage.text("权限不足!")); + return 0; + } + } + instance.removeOp(targetGroupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "移除群:" + targetGroupId + "的OP权限")); + return 0; + }) + ).requires((e) -> e instanceof GroupCommandSender).executes(e -> { + GroupCommandSender source = (GroupCommandSender) e.getSource(); + Long groupId = source.getGroupId(); + Long targetId = e.getArgument("user", Long.class); + instance.removeOp(groupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "移除群:" + groupId + "的OP权限")); + return 0; + }) + ) + ); + } +} diff --git a/src/main/java/cn/wzpmc/builtin/commands/HelpCommand.java b/src/main/java/cn/wzpmc/builtin/commands/HelpCommand.java new file mode 100644 index 0000000..c588c04 --- /dev/null +++ b/src/main/java/cn/wzpmc/builtin/commands/HelpCommand.java @@ -0,0 +1,58 @@ +package cn.wzpmc.builtin.commands; + +import cn.wzpmc.api.commands.BrigadierCommand; +import cn.wzpmc.api.commands.RawCommand; +import cn.wzpmc.api.message.StringMessage; +import cn.wzpmc.api.user.CommandSender; +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.entities.user.bot.MyBot; +import cn.wzpmc.plugins.CommandManager; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.RootCommandNode; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 15:07 + */ +public class HelpCommand implements BrigadierCommand { + @Override + public LiteralArgumentBuilder getCommandNode() { + return LiteralArgumentBuilder.literal("help").executes(e -> { + IBot instance = MyBot.getInstance(); + CommandManager commandManager = (CommandManager) instance.getCommandManager(); + CommandDispatcher dispatcher = commandManager.getDispatcher(); + RootCommandNode root = dispatcher.getRoot(); + Collection> children = root.getChildren(); + CommandSender source = e.getSource(); + for (CommandNode child : children) { + StringBuilder builder = new StringBuilder(); + builder.append('\n'); + builder.append('/'); + builder.append(child.getUsageText()); + builder.append('\n'); + handlerNode(child.getChildren(), 1, builder); + source.sendMessage(StringMessage.text(builder.toString())); + } + ConcurrentHashMap rawCommands = commandManager.getRawCommands(); + for (Map.Entry stringRawCommandEntry : rawCommands.entrySet()) { + source.sendMessage(StringMessage.text("/" + stringRawCommandEntry.getKey())); + } + return 0; + }); + } + private static void handlerNode(Collection> node, int tabCount, StringBuilder builder){ + for (CommandNode commandSenderCommandNode : node) { + builder.append("\t".repeat(Math.max(0, tabCount))); + builder.append(commandSenderCommandNode.getUsageText()); + builder.append('\n'); + handlerNode(commandSenderCommandNode.getChildren(), tabCount + 1, builder); + } + } +} diff --git a/src/main/java/cn/wzpmc/builtin/commands/OpCommand.java b/src/main/java/cn/wzpmc/builtin/commands/OpCommand.java new file mode 100644 index 0000000..4e34685 --- /dev/null +++ b/src/main/java/cn/wzpmc/builtin/commands/OpCommand.java @@ -0,0 +1,183 @@ +package cn.wzpmc.builtin.commands; + +import cn.wzpmc.api.commands.BrigadierCommand; +import cn.wzpmc.api.commands.arguments.UserIdArguments; +import cn.wzpmc.api.entities.Ops; +import cn.wzpmc.api.message.StringMessage; +import cn.wzpmc.api.user.CommandSender; +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.group.GroupCommandSender; +import cn.wzpmc.entities.user.bot.MyBot; +import com.mojang.brigadier.arguments.LongArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; + +import java.util.*; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 15:35 + */ +public class OpCommand implements BrigadierCommand { + private final IBot instance; + public OpCommand(){ + this.instance = MyBot.getInstance(); + } + @Override + public LiteralArgumentBuilder getCommandNode() { + return LiteralArgumentBuilder. + literal("op"). + then(LiteralArgumentBuilder.literal("list").executes(e -> { + Ops ops = instance.getOps(); + Map> groupAdmins = ops.getGroupAdmins(); + Set fullAdmins = ops.getAdmins(); + CommandSender source = e.getSource(); + Long id = source.getId(); + boolean groupAdmin = false; + boolean fullAdmin = fullAdmins.contains(id); + Long sendGroupId = -1L; + List sendGroupAdmins = new ArrayList<>(); + if (source instanceof GroupCommandSender) { + sendGroupId = ((GroupCommandSender) source).getGroupId(); + List orDefault = groupAdmins.getOrDefault(sendGroupId.toString(), new ArrayList<>()); + sendGroupAdmins.addAll(orDefault); + groupAdmin = orDefault.contains(id); + } + if (source instanceof IBot) { + groupAdmin = true; + fullAdmin = true; + } + StringBuilder builder = new StringBuilder(); + if (fullAdmin){ + builder.append(getFullOpListString(fullAdmins)).append('\n'); + for (Map.Entry> stringListEntry : groupAdmins.entrySet()) { + builder.append(getGroupOpListString(stringListEntry.getValue(), Long.valueOf(stringListEntry.getKey()))).append('\n'); + } + builder.deleteCharAt(builder.lastIndexOf("\n")); + + }else if (groupAdmin) { + builder.append(getGroupOpListString(sendGroupAdmins, sendGroupId)); + }else { + builder.append("权限不足!"); + } + source.sendMessage(StringMessage.text(builder.toString())); + return 0; + }).then(LiteralArgumentBuilder.literal("group"). + then(RequiredArgumentBuilder.argument("groupId", LongArgumentType.longArg()). + executes(e -> { + Long groupId = e.getArgument("groupId", Long.class); + Ops ops = instance.getOps(); + Map> groupsAdmins = ops.getGroupAdmins(); + Set admins = ops.getAdmins(); + List groupAdmins = groupsAdmins.getOrDefault(groupId.toString(), new ArrayList<>()); + CommandSender source = e.getSource(); + Long id = source.getId(); + String result = "权限不足"; + if (source instanceof IBot || admins.contains(id) || groupAdmins.contains(id)) { + result = getGroupOpListString(groupAdmins, groupId); + } + source.sendMessage(StringMessage.text(result)); + return 0; + })). + executes(e -> { + CommandSender commandSender = e.getSource(); + if (!(commandSender instanceof GroupCommandSender)) { + commandSender.sendMessage(StringMessage.text("仅支持群内使用此指令!")); + return 0; + } + GroupCommandSender source = (GroupCommandSender) commandSender; + Long groupId = source.getGroupId(); + Ops ops = instance.getOps(); + Map> groupsAdmins = ops.getGroupAdmins(); + Set admins = ops.getAdmins(); + List groupAdmins = groupsAdmins.getOrDefault(groupId.toString(), new ArrayList<>()); + Long id = source.getId(); + String result = "权限不足"; + if (admins.contains(id) || groupAdmins.contains(id)) { + result = getGroupOpListString(groupAdmins, groupId); + } + source.sendMessage(StringMessage.text(result)); + return 0; + }) + ).then(LiteralArgumentBuilder.literal("full").executes(e -> { + Ops ops = instance.getOps(); + Set admins = ops.getAdmins(); + CommandSender source = e.getSource(); + Long id = source.getId(); + String result = "权限不足"; + if (source instanceof IBot || admins.contains(id)) { + result = getFullOpListString(admins); + } + source.sendMessage(StringMessage.text(result)); + return 0; + }))). + requires(CommandSender::isAdmin). + then(RequiredArgumentBuilder.argument("user", new UserIdArguments()). + executes(e -> { + Ops ops = instance.getOps(); + CommandSender source = e.getSource(); + Long targetId = e.getArgument("user", Long.class); + if (source instanceof GroupCommandSender){ + if (ops.isAdmin(source.getId())){ + instance.addOp(targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "添加总OP权限")); + }else{ + Long groupId = ((GroupCommandSender) source).getGroupId(); + instance.addOp(groupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "添加群:" + groupId + "的OP权限")); + } + return 0; + } + instance.addOp(targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "添加总OP权限")); + return 0; + }). + then(LiteralArgumentBuilder.literal("group") + .then(RequiredArgumentBuilder.argument("group", LongArgumentType.longArg()). + executes(e -> { + Long targetGroupId = e.getArgument("group", Long.class); + Long targetId = e.getArgument("user", Long.class); + CommandSender source = e.getSource(); + if (source instanceof GroupCommandSender){ + if (!instance.isBotOp(targetGroupId, source.getId())) { + source.sendMessage(StringMessage.text("权限不足!")); + return 0; + } + } + instance.addOp(targetGroupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "添加群:" + targetGroupId + "的OP权限")); + return 0; + }) + ).requires((e) -> e instanceof GroupCommandSender).executes(e -> { + GroupCommandSender source = (GroupCommandSender) e.getSource(); + Long groupId = source.getGroupId(); + Long targetId = e.getArgument("user", Long.class); + instance.addOp(groupId, targetId); + source.sendMessage(StringMessage.text("已为用户:" + targetId + "添加群:" + groupId + "的OP权限")); + return 0; + }) + ) + ); + } + private static String getFullOpListString(Collection admins){ + return "总管理:" + '\n' + + getOpListString(admins); + } + private static String getGroupOpListString(Collection admins, Long groupId){ + return "群" + groupId + "的管理员:" + '\n' + + getOpListString(admins); + } + private static StringBuilder getOpListString(Collection admins) { + StringBuilder builder = new StringBuilder(); + if (admins.isEmpty()){ + builder.append("无").append('\n'); + } + for (Long admin : admins) { + builder.append('\t').append(admin).append('\n'); + } + int lastLine = builder.lastIndexOf("\n"); + builder.deleteCharAt(lastLine); + return builder; + } +} diff --git a/src/main/java/cn/wzpmc/commands/StopCommand.java b/src/main/java/cn/wzpmc/builtin/commands/StopCommand.java similarity index 57% rename from src/main/java/cn/wzpmc/commands/StopCommand.java rename to src/main/java/cn/wzpmc/builtin/commands/StopCommand.java index 1ea2f84..57e5094 100644 --- a/src/main/java/cn/wzpmc/commands/StopCommand.java +++ b/src/main/java/cn/wzpmc/builtin/commands/StopCommand.java @@ -1,11 +1,15 @@ -package cn.wzpmc.commands; +package cn.wzpmc.builtin.commands; import cn.wzpmc.api.commands.BrigadierCommand; +import cn.wzpmc.api.entities.Ops; +import cn.wzpmc.api.message.StringMessage; import cn.wzpmc.api.user.CommandSender; import cn.wzpmc.api.user.IBot; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import lombok.RequiredArgsConstructor; +import java.util.Set; + /** * /stop指令 * @author wzp @@ -18,9 +22,16 @@ public class StopCommand implements BrigadierCommand { @Override public LiteralArgumentBuilder getCommandNode() { return LiteralArgumentBuilder.literal("stop") - .requires(CommandSender::isAdmin) + .requires(e -> { + Ops ops = bot.getOps(); + Set admins = ops.getAdmins(); + return admins.contains(e.getId()); + }) .executes((e) -> { + CommandSender source = e.getSource(); + source.sendMessage(StringMessage.text(" !!!MyBot已关闭!!!")); this.bot.stop(); + System.exit(0); return 0; }); } diff --git a/src/main/java/cn/wzpmc/builtin/event/CommandEventHandler.java b/src/main/java/cn/wzpmc/builtin/event/CommandEventHandler.java new file mode 100644 index 0000000..91ecdcd --- /dev/null +++ b/src/main/java/cn/wzpmc/builtin/event/CommandEventHandler.java @@ -0,0 +1,45 @@ +package cn.wzpmc.builtin.event; + +import cn.wzpmc.api.events.message.group.GroupMessageEvent; +import cn.wzpmc.api.events.message.priv.PrivateMessageEvent; +import cn.wzpmc.api.message.StringMessage; +import cn.wzpmc.api.plugins.event.EventHandler; +import cn.wzpmc.api.user.Friend; +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.group.GroupCommandSender; +import cn.wzpmc.entities.user.bot.MyBot; +import cn.wzpmc.plugins.CommandManager; + +import java.util.regex.Pattern; + +/** + * 命令事件处理器 + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 13:40 + */ +public class CommandEventHandler { + @EventHandler + public void onGroupMessage(GroupMessageEvent event){ + GroupCommandSender groupCommandSender = GroupCommandSender.of(event); + IBot instance = MyBot.getInstance(); + Long id = instance.getId(); + String message = event.getRawMessage().getMessage(); + Pattern compile = Pattern.compile("\\[CQ:at,qq=" + id + ".*?]\\s*?/.*"); + if (compile.asMatchPredicate().test(message)){ + CommandManager commandManager = (CommandManager) instance.getCommandManager(); + commandManager.execute(groupCommandSender, message.replaceFirst("\\[CQ:at,qq=[0-9]{10}.*?]\\s*?/", "")); + } + } + @EventHandler + public void onPrivateMessage(PrivateMessageEvent event){ + Friend sender = event.getSender(); + IBot instance = MyBot.getInstance(); + StringMessage rawMessage = event.getRawMessage(); + String message = rawMessage.getMessage(); + if (message.startsWith("/")){ + CommandManager commandManager = (CommandManager) instance.getCommandManager(); + commandManager.execute(sender, message.replaceFirst("/", "")); + } + } +} diff --git a/src/main/java/cn/wzpmc/console/MyBotConsole.java b/src/main/java/cn/wzpmc/console/MyBotConsole.java index 5fe6faa..160ad41 100644 --- a/src/main/java/cn/wzpmc/console/MyBotConsole.java +++ b/src/main/java/cn/wzpmc/console/MyBotConsole.java @@ -1,10 +1,8 @@ package cn.wzpmc.console; -import cn.wzpmc.api.plugins.BasePlugin; import cn.wzpmc.entities.user.bot.MyBot; import cn.wzpmc.network.WebSocketConnectionHandler; import cn.wzpmc.plugins.CommandManager; -import cn.wzpmc.plugins.PluginManager; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -46,8 +44,6 @@ public class MyBotConsole extends SimpleTerminalConsole { @Override public void shutdown() { - PluginManager pluginManager = this.bot.getPluginManager(); - pluginManager.getPlugins().forEach(BasePlugin::onUnload); this.webSocketConnectionHandler.kill(); running = false; } diff --git a/src/main/java/cn/wzpmc/console/commands/exceptions/CqCodeException.java b/src/main/java/cn/wzpmc/console/commands/exceptions/CqCodeException.java new file mode 100644 index 0000000..e3c3d5a --- /dev/null +++ b/src/main/java/cn/wzpmc/console/commands/exceptions/CqCodeException.java @@ -0,0 +1,24 @@ +package cn.wzpmc.console.commands.exceptions; + +import com.mojang.brigadier.LiteralMessage; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 21:06 + */ +public class CqCodeException extends CommandSyntaxException { + private static final Message errorMessage = new LiteralMessage("Cannot read cq code"); + public CqCodeException() { + super(new CqCodeExceptionType(errorMessage), errorMessage); + + } + private static final class CqCodeExceptionType extends SimpleCommandExceptionType { + public CqCodeExceptionType(Message message) { + super(message); + } + } +} diff --git a/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java b/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java index 427afe4..4bc8360 100644 --- a/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java +++ b/src/main/java/cn/wzpmc/entities/user/bot/MyBot.java @@ -1,6 +1,10 @@ package cn.wzpmc.entities.user.bot; +import cn.wzpmc.api.api.ActionResponse; import cn.wzpmc.api.api.IMainApi; +import cn.wzpmc.api.api.actions.message.get.GetGroupListAction; +import cn.wzpmc.api.entities.GroupInformation; +import cn.wzpmc.api.entities.Ops; import cn.wzpmc.api.events.Event; import cn.wzpmc.api.message.MessageComponent; import cn.wzpmc.api.message.StringMessage; @@ -17,13 +21,21 @@ import cn.wzpmc.plugins.CommandManager; import cn.wzpmc.plugins.PluginManager; import cn.wzpmc.plugins.api.MainApi; import cn.wzpmc.utils.ReflectionUtils; +import com.alibaba.fastjson2.JSON; import lombok.Getter; import lombok.Setter; +import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Set; /** * 机器人实现类 @@ -48,9 +60,36 @@ public class MyBot extends IBot { @Getter private IMainApi mainApi; private WebSocketConnectionHandler connectionHandler; + @Getter + private final Ops ops; + private final File opFile; public MyBot(Configuration configuration){ + Ops opsTmp; this.configuration = configuration; this.permissions = Permissions.ADMIN; + this.opFile = new File("ops.json"); + if (!this.opFile.isFile()) { + opsTmp = new Ops(); + try { + if (!this.opFile.createNewFile()) { + log.error("无法创建OP文件!"); + } + } catch (IOException e) { + log.error("创建OP文件失败!",e); + throw new RuntimeException(e); + } + } else { + try(FileInputStream fis = new FileInputStream(this.opFile)) { + opsTmp = JSON.parseObject(fis, Ops.class); + } catch (IOException e) { + log.error("读取OP文件失败!"); + throw new RuntimeException(e); + } + } + if (opsTmp == null){ + opsTmp = new Ops(); + } + this.ops = opsTmp; } @Override @@ -71,6 +110,7 @@ public class MyBot extends IBot { if (this.console != null) { this.console.shutdown(); } + this.flushOpsFile(); } @Override @@ -99,4 +139,77 @@ public class MyBot extends IBot { this.connectionHandler = connectionHandler; this.mainApi = new MainApi(this, this.connectionHandler); } + + @Override + public void addOp(Long userId) { + Set admins = this.ops.getAdmins(); + admins.add(userId); + this.flushOpsFile(); + } + + @Override + public void addOp(Long groupId, Long userId) { + Map> admins = this.ops.getGroupAdmins(); + String string = groupId.toString(); + List longs = admins.getOrDefault(string, new ArrayList<>()); + if (!longs.contains(userId)) { + longs.add(userId); + admins.put(string, longs); + this.flushOpsFile(); + } + } + + @Override + public boolean removeOp(Long userId) { + if (!this.ops.isAdmin(userId)) { + return false; + } + Set admins = this.ops.getAdmins(); + admins.remove(userId); + flushOpsFile(); + return true; + } + + @SneakyThrows + @Override + public boolean removeOp(Long groupId, Long userId) { + boolean groupAdmin = this.ops.isAdmin(groupId, userId); + boolean admin = this.ops.isAdmin(userId); + if (!groupAdmin && !admin){ + return false; + } + Map> admins = this.ops.getGroupAdmins(); + String string = groupId.toString(); + if (groupAdmin && !admin) { + List longs = admins.get(string); + longs.remove(userId); + admins.put(string, longs); + } else { + ActionResponse> listActionResponse = this.mainApi.doApiCall(new GetGroupListAction()); + List data = listActionResponse.getData(); + this.ops.getAdmins().remove(userId); + for (GroupInformation groupInformation : data) { + Long groupInformationGroupId = groupInformation.getGroupId(); + if (groupInformationGroupId.equals(groupId)) { + continue; + } + String groupIdString = groupInformationGroupId.toString(); + List orDefault = admins.getOrDefault(groupIdString, new ArrayList<>()); + if (!orDefault.contains(userId)) { + orDefault.add(userId); + } + admins.put(groupIdString, orDefault); + } + } + flushOpsFile(); + return true; + } + + private void flushOpsFile() { + try(FileOutputStream fos = new FileOutputStream(this.opFile)){ + JSON.writeTo(fos, this.ops); + } catch (IOException e) { + log.error("写入OP文件失败!", e); + } + } } diff --git a/src/main/java/cn/wzpmc/network/PacketHandler.java b/src/main/java/cn/wzpmc/network/PacketHandler.java index 41516cd..25601f9 100644 --- a/src/main/java/cn/wzpmc/network/PacketHandler.java +++ b/src/main/java/cn/wzpmc/network/PacketHandler.java @@ -33,7 +33,7 @@ public class PacketHandler extends SimpleChannelInboundHandler dispatcher = new CommandDispatcher<>(); + @Getter private final ConcurrentHashMap rawCommands = new ConcurrentHashMap<>(); private static final int[] COLORS = {AttributedStyle.CYAN, AttributedStyle.YELLOW, AttributedStyle.GREEN, AttributedStyle.MAGENTA, AttributedStyle.BLUE}; private final IBot bot; diff --git a/src/main/java/cn/wzpmc/utils/JsonUtils.java b/src/main/java/cn/wzpmc/utils/JsonUtils.java index 2343a57..33c72a0 100644 --- a/src/main/java/cn/wzpmc/utils/JsonUtils.java +++ b/src/main/java/cn/wzpmc/utils/JsonUtils.java @@ -11,8 +11,10 @@ import cn.wzpmc.api.events.notice.notify.NotifyEvent; import cn.wzpmc.api.events.request.RequestEvent; import cn.wzpmc.api.message.StringMessage; import cn.wzpmc.api.message.json.JsonMessage; +import cn.wzpmc.api.user.Friend; import cn.wzpmc.api.user.IBot; import cn.wzpmc.api.user.IUser; +import cn.wzpmc.api.user.group.GroupUser; import cn.wzpmc.utils.json.action.ActionReader; import cn.wzpmc.utils.json.action.ActionWriter; import cn.wzpmc.utils.json.event.*; @@ -20,6 +22,8 @@ import cn.wzpmc.utils.json.honor.HonorWriter; import cn.wzpmc.utils.json.message.JsonMessageReader; import cn.wzpmc.utils.json.message.JsonMessageWriter; import cn.wzpmc.utils.json.message.StringMessageReader; +import cn.wzpmc.utils.json.user.FriendUserReader; +import cn.wzpmc.utils.json.user.GroupUserReader; import cn.wzpmc.utils.json.user.IBotReader; import cn.wzpmc.utils.json.user.IUserReader; import com.alibaba.fastjson2.JSON; @@ -59,6 +63,8 @@ public class JsonUtils { JSON.register(ActionResponse.class, new ActionReader()); JSON.register(IUser.class, new IUserReader()); JSON.register(IBot.class, new IBotReader()); + JSON.register(Friend.class, new FriendUserReader()); + JSON.register(GroupUser.class, new GroupUserReader()); } } diff --git a/src/main/java/cn/wzpmc/utils/ReflectionUtils.java b/src/main/java/cn/wzpmc/utils/ReflectionUtils.java index 0f382a5..18152e6 100644 --- a/src/main/java/cn/wzpmc/utils/ReflectionUtils.java +++ b/src/main/java/cn/wzpmc/utils/ReflectionUtils.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URLClassLoader; +import java.util.List; import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -69,9 +70,9 @@ public class ReflectionUtils { return null; } } - public static IncreasbleMap, EventHandlerMethod> loadEvents(Object eventHandlerObject){ + public static IncreasbleMap, EventHandlerMethod, List> loadEvents(Object eventHandlerObject){ Class eventHandlerClass = eventHandlerObject.getClass(); - IncreasbleMap, EventHandlerMethod> result = new IncreasbleHashMap<>(); + IncreasbleMap, EventHandlerMethod, List> result = new IncreasbleHashMap<>(); for (Method declaredMethod : eventHandlerClass.getDeclaredMethods()) { declaredMethod.setAccessible(true); if (!declaredMethod.isAnnotationPresent(EventHandler.class)){ diff --git a/src/main/java/cn/wzpmc/utils/json/message/JsonMessageReader.java b/src/main/java/cn/wzpmc/utils/json/message/JsonMessageReader.java index a29d46d..1984a93 100644 --- a/src/main/java/cn/wzpmc/utils/json/message/JsonMessageReader.java +++ b/src/main/java/cn/wzpmc/utils/json/message/JsonMessageReader.java @@ -2,25 +2,16 @@ package cn.wzpmc.utils.json.message; import cn.wzpmc.api.message.json.JsonMessage; import cn.wzpmc.api.message.json.JsonMessagePart; -import cn.wzpmc.api.message.json.parts.PartType; -import cn.wzpmc.api.message.json.parts.music.MusicType; -import cn.wzpmc.api.message.json.parts.node.CustomNode; -import cn.wzpmc.api.message.json.parts.node.SingleNode; -import cn.wzpmc.api.message.json.parts.poke.Poke; -import cn.wzpmc.api.message.json.parts.poke.PokeType; +import cn.wzpmc.api.utils.CqCodeUtils; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONReader; -import com.alibaba.fastjson2.annotation.JSONField; import com.alibaba.fastjson2.reader.ObjectReader; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.List; -import java.util.Objects; /** * json消息解析器 @@ -36,49 +27,9 @@ public class JsonMessageReader implements ObjectReader { JSONArray objects = jsonReader.readJSONArray(); JsonMessage message = new JsonMessage(); List messageParts = message.getMessageParts(); - messageFor: for (int i = 0; i < objects.size(); i++) { + for (int i = 0; i < objects.size(); i++) { JSONObject jsonObject = objects.getJSONObject(i); - PartType type = jsonObject.getObject("type", PartType.class); - Class clazz = type.clazz; - JSONObject dataObject = jsonObject.getJSONObject("data"); - if (type.equals(PartType.POKE)){ - Integer pokeType = dataObject.getInteger("type"); - Integer pokeId = dataObject.getInteger("id"); - for (PokeType value : PokeType.values()) { - if (value.type.equals(pokeType) && value.id.equals(pokeId)){ - messageParts.add(new Poke(value)); - continue messageFor; - } - } - log.warn("无法识别的戳一戳类型:type: {}, id: {}", pokeType, pokeId); - messageParts.add(new Poke()); - } - if (type.equals(PartType.NODE)){ - if (jsonObject.containsKey("content")){ - clazz = CustomNode.class; - }else{ - clazz = SingleNode.class; - } - } - if (type.equals(PartType.MUSIC)){ - MusicType musicType = dataObject.getObject("type", MusicType.class); - dataObject.put("musicType", musicType); - clazz = musicType.clazz; - } - Constructor declaredConstructor = clazz.getDeclaredConstructor(); - JsonMessagePart resultPart = declaredConstructor.newInstance(); - for (Field declaredField : clazz.getDeclaredFields()) { - JSONField annotation = declaredField.getAnnotation(JSONField.class); - String name = Objects.isNull(annotation) ? declaredField.getName() : annotation.name(); - if (!dataObject.containsKey(name)) { - continue; - } - declaredField.setAccessible(true); - Class thisFieldClass = declaredField.getType(); - Object value = dataObject.getObject(name, thisFieldClass); - declaredField.set(resultPart, value); - } - messageParts.add(resultPart); + messageParts.add(CqCodeUtils.parsePart(jsonObject)); } return message; } diff --git a/src/main/java/cn/wzpmc/utils/json/user/FriendUserReader.java b/src/main/java/cn/wzpmc/utils/json/user/FriendUserReader.java new file mode 100644 index 0000000..b37383f --- /dev/null +++ b/src/main/java/cn/wzpmc/utils/json/user/FriendUserReader.java @@ -0,0 +1,35 @@ +package cn.wzpmc.utils.json.user; + +import cn.wzpmc.api.user.Friend; +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.permission.Permissions; +import cn.wzpmc.entities.user.bot.MyBot; +import com.alibaba.fastjson2.JSONFactory; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; +import com.alibaba.fastjson2.reader.ObjectReaderCreator; +import com.alibaba.fastjson2.reader.ObjectReaderProvider; + +import java.lang.reflect.Type; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 15:08 + */ +public class FriendUserReader implements ObjectReader { + @Override + public Friend readObject(JSONReader jsonReader, Type type, Object o, long l) { + ObjectReaderProvider defaultObjectReaderProvider = JSONFactory.getDefaultObjectReaderProvider(); + ObjectReaderCreator creator = defaultObjectReaderProvider.getCreator(); + ObjectReader objectReader = creator.createObjectReader(Friend.class); + Friend friend = objectReader.readObject(jsonReader, type, o, l); + Long id = friend.getId(); + IBot instance = MyBot.getInstance(); + friend.setPermissions(Permissions.MEMBER); + if (instance.isBotOp(id)) { + friend.setPermissions(Permissions.ADMIN); + } + return friend; + } +} diff --git a/src/main/java/cn/wzpmc/utils/json/user/GroupUserReader.java b/src/main/java/cn/wzpmc/utils/json/user/GroupUserReader.java new file mode 100644 index 0000000..b4bd5d2 --- /dev/null +++ b/src/main/java/cn/wzpmc/utils/json/user/GroupUserReader.java @@ -0,0 +1,36 @@ +package cn.wzpmc.utils.json.user; + +import cn.wzpmc.api.user.IBot; +import cn.wzpmc.api.user.group.GroupUser; +import cn.wzpmc.api.user.permission.Permissions; +import cn.wzpmc.entities.user.bot.MyBot; +import com.alibaba.fastjson2.JSONFactory; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.reader.ObjectReader; +import com.alibaba.fastjson2.reader.ObjectReaderCreator; +import com.alibaba.fastjson2.reader.ObjectReaderProvider; + +import java.lang.reflect.Type; +import java.util.Objects; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/25 15:08 + */ +public class GroupUserReader implements ObjectReader { + @Override + public GroupUser readObject(JSONReader jsonReader, Type type, Object o, long l) { + ObjectReaderProvider defaultObjectReaderProvider = JSONFactory.getDefaultObjectReaderProvider(); + ObjectReaderCreator creator = defaultObjectReaderProvider.getCreator(); + ObjectReader objectReader = creator.createObjectReader(GroupUser.class); + GroupUser user = objectReader.readObject(jsonReader, type, o, l); + Long id = user.getId(); + IBot instance = MyBot.getInstance(); + user.setPermissions(Permissions.valueOf(user.getRole().name())); + if (Objects.nonNull(user.getPermissions()) && !user.getPermissions().isAdmin() && instance.isBotOp(id)) { + user.setPermissions(Permissions.ADMIN); + } + return user; + } +} diff --git a/src/test/java/TestCqUtils.java b/src/test/java/TestCqUtils.java new file mode 100644 index 0000000..7fa15ff --- /dev/null +++ b/src/test/java/TestCqUtils.java @@ -0,0 +1,29 @@ +import cn.wzpmc.api.message.json.JsonMessagePart; +import cn.wzpmc.api.utils.CqCodeUtils; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +/** + * @author wzp + * @version 1.0.0 + * @since 2024/8/26 14:14 + */ +public class TestCqUtils { + @Test + public void testIsCq(){ + System.out.println(CqCodeUtils.isCQ("[CQ:at,qq=123456789]")); + } + @Test + public void testCqParser(){ + Map parse = CqCodeUtils.parse("[CQ:at,qq=123456789,name=123]"); + for (Map.Entry stringStringEntry : parse.entrySet()) { + System.out.println(stringStringEntry); + } + } + @Test + public void testParseCq(){ + JsonMessagePart jsonMessagePart = CqCodeUtils.parseToPart("[CQ:at,qq=123456789,name=123]"); + System.out.println(jsonMessagePart); + } +}