diff --git a/README.md b/README.md index 8c73da6db..66c0555cf 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ 一个以TIM QQ协议驱动的JAVA(+Kotlin) QQ机器人服务端核心 我们坚持免费与开源 -项目处于快速开发阶段, 现在已经可以接受和发送群聊/好友消息. -协议来自网络上开源项目 -一切开发旨在学习, 请勿用于非法用途 +项目处于快速开发阶段, 现在已经可以接受和发送群聊/好友消息. +协议来自网络上开源项目 +一切开发旨在学习, 请勿用于非法用途
@@ -14,8 +14,8 @@ It use protocols from TIM QQ, that is, it won't be affected by the close The project is all for learning proposes and still in developing stage
### 代码结构 -Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持). -与插件相关性强(或其他在二次开发中容易接触的部分)均使用 Java 完成, +Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持). +与插件相关性强(或其他在二次开发中容易接触的部分)均使用 Java 完成, 同时也会针对kotlin提供优化的方法调用. 例如对'+'操作符的重载: `String+BufferedImage+QQ.At+Face+URL+String+File` 将会被自动处理为String消息. @@ -24,14 +24,14 @@ Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持). - [ ] 插件(Plugin)模块 **(Working on)** - [x] Network - Touch - [X] Network - Login -- [X] Network - Session -- [ ] Network - Verification Code (Low priority) -- [X] Network - Message Receiving -- [X] Network - Message Sending -- [ ] Network - Events **(Working on)** -- [ ] Robot - Friend/group list -- [ ] Message Section **(Working on)** -- [ ] Contact +- [X] Network - Session +- [ ] Network - Verification Code (Low priority) +- [X] Network - Message Receiving +- [X] Network - Message Sending +- [ ] Network - Events **(Working on)** +- [ ] Robot - Friend/group list +- [ ] Message Section **(Working on)** +- [ ] Contact
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java index 6632665c0..6ba54eb76 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java @@ -53,7 +53,7 @@ public class MiraiServer { MiraiConfig qqs; - protected MiraiServer(){ + protected MiraiServer() { instance = this; this.onLoad(); this.onEnable(); @@ -61,17 +61,17 @@ public class MiraiServer { private boolean enabled; - protected void shutdown(){ - if(this.enabled) { - getLogger().info(LoggerTextFormat.SKY_BLUE + "About to shutdown Mirai"); + protected void shutdown() { + if (this.enabled) { + getLogger().info("About to shutdown Mirai"); this.getEventManager().broadcastEvent(new ServerDisableEvent()); - getLogger().info(LoggerTextFormat.SKY_BLUE + "Data have been saved"); + getLogger().info("Data have been saved"); } } - private void onLoad(){ + private void onLoad() { this.parentFolder = new File(System.getProperty("user.dir")); this.unix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS"); @@ -79,36 +79,36 @@ public class MiraiServer { this.eventManager = MiraiEventManager.getInstance(); this.taskManager = MiraiTaskManager.getInstance(); - getLogger().info(LoggerTextFormat.SKY_BLUE + "About to run Mirai (" + MiraiServer.MIRAI_VERSION + ") under " + (isUnix() ? "unix" : "windows")); + getLogger().info("About to run Mirai (" + MiraiServer.MIRAI_VERSION + ") under " + (isUnix() ? "unix" : "windows")); getLogger().info("Loading data under " + LoggerTextFormat.GREEN + this.parentFolder); File setting = new File(this.parentFolder + "/Mirai.ini"); getLogger().info("Selecting setting from " + LoggerTextFormat.GREEN + setting); - if(!setting.exists()){ + if (!setting.exists()) { this.initSetting(setting); - }else { + } else { this.setting = new MiraiSetting(setting); } File qqs = new File(this.parentFolder + "/QQ.yml"); getLogger().info("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs); - if(!qqs.exists()){ + if (!qqs.exists()) { this.initQQConfig(qqs); - }else { + } else { this.qqs = new MiraiConfig(qqs); } - if(this.qqs.isEmpty()){ + if (this.qqs.isEmpty()) { this.initQQConfig(qqs); } /* MiraiSettingMapSection qqs = this.setting.getMapSection("qq"); qqs.forEach((a,p) -> { - this.getLogger().info(LoggerTextFormat.SKY_BLUE + "Finding available ports between " + "1-65536"); + this.getLogger().info("Finding available ports between " + "1-65536"); try { int port = MiraiNetwork.getAvailablePort(); - this.getLogger().info(LoggerTextFormat.SKY_BLUE + "Listening on port " + port); + this.getLogger().info("Listening on port " + port); } catch (IOException e) { e.printStackTrace(); @@ -178,56 +178,56 @@ public class MiraiServer { } private void initSetting(File setting) { - getLogger().info(LoggerTextFormat.SKY_BLUE + "Thanks for using Mirai"); - getLogger().info(LoggerTextFormat.SKY_BLUE + "initializing Settings"); + getLogger().info("Thanks for using Mirai"); + getLogger().info("initializing Settings"); try { - if(setting.createNewFile()){ - getLogger().info(LoggerTextFormat.SKY_BLUE + "Mirai Config Created"); + if (setting.createNewFile()) { + getLogger().info("Mirai Config Created"); } } catch (IOException e) { e.printStackTrace(); } this.setting = new MiraiSetting(setting); - MiraiSettingMapSection network = this.setting.getMapSection("network"); - network.set("enable_proxy","not supporting yet"); + MiraiSettingMapSection network = this.setting.getMapSection("network"); + network.set("enable_proxy", "not supporting yet"); - MiraiSettingListSection proxy = this.setting.getListSection("proxy"); + MiraiSettingListSection proxy = this.setting.getListSection("proxy"); proxy.add("1.2.3.4:95"); proxy.add("1.2.3.4:100"); - MiraiSettingMapSection worker = this.setting.getMapSection("worker"); - worker.set("core_task_pool_worker_amount",5); + MiraiSettingMapSection worker = this.setting.getMapSection("worker"); + worker.set("core_task_pool_worker_amount", 5); MiraiSettingMapSection plugin = this.setting.getMapSection("plugin"); plugin.set("debug", false); this.setting.save(); - getLogger().info(LoggerTextFormat.SKY_BLUE + "initialized; changing can be made in setting file: " + setting.toString()); + getLogger().info("initialized; changing can be made in setting file: " + setting.toString()); } - private void initQQConfig(File qqConfig){ + private void initQQConfig(File qqConfig) { this.qqs = new MiraiConfig(qqConfig); MiraiConfigSection section = new MiraiConfigSection<>(); System.out.println("/"); Scanner scanner = new Scanner(System.in); - getLogger().info(LoggerTextFormat.SKY_BLUE + "input one " + LoggerTextFormat.RED + " QQ number " + LoggerTextFormat.SKY_BLUE + "for default robotNetworkHandler"); - getLogger().info(LoggerTextFormat.SKY_BLUE + "输入用于默认机器人的QQ号"); + getLogger().info("Input a " + LoggerTextFormat.RED + " QQ number " + LoggerTextFormat.GREEN + "for default robotNetworkHandler"); + getLogger().info("输入用于默认机器人的QQ号"); long qqNumber = scanner.nextLong(); - getLogger().info(LoggerTextFormat.SKY_BLUE + "input the password for that QQ account"); - getLogger().info(LoggerTextFormat.SKY_BLUE + "输入该QQ号对应密码"); + getLogger().info("Input the password for that QQ account"); + getLogger().info("输入该QQ号的密码"); String qqPassword = scanner.next(); - section.put("password",qqPassword); - section.put("owner","default"); + section.put("password", qqPassword); + section.put("owner", "default"); - this.qqs.put(String.valueOf(qqNumber),section); + this.qqs.put(String.valueOf(qqNumber), section); this.qqs.save(); - getLogger().info(LoggerTextFormat.SKY_BLUE + "QQ account initialized; changing can be made in Config file: " + qqConfig.toString()); + getLogger().info("QQ account initialized; changing can be made in Config file: " + qqConfig.toString()); } - private void onEnable(){ + private void onEnable() { this.eventManager.broadcastEvent(new ServerEnableEvent()); this.enabled = true; getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai"); diff --git a/mirai-core/src/main/java/net/mamoe/mirai/Robot.java b/mirai-core/src/main/java/net/mamoe/mirai/Robot.java index 79b9fe6d5..fa6d90e37 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/Robot.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/Robot.java @@ -1,7 +1,10 @@ package net.mamoe.mirai; import lombok.Getter; +import net.mamoe.mirai.contact.Group; +import net.mamoe.mirai.contact.QQ; import net.mamoe.mirai.network.RobotNetworkHandler; +import net.mamoe.mirai.utils.ContactList; import net.mamoe.mirai.utils.config.MiraiConfigSection; import java.util.ArrayList; @@ -10,10 +13,8 @@ import java.util.List; public class Robot { - private final int qq; + private final int qqNumber; private final String password; - - @Getter private final RobotNetworkHandler handler; @@ -23,6 +24,9 @@ public class Robot { @Getter private final List owners; + private final ContactList groups = new ContactList<>(); + private final ContactList qqs = new ContactList<>(); + public boolean isOwnBy(String ownerName) { return owners.contains(ownerName); } @@ -30,20 +34,36 @@ public class Robot { public Robot(MiraiConfigSection data) throws Throwable { this( - data.getIntOrThrow("account", () -> new Exception("can not parse QQ account")), - data.getStringOrThrow("password", () -> new Exception("can not parse QQ password")), + data.getIntOrThrow("account", () -> new IllegalArgumentException("account")), + data.getStringOrThrow("password", () -> new IllegalArgumentException("password")), data.getAsOrDefault("owners", ArrayList::new) ); } - - public Robot(int qq, String password, List owners) { - this.qq = qq; + public Robot(int qqNumber, String password, List owners) { + this.qqNumber = qqNumber; this.password = password; this.owners = Collections.unmodifiableList(owners); - this.handler = new RobotNetworkHandler(this, this.qq, this.password); + this.handler = new RobotNetworkHandler(this, this.qqNumber, this.password); } + public QQ getQQ(int qqNumber) { + if (!this.qqs.containsKey(qqNumber)) { + this.qqs.put(qqNumber, new QQ(qqNumber)); + } + return this.qqs.get(qqNumber); + } + + public Group getGroup(int groupNumber) { + if (!this.groups.containsKey(groupNumber)) { + this.groups.put(groupNumber, new Group(groupNumber)); + } + return groups.get(groupNumber); + } + + public Group getGroupByGroupId(int groupId) { + return getGroup(Group.Companion.groupIdToNumber(groupId)); + } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt b/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt index 8a5c322db..835a1b0a2 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt @@ -1,9 +1,11 @@ package net.mamoe.mirai.contact import net.mamoe.mirai.message.Message +import net.mamoe.mirai.utils.ContactList class Group(number: Int) : Contact(number) { val groupId = groupNumberToId(number) + val members = ContactList() init { Instances.groups.add(this) @@ -55,6 +57,11 @@ class Group(number: Int) : Contact(number) { } } + @JvmStatic + fun main(args: Array) { + groupNumberToId(580266363) + } + fun groupIdToNumber(id: Int): Int { var left: Int = id.toString().let { if (it.length < 6) { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupEvent.java new file mode 100644 index 000000000..59715c343 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupEvent.java @@ -0,0 +1,23 @@ +package net.mamoe.mirai.event.events.group; + +import net.mamoe.mirai.Robot; +import net.mamoe.mirai.contact.Group; +import net.mamoe.mirai.event.events.robot.RobotEvent; +import org.jetbrains.annotations.NotNull; + +/** + * @author Him188moe + */ +public abstract class GroupEvent extends RobotEvent { + private final Group group; + + public GroupEvent(Robot robot, Group group) { + super(robot); + this.group = group; + } + + @NotNull + public Group getGroup() { + return group; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupMessageEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupMessageEvent.java new file mode 100644 index 000000000..3f6662df1 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/group/GroupMessageEvent.java @@ -0,0 +1,38 @@ +package net.mamoe.mirai.event.events.group; + +import net.mamoe.mirai.Robot; +import net.mamoe.mirai.contact.Group; +import net.mamoe.mirai.contact.QQ; +import net.mamoe.mirai.message.defaults.MessageChain; +import org.jetbrains.annotations.NotNull; + +/** + * @author Him188moe + */ +public final class GroupMessageEvent extends GroupEvent { + private final QQ sender; + private final MessageChain messageChain; + private final String messageString; + + public GroupMessageEvent(@NotNull Robot robot, @NotNull Group group, @NotNull QQ sender, @NotNull MessageChain messageChain) { + super(robot, group); + this.sender = sender; + this.messageChain = messageChain; + this.messageString = messageChain.toString(); + } + + @NotNull + public MessageChain getMessageChain() { + return messageChain; + } + + @NotNull + public String getMessageString() { + return messageString; + } + + @NotNull + public QQ getSender() { + return sender; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendEvent.java new file mode 100644 index 000000000..7c25d5308 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendEvent.java @@ -0,0 +1,25 @@ +package net.mamoe.mirai.event.events.qq; + +import net.mamoe.mirai.Robot; +import net.mamoe.mirai.contact.QQ; +import net.mamoe.mirai.event.events.robot.RobotEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * @author Him188moe + */ +public abstract class FriendEvent extends RobotEvent { + private final QQ qq; + + public FriendEvent(@NotNull Robot robot, @NotNull QQ qq) { + super(robot); + this.qq = Objects.requireNonNull(qq); + } + + @NotNull + public QQ getQQ() { + return qq; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java new file mode 100644 index 000000000..38d18b5a4 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/qq/FriendMessageEvent.java @@ -0,0 +1,30 @@ +package net.mamoe.mirai.event.events.qq; + +import net.mamoe.mirai.Robot; +import net.mamoe.mirai.contact.QQ; +import net.mamoe.mirai.message.defaults.MessageChain; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * @author Him188moe + */ +public final class FriendMessageEvent extends FriendEvent { + private final MessageChain messageChain; + private final String messageString; + + public FriendMessageEvent(@NotNull Robot robot, @NotNull QQ sender, @NotNull MessageChain messageChain) { + super(robot, sender); + this.messageChain = Objects.requireNonNull(messageChain); + this.messageString = messageChain.toString(); + } + + public String getMessageString() { + return messageString; + } + + public MessageChain getMessageChain() { + return messageChain; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java index 5c1ad7b68..ab410af4a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java @@ -2,15 +2,20 @@ package net.mamoe.mirai.event.events.robot; import net.mamoe.mirai.Robot; import net.mamoe.mirai.event.events.MiraiEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; public abstract class RobotEvent extends MiraiEvent { private final Robot robot; + public RobotEvent(@NotNull Robot robot) { + this.robot = Objects.requireNonNull(robot); + } + + @NotNull public Robot getRobot() { return robot; } - public RobotEvent(Robot robot){ - this.robot = robot; - } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java index 5f6c37642..dd0be1c57 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java @@ -2,6 +2,6 @@ package net.mamoe.mirai.event.events.server; import net.mamoe.mirai.event.events.MiraiEvent; -public class ServerDisableEvent extends MiraiEvent { +public final class ServerDisableEvent extends MiraiEvent { } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java index 3c9f61da9..18aeefccf 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java @@ -2,7 +2,7 @@ package net.mamoe.mirai.event.events.server; import net.mamoe.mirai.event.events.MiraiEvent; -public class ServerEnableEvent extends MiraiEvent { +public final class ServerEnableEvent extends MiraiEvent { } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java index 3561460ce..d0fe6465f 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java @@ -3,9 +3,12 @@ package net.mamoe.mirai.message.defaults; import net.mamoe.mirai.message.Message; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Him188moe @@ -21,6 +24,19 @@ public final class MessageChain extends Message { list.add(tail); } + public MessageChain(@NotNull Message message) { + Objects.requireNonNull(message); + list.add(message); + } + + public List toList() { + return List.copyOf(list); + } + + public Stream stream() { + return new ArrayList<>(list).stream(); + } + @Override public synchronized String toString() { return this.list.stream().map(Message::toString).collect(Collectors.joining("")); diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt index 805160d16..7d28047fc 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt @@ -2,11 +2,14 @@ package net.mamoe.mirai.network import net.mamoe.mirai.MiraiServer import net.mamoe.mirai.Robot +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.event.events.qq.FriendMessageEvent import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.login.* -import net.mamoe.mirai.network.packet.message.ClientSendFriendMessagePacket import net.mamoe.mirai.network.packet.message.ClientSendGroupMessagePacket +import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket +import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacket import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted import net.mamoe.mirai.task.MiraiThreadPool @@ -84,6 +87,7 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor */ private lateinit var cookies: String private var gtk: Int = 0 + private var ignoreMessage: Boolean = false init { tlv0105 = lazyEncode { @@ -197,6 +201,11 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey)) }, 90000, 90000, TimeUnit.MILLISECONDS) RobotLoginSucceedEvent(robot).broadcast() + + MiraiThreadPool.getInstance().schedule({ + ignoreMessage = false + }, 2, TimeUnit.SECONDS) + this.tlv0105 = packet.tlv0105 sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE)) } @@ -225,23 +234,23 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor } - is ServerMessageEventPacketRaw -> onPacketReceived(packet.analyze()) - is ServerFriendMessageEventPacket -> { - println(packet.toString()) - if (packet.message == "牛逼") { - sendPacket(ClientSendFriendMessagePacket(this.number, packet.qq, this.sessionKey, "牛逼!!")) + if (ignoreMessage) { + return } - //friend message + FriendMessageEvent(this.robot, this.robot.getQQ(packet.qq), packet.message) } is ServerGroupMessageEventPacket -> { //group message if (packet.message == "牛逼") { - sendPacket(ClientSendGroupMessagePacket(packet.group, this.number, this.sessionKey, "牛逼!")) + sendPacket(ClientSendGroupMessagePacket(Group.groupNumberToId(packet.groupNumber), this.number, this.sessionKey, "牛逼!")) } + + //todo + //GroupMessageEvent(this.robot, this.robot.getGroup(packet.groupNumber), this.robot.getQQ(packet.qq), packet.message) } is UnknownServerEventPacket -> { @@ -256,6 +265,8 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor } + is ServerMessageEventPacketRaw -> onPacketReceived(packet.analyze()) + is ServerVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt(this.token00BA)) is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt()) is ServerLoginResponseResendPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) @@ -267,6 +278,11 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor is ServerMessageEventPacketRawEncoded -> onPacketReceived(packet.decrypt(this.sessionKey)) + is ServerSendFriendMessageResponsePacket, + is ServerSendGroupMessageResponsePacket -> { + + } + else -> throw IllegalArgumentException(packet.toString()) } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt index 2619309e2..8d73cc493 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt @@ -1,5 +1,7 @@ package net.mamoe.mirai.network.packet +import net.mamoe.mirai.message.defaults.MessageChain +import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.utils.toUHexString import java.io.ByteArrayOutputStream import java.io.DataInputStream @@ -29,15 +31,15 @@ class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArra * 群文件上传 */ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - lateinit var message: String + lateinit var xmlMessage: String override fun decode() { - message = String(this.input.goto(65).readNBytes(this.input.goto(60).readShort().toInt())) + xmlMessage = String(this.input.goto(65).readNBytes(this.input.goto(60).readShort().toInt())) }//todo test } class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - var group: Int = 0 + var groupNumber: Int = 0 var qq: Int = 0 lateinit var message: String lateinit var messageType: MessageType @@ -57,10 +59,10 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, } override fun decode() { - group = this.input.goto(51).readInt() + groupNumber = this.input.goto(51).readInt() qq = this.input.goto(56).readInt() val fontLength = this.input.goto(108).readShort() - //println(this.input.goto(110 + fontLength).readNBytes(2).toUHexString())//always 00 00 + //println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00 messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) { 19 -> MessageType.NORMAL @@ -102,7 +104,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, message = "[face${faceId}.gif]" } - MessageType.AT, MessageType.OTHER -> { + MessageType.AT, MessageType.OTHER, MessageType.PLAIN_TEXT, MessageType.IMAGE, MessageType.ANONYMOUS -> { var messageLength: Int = this.input.goto(110 + fontLength + 6).readShort().toInt() message = String(this.input.goto(110 + fontLength + 8).readNBytes(messageLength)) @@ -120,7 +122,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, //读取 nick, ignore. /* - when (this.input.goto(110 + fontLength + 3 + oeLength).readByte().toInt()) { + when (this.input.goto(110 + fontLength + 3 + oeLength).readByteAt().toInt()) { 12 -> { this.input.skip(4)//maybe 5? @@ -139,6 +141,35 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, } } +class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { + var qq: Int = 0 + lateinit var message: MessageChain + + + @ExperimentalUnsignedTypes + override fun decode() { + //start at Sep1.0:27 + input.goto(0) + println(input.readAllBytes().toUHexString()) + input.goto(0) + + qq = input.readIntAt(0) + val msgLength = input.readShortAt(22) + val fontLength = input.readShortAt(93 + msgLength) + val offset = msgLength + fontLength + message = MessageChain(PlainText(let { + val offset2 = input.readShortAt(101 + offset) + input.goto(103 + offset).readVarString(offset2.toInt()) + })) + } + +} + +/* + + +backup + class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { var qq: Int = 0 lateinit var message: String @@ -147,17 +178,17 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray @ExperimentalUnsignedTypes override fun decode() { //start at Sep1.0:27 - qq = input.readInt(0) - val msgLength = input.readShort(22) - val fontLength = input.readShort(93+msgLength) + qq = input.readIntAt(0) + val msgLength = input.readShortAt(22) + val fontLength = input.readShortAt(93+msgLength) val offset = msgLength+fontLength - message = if(input.readByte(97+offset).toUHexString() == "02"){ - "[face" + input.goto(103+offset).readByte(1).toInt().toString() + ".gif]" + message = if(input.readByteAt(97+offset).toUHexString() == "02"){ + "[face" + input.goto(103+offset).readByteAt(1).toInt().toString() + ".gif]" //.gif }else { - val offset2 = input.readShort(101 + offset) + val offset2 = input.readShortAt(101 + offset) input.goto(103 + offset).readVarString(offset2.toInt()) } - // TODO("FRIEND 解析")d } -} \ No newline at end of file +} + */ \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt index 7de9f43d5..c3e0e2e3a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt @@ -145,22 +145,22 @@ infix fun DataInputStream.goto(position: N): DataInputStream { return this } -fun DataInputStream.readNBytes(position: N, length: Int): ByteArray { +fun DataInputStream.readNBytesAt(position: N, length: Int): ByteArray { this.goto(position) return this.readNBytes(length) } -fun DataInputStream.readInt(position: N): Int { +fun DataInputStream.readIntAt(position: N): Int { this.goto(position) return this.readInt(); } -fun DataInputStream.readByte(position: N): Byte { +fun DataInputStream.readByteAt(position: N): Byte { this.goto(position) return this.readByte(); } -fun DataInputStream.readShort(position: N): Short { +fun DataInputStream.readShortAt(position: N): Short { this.goto(position) return this.readShort(); } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt index 09e0c90f7..033f993d9 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt @@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.login import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.goto -import net.mamoe.mirai.network.packet.readNBytes +import net.mamoe.mirai.network.packet.readNBytesAt import net.mamoe.mirai.network.packet.readVarString import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.utils.TEACryptor @@ -31,16 +31,16 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in * Version 1 @Deprecated this.input.skip(7)//8 - encryptionKey = this.input.readNBytes(16)//24 + encryptionKey = this.input.readNBytesAt(16)//24 this.input.skip(2)//25->26 - token38 = this.input.readNBytes(56)//81->82 + token38 = this.input.readNBytesAt(56)//81->82 this.input.skip(60L)//142 //?? - var b = this.input.readNBytes(2) + var b = this.input.readNBytesAt(2) val msgLength = when (b.toUByteArray().toUHexString()) { "01 07" -> 0 "00 33" -> 28 @@ -55,17 +55,17 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in this.input.skip(10)//171+msgLength - _0828_rec_decr_key = this.input.readNBytes(16)//187+msgLength + _0828_rec_decr_key = this.input.readNBytesAt(16)//187+msgLength this.input.skip(2) - token88 = this.input.readNBytes(136)//325+ // msgLength + token88 = this.input.readNBytesAt(136)//325+ // msgLength this.input.skip(299L)//624+msgLength //varString (nickLength bytes) - val nickLength = this.input.readByte().toInt()//625+msgLength + val nickLength = this.input.readByteAt().toInt()//625+msgLength System.out.println(nickLength) @@ -76,11 +76,11 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in /* this.input.skip((dataIndex - (625 + msgLength + nickLength)) + 0L)//-31 - gender = this.input.readByte().toUByte().toInt()//-30 + gender = this.input.readByteAt().toUByte().toInt()//-30 this.input.skip(9)//-27 - age = this.input.readShort()//-25 + age = this.input.readShortAt()//-25 */ age = 0 gender = 0 @@ -106,16 +106,16 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in else -> throw IllegalStateException(id) } - this._0828_rec_decr_key = this.input.readNBytes(171 + msgLength, 16) + this._0828_rec_decr_key = this.input.readNBytesAt(171 + msgLength, 16) - this.token88 = this.input.readNBytes(189 + msgLength, 136) + this.token88 = this.input.readNBytesAt(189 + msgLength, 136) val nickLength = this.input.goto(624 + msgLength).readByte().toInt() this.nick = this.input.readVarString(nickLength) - //this.age = this.input.goto(packetDataLength - 28).readShort() + //this.age = this.input.goto(packetDataLength - 28).readShortAt() - //this.gender = this.input.goto(packetDataLength - 32).readByte().toInt() + //this.gender = this.input.goto(packetDataLength - 32).readByteAt().toInt() } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt index 0cdbbbeba..315814a29 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt @@ -2,6 +2,8 @@ package net.mamoe.mirai.network.packet.message import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.packet.* +import net.mamoe.mirai.utils.lazyEncode +import net.mamoe.mirai.utils.toUHexString import java.io.DataInputStream /** @@ -21,6 +23,11 @@ class ClientSendGroupMessagePacket( this.writeHex(Protocol._fixVer) this.encryptAndWrite(sessionKey) { + val bytes = message.toByteArray() + it.writeByte(0x2A) + it.writeInt(groupId) + it.writeShort(56 + bytes.size) + it.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00") it.writeTime() it.writeRandom(4) @@ -28,21 +35,33 @@ class ClientSendGroupMessagePacket( it.writeZero(2) //messages - val bytes = message.toByteArray() - it.writeByte(0x2A) - it.writeInt(groupId) - it.writeShort(19 + bytes.size) - - it.writeByte(0x01) it.writeByte(0x01) it.writeShort(bytes.size + 3) it.writeByte(0x01) it.writeShort(bytes.size) it.write(bytes) + + println(it.toByteArray().toUHexString()) } } } +fun main() { + println(lazyEncode { + val bytes = "message".toByteArray() + it.writeByte(0x2A) + it.writeInt(580266363) + it.writeShort(19 + bytes.size) + + it.writeByte(0x01) + it.writeByte(0x01) + it.writeShort(bytes.size + 3) + it.writeByte(0x01) + it.writeShort(bytes.size) + it.write(bytes) + }.toUHexString()) +} + @PacketId("00 02") class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) { override fun decode() { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt index 545fcabbc..710c1c2a0 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt @@ -27,7 +27,7 @@ class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) this.unknownBoolean = this.input.readByte().toInt() == 1 - //this.token00BA = this.input.goto(packetLength - 60).readNBytes(40) + //this.token00BA = this.input.goto(packetLength - 60).readNBytesAt(40) } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java new file mode 100644 index 000000000..2729890f2 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java @@ -0,0 +1,10 @@ +package net.mamoe.mirai.utils; + +import net.mamoe.mirai.contact.Contact; +import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap; + +/** + * @author Him188moe + */ +public class ContactList extends MiraiSynchronizedLinkedListMap { +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/LoggerTextFormat.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/LoggerTextFormat.java index 32626ed75..f580f71b1 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/LoggerTextFormat.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/LoggerTextFormat.java @@ -1,14 +1,25 @@ package net.mamoe.mirai.utils; -import net.mamoe.mirai.MiraiServer; - public enum LoggerTextFormat { - RED("\033[31m"), - GREEN("\033[32m"), - YELLOW("\033[33m"), - BLUE("\033[34m"), - SKY_BLUE("\033[36m"), - RESET("\33[0m"); + RESET("\33[0m"), + + BLUE("\033[0;34m"), + BLACK("\033[0;30m"), + DARK_GREY("\033[1;30m"), + LIGHT_BLUE("\033[1;34m"), + GREEN("\033[0;32m"), + LIGHT_GTEEN("\033[1;32m"), + CYAN("\033[0;36m"), + LIGHT_CYAN("\033[1;36m"), + RED("\033[0;31m"), + LIGHT_RED("\033[1;31m"), + PURPLE("\033[0;35m"), + LIGHT_PURPLE("\033[1;35m"), + BROWN("\033[0;33m"), + YELLOW("\033[1;33m"), + LIGHT_GRAY("\033[0;37m"), + WHITE("\033[1;37m"); + private String format; LoggerTextFormat(String format) { @@ -17,9 +28,9 @@ public enum LoggerTextFormat { @Override public String toString() { - if(MiraiServer.getInstance().isUnix()){ - return format; - } - return ""; + //if(MiraiServer.getInstance().isUnix()){ + return format; + // } + // return ""; } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt index ff946d6ea..099f5fa05 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt @@ -7,21 +7,16 @@ import java.util.* * used to replace old logger */ object MiraiLogger { - infix fun info(o: Any?) { - this.print(o.toString()) - } - - infix fun error(o: Any?) { - this.print(o.toString(), LoggerTextFormat.RED) - } - infix fun log(o: Any?) = info(o) - infix fun println(o: Any?) = info(o) + infix fun info(o: Any?) = this.print(o.toString(), LoggerTextFormat.RESET) - infix fun debug(o: Any?) { - this.print(o.toString()) - } + + infix fun error(o: Any?) = this.print(o.toString(), LoggerTextFormat.RED) + + infix fun notice(o: Any?) = this.print(o.toString(), LoggerTextFormat.LIGHT_BLUE) + + infix fun debug(o: Any?) = this.print(o.toString(), LoggerTextFormat.YELLOW) infix fun catching(e: Throwable) { e.printStackTrace() @@ -31,7 +26,7 @@ object MiraiLogger { this.print(e.cause.toString())*/ } - private fun print(value: String?, color: LoggerTextFormat = LoggerTextFormat.BLUE) { + private fun print(value: String?, color: LoggerTextFormat = LoggerTextFormat.WHITE) { val s = SimpleDateFormat("MM-dd HH:mm:ss").format(Date()) kotlin.io.println("$color[Mirai] $s : $value") } @@ -42,4 +37,6 @@ fun Any.logInfo() = MiraiLogger.info(this) fun Any.logDebug() = MiraiLogger.debug(this) -fun Any.logError() = MiraiLogger.error(this) \ No newline at end of file +fun Any.logError() = MiraiLogger.error(this) + +fun Any.logNotice() = MiraiLogger.notice(this) \ No newline at end of file diff --git a/mirai-core/src/test/java/HexComparator.java b/mirai-core/src/test/java/HexComparator.java index 5ed52bd6b..cabaeba5d 100644 --- a/mirai-core/src/test/java/HexComparator.java +++ b/mirai-core/src/test/java/HexComparator.java @@ -6,12 +6,10 @@ import net.mamoe.mirai.network.Protocol; import java.lang.reflect.Field; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.LongAdder; import java.util.function.BiConsumer; /** @@ -213,16 +211,16 @@ public class HexComparator { } public static void main(String[] args) { - /* + System.out.println(HexComparator.compare( //mirai - "01 12 00 38 56 3A E4 8B B4 64 D2 72 60 FE 01 54 FC B1 5F 88 E0 BA 64 1A 55 F2 84 FC 97 D0 BF 5F 47 A8 D9 76 BB FB 4A 7A F3 5E 0E A4 8E CA 8F 27 C2 02 6E 5D E7 68 9F 7C CF 91 83 F4 03 0F 00 11 00 0F 44 45 53 4B 54 4F 50 2D 4D 31 37 4A 52 45 55 00 05 00 06 00 02 76 E4 B8 DD 00 06 00 78 0D DF 92 9C 5A 08 D1 67 FD 7D D6 DE CE D0 92 39 79 17 53 57 41 9B D6 D3 F9 F8 9A 3B E1 C2 3A E7 CF 02 6E 5E 36 B7 6D CF 33 66 77 FE AC 58 93 A3 85 E7 AF 6F 2D A2 74 E2 60 28 4B 29 17 04 79 95 39 D4 BF 4D C1 ED 61 49 13 23 9D 71 62 29 AF 87 D7 E3 42 49 88 3F D8 5C DB 9F 9E 5A 2A EA 02 F6 4F 2B D3 5B AF BE 0C B2 54 46 AE 99 1B 07 0B BE 6A C2 29 18 25 6A 95 0A 00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B 00 1A 00 40 E2 9A D0 B3 7D CD 3D F4 55 DB 04 A4 68 2A AF 4E 38 46 8B E0 1F 2D 2A B8 F6 C6 AB 4F DF D1 5F 8C D4 CA 2A 91 25 14 33 A3 C5 08 F8 01 4C E6 3D 89 5F 23 38 3A EC 01 58 F6 B1 F6 7F 2E 6A 02 17 4A 00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 76 E4 B8 DD 00 00 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 01 10 00 3C 00 01 00 38 57 3A 37 C3 FB A0 C3 E5 AE F3 0E B6 03 DE BF 9E E2 B5 C5 FE A0 F0 03 4F F7 8A 5C 29 5C E0 5A A2 89 D5 3F 60 E2 B2 81 FE D4 16 04 D4 E3 C6 4A D7 A9 D9 E6 FC 2E 7E 0C F3 03 12 00 05 01 00 00 00 01 05 08 00 05 10 00 00 00 00 03 13 00 19 01 01 02 00 10 04 EA 78 D1 A4 FF CD CC 7C B8 D4 12 7D BB 03 AA 00 00 00 00 01 02 00 62 00 01 04 EB B7 C1 86 F9 08 96 ED 56 84 AB 50 85 2E 48 00 38 E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3 00 14 10 21 DF C4 F3 DD 42 09 6F A0 93 8C 7E 0E 78 EF 22 8D 88 35" + "2A 22 96 29 7B 00 40 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 EC 21 40 06 18 89 54 BC 00 00 00 00 09 00 86 00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 0A 01 00 07 E7 89 9B E9 80 BC 21\n" , //e - "01 12 00 38 56 3A E4 8B B4 64 D2 72 60 FE 01 54 FC B1 5F 88 E0 BA 64 1A 55 F2 84 FC 97 D0 BF 5F 47 A8 D9 76 BB FB 4A 7A F3 5E 0E A4 8E CA 8F 27 C2 02 6E 5D E7 68 9F 7C CF 91 83 F4 03 0F 00 11 00 0F 44 45 53 4B 54 4F 50 2D 4D 31 37 4A 52 45 55 00 05 00 06 00 02 76 E4 B8 DD 00 06 00 78 0D DF 92 9C 5A 08 D1 67 FD 7D D6 DE CE D0 92 39 79 17 53 57 41 9B D6 D3 F9 F8 9A 3B E1 C2 3A E7 CF 02 6E 5E 36 B7 6D CF 33 66 77 FE AC 58 93 A3 85 E7 AF 6F 2D A2 74 E2 60 28 4B 29 17 04 79 95 39 D4 BF 4D C1 ED 61 49 13 23 9D 71 62 29 AF 87 D7 E3 42 49 88 3F D8 5C DB 9F 9E 5A 2A EA 02 F6 4F 2B D3 5B AF BE 0C B2 54 46 AE 99 1B 07 0B BE 6A C2 29 18 25 6A 95 0A 00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B 00 1A 00 40 26 2B FE EB 21 8E 08 16 BA 42 B3 A3 C5 6A 5B 31 33 A9 B3 F0 EB 16 80 BE F0 58 D3 51 98 2F A7 23 27 7C 06 97 F4 85 01 77 5C 5B D6 A2 82 54 06 A0 07 53 39 E2 E7 62 E5 09 1A 25 79 6F D9 E0 ED B6 00 18 00 16 00 01 00 00 04 53 00 00 00 01 00 00 15 85 76 E4 B8 DD 00 00 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 01 10 00 3C 00 01 00 38 57 3A 37 C3 FB A0 C3 E5 AE F3 0E B6 03 DE BF 9E E2 B5 C5 FE A0 F0 03 4F F7 8A 5C 29 5C E0 5A A2 89 D5 3F 60 E2 B2 81 FE D4 16 04 D4 E3 C6 4A D7 A9 D9 E6 FC 2E 7E 0C F3 03 12 00 05 01 00 00 00 01 05 08 00 05 01 00 00 00 00 03 13 00 19 01 01 02 00 10 04 EA 78 D1 A4 FF CD CC 7C B8 D4 12 7D BB 03 AA 00 00 00 00 01 02 00 62 00 01 04 EB B7 C1 86 F9 08 96 ED 56 84 AB 50 85 2E 48 00 38 E9 AA 2B 4D 26 4C 76 18 FE 59 D5 A9 82 6A 0C 04 B4 49 50 D7 9B B1 FE 5D 97 54 8D 82 F3 22 C2 48 B9 C9 22 69 CA 78 AD 3E 2D E9 C9 DF A8 9E 7D 8C 8D 6B DF 4C D7 34 D0 D3 00 14 4C 97 73 32 83 1A 6F C4 94 91 0A 7A D8 21 81 58 73 3D 24 F6" + "2A 22 96 29 7B 00 3F 00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00 5D 6B 8E 1A FE 39 0B FC 00 00 00 00 09 00 86 00 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 0A 01 00 07 6D 65 73 73 61 67 65" )); - */ + /* System.out.println(HexComparator.compare(