Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core/src/main/java/net/mamoe/mirai/Robot.java
This commit is contained in:
liujiahua123123 2019-09-02 10:55:25 +08:00
commit 1c8e5679a1
22 changed files with 386 additions and 140 deletions

View File

@ -53,7 +53,7 @@ public class MiraiServer {
MiraiConfig qqs; MiraiConfig qqs;
protected MiraiServer(){ protected MiraiServer() {
instance = this; instance = this;
this.onLoad(); this.onLoad();
this.onEnable(); this.onEnable();
@ -61,17 +61,17 @@ public class MiraiServer {
private boolean enabled; private boolean enabled;
protected void shutdown(){ protected void shutdown() {
if(this.enabled) { if (this.enabled) {
getLogger().info(LoggerTextFormat.SKY_BLUE + "About to shutdown Mirai"); getLogger().info("About to shutdown Mirai");
this.getEventManager().broadcastEvent(new ServerDisableEvent()); 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.parentFolder = new File(System.getProperty("user.dir"));
this.unix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS"); this.unix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS");
@ -79,36 +79,36 @@ public class MiraiServer {
this.eventManager = MiraiEventManager.getInstance(); this.eventManager = MiraiEventManager.getInstance();
this.taskManager = MiraiTaskManager.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); getLogger().info("Loading data under " + LoggerTextFormat.GREEN + this.parentFolder);
File setting = new File(this.parentFolder + "/Mirai.ini"); File setting = new File(this.parentFolder + "/Mirai.ini");
getLogger().info("Selecting setting from " + LoggerTextFormat.GREEN + setting); getLogger().info("Selecting setting from " + LoggerTextFormat.GREEN + setting);
if(!setting.exists()){ if (!setting.exists()) {
this.initSetting(setting); this.initSetting(setting);
}else { } else {
this.setting = new MiraiSetting(setting); this.setting = new MiraiSetting(setting);
} }
File qqs = new File(this.parentFolder + "/QQ.yml"); File qqs = new File(this.parentFolder + "/QQ.yml");
getLogger().info("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs); getLogger().info("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs);
if(!qqs.exists()){ if (!qqs.exists()) {
this.initQQConfig(qqs); this.initQQConfig(qqs);
}else { } else {
this.qqs = new MiraiConfig(qqs); this.qqs = new MiraiConfig(qqs);
} }
if(this.qqs.isEmpty()){ if (this.qqs.isEmpty()) {
this.initQQConfig(qqs); this.initQQConfig(qqs);
} }
/* /*
MiraiSettingMapSection qqs = this.setting.getMapSection("qq"); MiraiSettingMapSection qqs = this.setting.getMapSection("qq");
qqs.forEach((a,p) -> { 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 { try {
int port = MiraiNetwork.getAvailablePort(); int port = MiraiNetwork.getAvailablePort();
this.getLogger().info(LoggerTextFormat.SKY_BLUE + "Listening on port " + port); this.getLogger().info("Listening on port " + port);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -178,56 +178,56 @@ public class MiraiServer {
} }
private void initSetting(File setting) { private void initSetting(File setting) {
getLogger().info(LoggerTextFormat.SKY_BLUE + "Thanks for using Mirai"); getLogger().info("Thanks for using Mirai");
getLogger().info(LoggerTextFormat.SKY_BLUE + "initializing Settings"); getLogger().info("initializing Settings");
try { try {
if(setting.createNewFile()){ if (setting.createNewFile()) {
getLogger().info(LoggerTextFormat.SKY_BLUE + "Mirai Config Created"); getLogger().info("Mirai Config Created");
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
this.setting = new MiraiSetting(setting); this.setting = new MiraiSetting(setting);
MiraiSettingMapSection network = this.setting.getMapSection("network"); MiraiSettingMapSection network = this.setting.getMapSection("network");
network.set("enable_proxy","not supporting yet"); 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:95");
proxy.add("1.2.3.4:100"); proxy.add("1.2.3.4:100");
MiraiSettingMapSection worker = this.setting.getMapSection("worker"); MiraiSettingMapSection worker = this.setting.getMapSection("worker");
worker.set("core_task_pool_worker_amount",5); worker.set("core_task_pool_worker_amount", 5);
MiraiSettingMapSection plugin = this.setting.getMapSection("plugin"); MiraiSettingMapSection plugin = this.setting.getMapSection("plugin");
plugin.set("debug", false); plugin.set("debug", false);
this.setting.save(); 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); this.qqs = new MiraiConfig(qqConfig);
MiraiConfigSection<Object> section = new MiraiConfigSection<>(); MiraiConfigSection<Object> section = new MiraiConfigSection<>();
System.out.println("/"); System.out.println("/");
Scanner scanner = new Scanner(System.in); 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("Input a " + LoggerTextFormat.RED + " QQ number " + LoggerTextFormat.GREEN + "for default robotNetworkHandler");
getLogger().info(LoggerTextFormat.SKY_BLUE + "输入用于默认机器人的QQ号"); getLogger().info("输入用于默认机器人的QQ号");
long qqNumber = scanner.nextLong(); long qqNumber = scanner.nextLong();
getLogger().info(LoggerTextFormat.SKY_BLUE + "input the password for that QQ account"); getLogger().info("Input the password for that QQ account");
getLogger().info(LoggerTextFormat.SKY_BLUE + "输入该QQ号对应密码"); getLogger().info("输入该QQ号的密码");
String qqPassword = scanner.next(); String qqPassword = scanner.next();
section.put("password",qqPassword); section.put("password", qqPassword);
section.put("owner","default"); section.put("owner", "default");
this.qqs.put(String.valueOf(qqNumber),section); this.qqs.put(String.valueOf(qqNumber), section);
this.qqs.save(); 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.eventManager.broadcastEvent(new ServerEnableEvent());
this.enabled = true; this.enabled = true;
getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai"); getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai");

View File

@ -1,7 +1,10 @@
package net.mamoe.mirai; package net.mamoe.mirai;
import lombok.Getter; 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.network.RobotNetworkHandler;
import net.mamoe.mirai.utils.ContactList;
import net.mamoe.mirai.utils.config.MiraiConfigSection; import net.mamoe.mirai.utils.config.MiraiConfigSection;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,10 +13,8 @@ import java.util.List;
public class Robot { public class Robot {
private final int qq; private final int qqNumber;
private final String password; private final String password;
@Getter @Getter
private final RobotNetworkHandler handler; private final RobotNetworkHandler handler;
@ -23,6 +24,9 @@ public class Robot {
@Getter @Getter
private final List<String> owners; private final List<String> owners;
private final ContactList<Group> groups = new ContactList<>();
private final ContactList<QQ> qqs = new ContactList<>();
public boolean isOwnBy(String ownerName) { public boolean isOwnBy(String ownerName) {
return owners.contains(ownerName); return owners.contains(ownerName);
} }
@ -30,20 +34,36 @@ public class Robot {
public Robot(MiraiConfigSection<Object> data) throws Throwable { public Robot(MiraiConfigSection<Object> data) throws Throwable {
this( this(
data.getIntOrThrow("account", () -> new Exception("can not parse QQ account")), data.getIntOrThrow("account", () -> new IllegalArgumentException("account")),
data.getStringOrThrow("password", () -> new Exception("can not parse QQ password")), data.getStringOrThrow("password", () -> new IllegalArgumentException("password")),
data.getAsOrDefault("owners", ArrayList::new) data.getAsOrDefault("owners", ArrayList::new)
); );
} }
public Robot(int qqNumber, String password, List<String> owners) {
public Robot(int qq, String password, List<String> owners) { this.qqNumber = qqNumber;
this.qq = qq;
this.password = password; this.password = password;
this.owners = Collections.unmodifiableList(owners); 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));
}
} }

View File

@ -1,9 +1,11 @@
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.utils.ContactList
class Group(number: Int) : Contact(number) { class Group(number: Int) : Contact(number) {
val groupId = groupNumberToId(number) val groupId = groupNumberToId(number)
val members = ContactList<QQ>()
init { init {
Instances.groups.add(this) Instances.groups.add(this)
@ -55,6 +57,11 @@ class Group(number: Int) : Contact(number) {
} }
} }
@JvmStatic
fun main(args: Array<String>) {
groupNumberToId(580266363)
}
fun groupIdToNumber(id: Int): Int { fun groupIdToNumber(id: Int): Int {
var left: Int = id.toString().let { var left: Int = id.toString().let {
if (it.length < 6) { if (it.length < 6) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -2,15 +2,20 @@ package net.mamoe.mirai.event.events.robot;
import net.mamoe.mirai.Robot; import net.mamoe.mirai.Robot;
import net.mamoe.mirai.event.events.MiraiEvent; import net.mamoe.mirai.event.events.MiraiEvent;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public abstract class RobotEvent extends MiraiEvent { public abstract class RobotEvent extends MiraiEvent {
private final Robot robot; private final Robot robot;
public RobotEvent(@NotNull Robot robot) {
this.robot = Objects.requireNonNull(robot);
}
@NotNull
public Robot getRobot() { public Robot getRobot() {
return robot; return robot;
} }
public RobotEvent(Robot robot){
this.robot = robot;
}
} }

View File

@ -2,6 +2,6 @@ package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent; import net.mamoe.mirai.event.events.MiraiEvent;
public class ServerDisableEvent extends MiraiEvent { public final class ServerDisableEvent extends MiraiEvent {
} }

View File

@ -2,7 +2,7 @@ package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent; import net.mamoe.mirai.event.events.MiraiEvent;
public class ServerEnableEvent extends MiraiEvent { public final class ServerEnableEvent extends MiraiEvent {
} }

View File

@ -3,9 +3,12 @@ package net.mamoe.mirai.message.defaults;
import net.mamoe.mirai.message.Message; import net.mamoe.mirai.message.Message;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* @author Him188moe * @author Him188moe
@ -21,6 +24,19 @@ public final class MessageChain extends Message {
list.add(tail); list.add(tail);
} }
public MessageChain(@NotNull Message message) {
Objects.requireNonNull(message);
list.add(message);
}
public List<Message> toList() {
return List.copyOf(list);
}
public Stream<Message> stream() {
return new ArrayList<>(list).stream();
}
@Override @Override
public synchronized String toString() { public synchronized String toString() {
return this.list.stream().map(Message::toString).collect(Collectors.joining("")); return this.list.stream().map(Message::toString).collect(Collectors.joining(""));

View File

@ -2,11 +2,14 @@ package net.mamoe.mirai.network
import net.mamoe.mirai.MiraiServer import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.Robot 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.event.events.robot.RobotLoginSucceedEvent
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.login.* 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.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.ServerVerificationCodePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.task.MiraiThreadPool 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 lateinit var cookies: String
private var gtk: Int = 0 private var gtk: Int = 0
private var ignoreMessage: Boolean = false
init { init {
tlv0105 = lazyEncode { tlv0105 = lazyEncode {
@ -197,6 +201,11 @@ class RobotNetworkHandler(val robot: Robot, val number: Int, private val passwor
sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey)) sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS) }, 90000, 90000, TimeUnit.MILLISECONDS)
RobotLoginSucceedEvent(robot).broadcast() RobotLoginSucceedEvent(robot).broadcast()
MiraiThreadPool.getInstance().schedule({
ignoreMessage = false
}, 2, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105 this.tlv0105 = packet.tlv0105
sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE)) 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 -> { is ServerFriendMessageEventPacket -> {
println(packet.toString()) if (ignoreMessage) {
if (packet.message == "牛逼") { return
sendPacket(ClientSendFriendMessagePacket(this.number, packet.qq, this.sessionKey, "牛逼!!"))
} }
//friend message FriendMessageEvent(this.robot, this.robot.getQQ(packet.qq), packet.message)
} }
is ServerGroupMessageEventPacket -> { is ServerGroupMessageEventPacket -> {
//group message //group message
if (packet.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 -> { 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 ServerVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt(this.token00BA))
is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt()) is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseResendPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) 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 ServerMessageEventPacketRawEncoded -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerSendFriendMessageResponsePacket,
is ServerSendGroupMessageResponsePacket -> {
}
else -> throw IllegalArgumentException(packet.toString()) else -> throw IllegalArgumentException(packet.toString())
} }

View File

@ -1,5 +1,7 @@
package net.mamoe.mirai.network.packet 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 net.mamoe.mirai.utils.toUHexString
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.DataInputStream 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) { class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
lateinit var message: String lateinit var xmlMessage: String
override fun decode() { 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 }//todo test
} }
class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var group: Int = 0 var groupNumber: Int = 0
var qq: Int = 0 var qq: Int = 0
lateinit var message: String lateinit var message: String
lateinit var messageType: MessageType lateinit var messageType: MessageType
@ -57,10 +59,10 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
} }
override fun decode() { override fun decode() {
group = this.input.goto(51).readInt() groupNumber = this.input.goto(51).readInt()
qq = this.input.goto(56).readInt() qq = this.input.goto(56).readInt()
val fontLength = this.input.goto(108).readShort() 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()) { messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
19 -> MessageType.NORMAL 19 -> MessageType.NORMAL
@ -102,7 +104,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
message = "[face${faceId}.gif]" 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() var messageLength: Int = this.input.goto(110 + fontLength + 6).readShort().toInt()
message = String(this.input.goto(110 + fontLength + 8).readNBytes(messageLength)) message = String(this.input.goto(110 + fontLength + 8).readNBytes(messageLength))
@ -120,7 +122,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
//读取 nick, ignore. //读取 nick, ignore.
/* /*
when (this.input.goto(110 + fontLength + 3 + oeLength).readByte().toInt()) { when (this.input.goto(110 + fontLength + 3 + oeLength).readByteAt().toInt()) {
12 -> { 12 -> {
this.input.skip(4)//maybe 5? 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) { class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var qq: Int = 0 var qq: Int = 0
lateinit var message: String lateinit var message: String
@ -147,17 +178,17 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
override fun decode() { override fun decode() {
//start at Sep1.0:27 //start at Sep1.0:27
qq = input.readInt(0) qq = input.readIntAt(0)
val msgLength = input.readShort(22) val msgLength = input.readShortAt(22)
val fontLength = input.readShort(93+msgLength) val fontLength = input.readShortAt(93+msgLength)
val offset = msgLength+fontLength val offset = msgLength+fontLength
message = if(input.readByte(97+offset).toUHexString() == "02"){ message = if(input.readByteAt(97+offset).toUHexString() == "02"){
"[face" + input.goto(103+offset).readByte(1).toInt().toString() + ".gif]" "[face" + input.goto(103+offset).readByteAt(1).toInt().toString() + ".gif]"
//.gif //.gif
}else { }else {
val offset2 = input.readShort(101 + offset) val offset2 = input.readShortAt(101 + offset)
input.goto(103 + offset).readVarString(offset2.toInt()) input.goto(103 + offset).readVarString(offset2.toInt())
} }
// TODO("FRIEND 解析")d
} }
} }
*/

View File

@ -145,22 +145,22 @@ infix fun <N : Number> DataInputStream.goto(position: N): DataInputStream {
return this return this
} }
fun <N : Number> DataInputStream.readNBytes(position: N, length: Int): ByteArray { fun <N : Number> DataInputStream.readNBytesAt(position: N, length: Int): ByteArray {
this.goto(position) this.goto(position)
return this.readNBytes(length) return this.readNBytes(length)
} }
fun <N : Number> DataInputStream.readInt(position: N): Int { fun <N : Number> DataInputStream.readIntAt(position: N): Int {
this.goto(position) this.goto(position)
return this.readInt(); return this.readInt();
} }
fun <N : Number> DataInputStream.readByte(position: N): Byte { fun <N : Number> DataInputStream.readByteAt(position: N): Byte {
this.goto(position) this.goto(position)
return this.readByte(); return this.readByte();
} }
fun <N : Number> DataInputStream.readShort(position: N): Short { fun <N : Number> DataInputStream.readShortAt(position: N): Short {
this.goto(position) this.goto(position)
return this.readShort(); return this.readShort();
} }

View File

@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto 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.network.packet.readVarString
import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.utils.TEACryptor import net.mamoe.mirai.utils.TEACryptor
@ -31,16 +31,16 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
* Version 1 @Deprecated * Version 1 @Deprecated
this.input.skip(7)//8 this.input.skip(7)//8
encryptionKey = this.input.readNBytes(16)//24 encryptionKey = this.input.readNBytesAt(16)//24
this.input.skip(2)//25->26 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 this.input.skip(60L)//142
//?? //??
var b = this.input.readNBytes(2) var b = this.input.readNBytesAt(2)
val msgLength = when (b.toUByteArray().toUHexString()) { val msgLength = when (b.toUByteArray().toUHexString()) {
"01 07" -> 0 "01 07" -> 0
"00 33" -> 28 "00 33" -> 28
@ -55,17 +55,17 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
this.input.skip(10)//171+msgLength 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) this.input.skip(2)
token88 = this.input.readNBytes(136)//325+ // msgLength token88 = this.input.readNBytesAt(136)//325+ // msgLength
this.input.skip(299L)//624+msgLength this.input.skip(299L)//624+msgLength
//varString (nickLength bytes) //varString (nickLength bytes)
val nickLength = this.input.readByte().toInt()//625+msgLength val nickLength = this.input.readByteAt().toInt()//625+msgLength
System.out.println(nickLength) System.out.println(nickLength)
@ -76,11 +76,11 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
/* /*
this.input.skip((dataIndex - (625 + msgLength + nickLength)) + 0L)//-31 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 this.input.skip(9)//-27
age = this.input.readShort()//-25 age = this.input.readShortAt()//-25
*/ */
age = 0 age = 0
gender = 0 gender = 0
@ -106,16 +106,16 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
else -> throw IllegalStateException(id) 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() val nickLength = this.input.goto(624 + msgLength).readByte().toInt()
this.nick = this.input.readVarString(nickLength) 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()
} }
} }

View File

@ -2,6 +2,8 @@ package net.mamoe.mirai.network.packet.message
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.lazyEncode
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
@ -21,6 +23,11 @@ class ClientSendGroupMessagePacket(
this.writeHex(Protocol._fixVer) this.writeHex(Protocol._fixVer)
this.encryptAndWrite(sessionKey) { 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.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
it.writeTime() it.writeTime()
it.writeRandom(4) it.writeRandom(4)
@ -28,21 +35,33 @@ class ClientSendGroupMessagePacket(
it.writeZero(2) it.writeZero(2)
//messages //messages
val bytes = message.toByteArray()
it.writeByte(0x2A)
it.writeInt(groupId)
it.writeShort(19 + bytes.size)
it.writeByte(0x01)
it.writeByte(0x01) it.writeByte(0x01)
it.writeShort(bytes.size + 3) it.writeShort(bytes.size + 3)
it.writeByte(0x01) it.writeByte(0x01)
it.writeShort(bytes.size) it.writeShort(bytes.size)
it.write(bytes) 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") @PacketId("00 02")
class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) { class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) {
override fun decode() { override fun decode() {

View File

@ -27,7 +27,7 @@ class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input)
this.unknownBoolean = this.input.readByte().toInt() == 1 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)
} }
} }

View File

@ -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<C extends Contact> extends MiraiSynchronizedLinkedListMap<Integer, C> {
}

View File

@ -1,14 +1,25 @@
package net.mamoe.mirai.utils; package net.mamoe.mirai.utils;
import net.mamoe.mirai.MiraiServer;
public enum LoggerTextFormat { public enum LoggerTextFormat {
RED("\033[31m"), RESET("\33[0m"),
GREEN("\033[32m"),
YELLOW("\033[33m"), BLUE("\033[0;34m"),
BLUE("\033[34m"), BLACK("\033[0;30m"),
SKY_BLUE("\033[36m"), DARK_GREY("\033[1;30m"),
RESET("\33[0m"); 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; private String format;
LoggerTextFormat(String format) { LoggerTextFormat(String format) {
@ -17,9 +28,9 @@ public enum LoggerTextFormat {
@Override @Override
public String toString() { public String toString() {
if(MiraiServer.getInstance().isUnix()){ //if(MiraiServer.getInstance().isUnix()){
return format; return format;
} // }
return ""; // return "";
} }
} }

View File

@ -7,21 +7,16 @@ import java.util.*
* used to replace old logger * used to replace old logger
*/ */
object MiraiLogger { 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 log(o: Any?) = info(o)
infix fun println(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) { infix fun catching(e: Throwable) {
e.printStackTrace() e.printStackTrace()
@ -31,7 +26,7 @@ object MiraiLogger {
this.print(e.cause.toString())*/ 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()) val s = SimpleDateFormat("MM-dd HH:mm:ss").format(Date())
kotlin.io.println("$color[Mirai] $s : $value") kotlin.io.println("$color[Mirai] $s : $value")
} }
@ -43,3 +38,5 @@ fun Any.logInfo() = MiraiLogger.info(this)
fun Any.logDebug() = MiraiLogger.debug(this) fun Any.logDebug() = MiraiLogger.debug(this)
fun Any.logError() = MiraiLogger.error(this) fun Any.logError() = MiraiLogger.error(this)
fun Any.logNotice() = MiraiLogger.notice(this)

View File

@ -6,12 +6,10 @@ import net.mamoe.mirai.network.Protocol;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
/** /**
@ -213,16 +211,16 @@ public class HexComparator {
} }
public static void main(String[] args) { public static void main(String[] args) {
/*
System.out.println(HexComparator.compare( System.out.println(HexComparator.compare(
//mirai //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 //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( System.out.println(HexComparator.compare(