Commit 93dc8e58 authored by liujiahua123123's avatar liujiahua123123

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core/src/main/java/net/mamoe/mirai/Robot.java
parents 5b07f09d 9a0092ff
...@@ -15,23 +15,25 @@ The project is all for <b>learning proposes</b> and still in <b>developing stage ...@@ -15,23 +15,25 @@ The project is all for <b>learning proposes</b> and still in <b>developing stage
### 代码结构 ### 代码结构
Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持). Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持).
与插件相关性强(或其他在二次开发中容易接触的部分)均使用 Java 完成, 与插件相关性强(或其他在二次开发中容易接触)的部分使用 Java 完成,
同时也会针对kotlin提供优化的方法调用. 例如对'+'操作符的重载: `String+BufferedImage+QQ.At+Face+URL+String+File` 将会被自动处理为String消息. 同时也会针对kotlin提供优化的方法调用. 例如对'+'操作符的重载: `String+BufferedImage+QQ.At+Face+URL+String+File` 将会被自动处理为String消息.
### TODO ### TODO
- [x] 事件(Event)模块 - [x] 事件(Event)模块
- [ ] 插件(Plugin)模块 **(Working on)** - [ ] 插件(Plugin)模块
- [x] Network - Touch - [x] Network - Touch
- [X] Network - Login - [X] Network - Login
- [X] Network - Session - [X] Network - Session
- [ ] Network - Verification Code (Low priority) - [ ] Network - Verification Code **(Working on)**
- [X] Network - Message Receiving - [X] Network - Message Receiving
- [X] Network - Message Sending - [X] Network - Message Sending
- [ ] Network - Events **(Working on)** - [ ] Network - Events **(Working on)**
- [ ] Robot - Friend/group list - [ ] Robot - Friend/group list
- [ ] Robot - Actions(joining group, adding friend, etc.)
- [ ] Message Section **(Working on)** - [ ] Message Section **(Working on)**
- [ ] Contact - [ ] Contact
- [ ] UI **(Working on)**
<br> <br>
......
...@@ -56,6 +56,12 @@ ...@@ -56,6 +56,12 @@
<artifactId>snakeyaml</artifactId> <artifactId>snakeyaml</artifactId>
<version>1.18</version> <version>1.18</version>
</dependency> </dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.3.41</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
......
package net.mamoe.mirai; package net.mamoe.mirai;
import net.mamoe.mirai.utils.config.MiraiConfig;
import net.mamoe.mirai.utils.config.MiraiConfigSection;
/** /**
* @author Him188moe * @author Him188moe
*/ */
public final class MiraiMain { public final class MiraiMain {
private static MiraiServer server; private static MiraiServer server;
public static void main(String[] args) { public static void main(String[] args) {
server = new MiraiServer(); server = new MiraiServer();
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> server.shutdown()));
server.shutdown();
}));
MiraiConfig config = new MiraiConfig("QQ.yml");
MiraiConfigSection<Object> data = config.getSection("123123");
data.put("account","123123a");
try {
Robot robot = new Robot(data);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
} }
} }
...@@ -2,24 +2,25 @@ package net.mamoe.mirai; ...@@ -2,24 +2,25 @@ package net.mamoe.mirai;
import lombok.Getter; import lombok.Getter;
import net.mamoe.mirai.event.MiraiEventManager; import net.mamoe.mirai.event.MiraiEventManager;
import net.mamoe.mirai.event.events.server.ServerDisableEvent; import net.mamoe.mirai.event.events.server.ServerDisabledEvent;
import net.mamoe.mirai.event.events.server.ServerEnableEvent; import net.mamoe.mirai.event.events.server.ServerEnabledEvent;
import net.mamoe.mirai.network.RobotNetworkHandler; import net.mamoe.mirai.network.packet.login.LoginState;
import net.mamoe.mirai.network.packet.ClientTouchPacket;
import net.mamoe.mirai.task.MiraiTaskManager; import net.mamoe.mirai.task.MiraiTaskManager;
import net.mamoe.mirai.utils.LoggerTextFormat; import net.mamoe.mirai.utils.LoggerTextFormat;
import net.mamoe.mirai.utils.MiraiLogger; import net.mamoe.mirai.utils.MiraiLogger;
import net.mamoe.mirai.utils.config.MiraiConfig; import net.mamoe.mirai.utils.config.MiraiConfig;
import net.mamoe.mirai.utils.config.MiraiConfigSection; import net.mamoe.mirai.utils.config.MiraiConfigSection;
import net.mamoe.mirai.utils.setting.MiraiSetting;
import net.mamoe.mirai.utils.setting.MiraiSettingListSection; import net.mamoe.mirai.utils.setting.MiraiSettingListSection;
import net.mamoe.mirai.utils.setting.MiraiSettingMapSection; import net.mamoe.mirai.utils.setting.MiraiSettingMapSection;
import net.mamoe.mirai.utils.setting.MiraiSettings;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.LinkedList;
import java.util.Scanner; import java.util.Scanner;
/**
* @author NaturalHG
*/
public class MiraiServer { public class MiraiServer {
private static MiraiServer instance; private static MiraiServer instance;
...@@ -27,17 +28,15 @@ public class MiraiServer { ...@@ -27,17 +28,15 @@ public class MiraiServer {
return instance; return instance;
} }
//mirai version
private final static String MIRAI_VERSION = "1.0.0"; private final static String MIRAI_VERSION = "1.0.0";
//qq version
private final static String QQ_VERSION = "4.9.0"; private final static String QQ_VERSION = "4.9.0";
@Getter //is running under UNIX @Getter //is running under UNIX
private boolean unix; private boolean unix;
@Getter//file path @Getter//file pathq
public File parentFolder; public File parentFolder;
@Getter @Getter
...@@ -48,30 +47,30 @@ public class MiraiServer { ...@@ -48,30 +47,30 @@ public class MiraiServer {
@Getter @Getter
MiraiLogger logger; MiraiLogger logger;
MiraiSetting setting; MiraiSettings settings;
MiraiConfig qqs; MiraiConfig qqs;
protected MiraiServer() { MiraiServer() {
instance = this; instance = this;
this.onLoad(); this.onLoaded();
this.onEnable(); this.onEnabled();
} }
private boolean enabled; private boolean enabled;
protected void shutdown() { void shutdown() {
if (this.enabled) { if (this.enabled) {
getLogger().info("About to shutdown Mirai"); getLogger().info("About to shutdown Mirai");
this.getEventManager().broadcastEvent(new ServerDisableEvent()); this.eventManager.broadcastEventAsync(new ServerDisabledEvent());
getLogger().info("Data have been saved"); getLogger().info("Data have been saved");
} }
} }
private void onLoad() { private void onLoaded() {
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");
...@@ -88,7 +87,7 @@ public class MiraiServer { ...@@ -88,7 +87,7 @@ public class MiraiServer {
if (!setting.exists()) { if (!setting.exists()) {
this.initSetting(setting); this.initSetting(setting);
} else { } else {
this.setting = new MiraiSetting(setting); this.settings = new MiraiSettings(setting);
} }
File qqs = new File(this.parentFolder + "/QQ.yml"); File qqs = new File(this.parentFolder + "/QQ.yml");
...@@ -116,65 +115,6 @@ public class MiraiServer { ...@@ -116,65 +115,6 @@ public class MiraiServer {
}); });
*/ */
getLogger().info("ready to connect");
/*
MiraiConfigSection section = new MiraiConfigSection<MiraiConfigSection<String>>(){{
put("1",new MiraiConfigSection<>(){{
put("1","0");
}});
}};
this.qqs.put("test",section);
this.qqs.save();
*/
MiraiConfigSection<MiraiConfigSection> x = this.qqs.getTypedSection("test");
//System.out.println(x.getSection("1").getInt("1"));
/*
System.out.println(v);
System.out.println(v.get("1111"));
*/
Robot robot = new Robot(1994701021, "xiaoqqq", new LinkedList<>());
RobotNetworkHandler robotNetworkHandler = robot.getHandler();
try {
//System.out.println(Protocol.Companion.getSERVER_IP().get(3));
//System.out.println(Protocol.Companion.getSERVER_IP().toString());
robotNetworkHandler.setServerIP("14.116.136.106");
robotNetworkHandler.sendPacket(new ClientTouchPacket(1994701021, "14.116.136.106"));
while (true) ;
//robotNetworkHandler.connect("14.116.136.106");
//robotNetworkHandler.connect(Protocol.Companion.getSERVER_IP().get(2));
//robotNetworkHandler.connect("125.39.132.242");
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
/*
System.out.println("network test");
try {
MiraiUDPServer server = new MiraiUDPServer();
MiraiUDPClient client = new MiraiUDPClient(InetAddress.getLocalHost(),9999,MiraiNetwork.getAvailablePort());
this.getTaskManager().repeatingTask(() -> {
byte[] sendInfo = "test test".getBytes(StandardCharsets.UTF_8);
try {
client.send(new DatagramPacket(sendInfo,sendInfo.length));
} catch (IOException e) {
e.printStackTrace();
}
},300);
} catch (IOException e) {
e.printStackTrace();
}*/
} }
private void initSetting(File setting) { private void initSetting(File setting) {
...@@ -187,21 +127,21 @@ public class MiraiServer { ...@@ -187,21 +127,21 @@ public class MiraiServer {
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
this.setting = new MiraiSetting(setting); this.settings = new MiraiSettings(setting);
MiraiSettingMapSection network = this.setting.getMapSection("network"); MiraiSettingMapSection network = this.settings.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.settings.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.settings.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.settings.getMapSection("plugin");
plugin.set("debug", false); plugin.set("debug", false);
this.setting.save(); this.settings.save();
getLogger().info("initialized; changing can be made in setting file: " + setting.toString()); getLogger().info("initialized; changing can be made in setting file: " + setting.toString());
} }
...@@ -227,11 +167,35 @@ public class MiraiServer { ...@@ -227,11 +167,35 @@ public class MiraiServer {
getLogger().info("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 onEnabled() {
this.eventManager.broadcastEvent(new ServerEnableEvent());
this.enabled = true; this.enabled = true;
this.eventManager.broadcastEventAsync(new ServerEnabledEvent());
getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai"); getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai");
getLogger().info("Mirai Version=" + MiraiServer.MIRAI_VERSION + " QQ Version=" + MiraiServer.QQ_VERSION); getLogger().info("Mirai Version=" + MiraiServer.MIRAI_VERSION + " QQ Version=" + MiraiServer.QQ_VERSION);
getLogger().info("Initializing [Robot]s");
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
getLogger().info("Initializing [Robot] " + section.getString("account"));
try {
Robot robot = new Robot(section);
var state = robot.network.tryLogin$mirai_core().get();
//robot.network.tryLogin$mirai_core().whenComplete((state, e) -> {
if (state == LoginState.SUCCEED) {
Robot.instances.add(robot);
getLogger().info(" Succeed");
} else {
getLogger().error(" Failed with error " + state);
robot.close();
}
// }).get();
} catch (Throwable e) {
e.printStackTrace();
getLogger().error("Could not load QQ robots config!");
System.exit(1);
}
});
} }
......
package net.mamoe.mirai; package net.mamoe.mirai;
import kotlin.jvm.internal.MagicApiIntrinsics;
import lombok.Getter; import lombok.Getter;
import net.mamoe.mirai.contact.Group; import net.mamoe.mirai.contact.Group;
import net.mamoe.mirai.contact.QQ; 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.ContactList;
import net.mamoe.mirai.utils.config.MiraiConfig; import net.mamoe.mirai.utils.RobotAccount;
import net.mamoe.mirai.utils.config.MiraiConfigSection; import net.mamoe.mirai.utils.config.MiraiConfigSection;
import org.jetbrains.annotations.NotNull;
import java.io.Closeable;
import java.util.*;
/**
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人账号.
* <br>
* {@link Robot} 由 2 个模块组成.
* {@linkplain ContactSystem 联系人管理}: 可通过 {@link Robot#contacts} 访问
* {@linkplain RobotNetworkHandler 网络处理器}: 可通过 {@link Robot#network} 访问
* <br>
* 另外地, 若你需要得到机器人的 QQ 账号, 请访问 {@link Robot#account}
* 若你需要得到服务器上所有机器人列表, 请访问 {@link Robot#instances}
*
* @author Him188moe
* @author NatrualHG
* @see net.mamoe.mirai.contact.Contact
*
* <p>
* Robot that is the base of the whole program.
* It contains a {@link ContactSystem}, which manage contacts such as {@link QQ} and {@link Group}.
*/
public final class Robot implements Closeable {
public static final List<Robot> instances = Collections.synchronizedList(new LinkedList<>());
public final RobotAccount account;
public final ContactSystem contacts = new ContactSystem();
public final RobotNetworkHandler network;
import java.util.ArrayList; /**
import java.util.Collections; * Robot 联系人管理.
import java.util.List; *
* @see Robot#contacts
*/
public final class ContactSystem {
private final ContactList<Group> groups = new ContactList<>();
private final ContactList<QQ> qqs = new ContactList<>();
public class Robot { private ContactSystem() {
}
public QQ getQQ(long qqNumber) {
if (!this.qqs.containsKey(qqNumber)) {
this.qqs.put(qqNumber, new QQ(Robot.this, qqNumber));
}
return this.qqs.get(qqNumber);
}
public Group getGroupByNumber(long groupNumber) {
if (!this.groups.containsKey(groupNumber)) {
this.groups.put(groupNumber, new Group(Robot.this, groupNumber));
}
return groups.get(groupNumber);
}
public Group getGroupById(long groupId) {
return getGroupByNumber(Group.Companion.groupIdToNumber(groupId));
}
}
private final int qqNumber;
private final String password;
@Getter
private final RobotNetworkHandler handler;
/** /**
* Ref list * Ref list
...@@ -26,66 +79,35 @@ public class Robot { ...@@ -26,66 +79,35 @@ 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);
} }
public Robot(MiraiConfigSection<Object> data) throws Throwable { public Robot(MiraiConfigSection<Object> data) throws Throwable {
this( this(
data.getIntOrThrow("account", () -> new IllegalArgumentException("account")), new RobotAccount(
data.getStringOrThrow("password", () -> new IllegalArgumentException("password")), data.getLongOrThrow("account", () -> new IllegalArgumentException("account")),
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(@NotNull RobotAccount account, @NotNull List<String> owners) {
this.qqNumber = qqNumber; Objects.requireNonNull(account);
this.password = password; Objects.requireNonNull(owners);
this.account = account;
this.owners = Collections.unmodifiableList(owners); this.owners = Collections.unmodifiableList(owners);
this.handler = new RobotNetworkHandler(this, this.qqNumber, this.password); this.network = new RobotNetworkHandler(this);
}
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));
}
/* Attribute
* Attribute will be SAVED and LOAD automatically as long as the QQ account is same
* {Attributes} is in the format of Map<String, Object>, keeping thread-safe
* {Attributes} is a KEY-VALUE typed Data.
* *
**/
}
/*
class RobotAttribute extends MiraiConfigSection<Object>{
static RobotAttribute load(Robot robot){
public void close() {
this.network.close();
this.contacts.groups.values().forEach(Group::close);
this.contacts.groups.clear();
this.contacts.qqs.clear();
} }
private MiraiConfigSection<Object> data;//late init
} }
*/
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.Robot
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.message.defaults.PlainText
/** /**
* A contact is a [QQ] or a [Group] for one particular [Robot] instance only. * A contact is a [QQ] or a [Group] for one particular [Robot] instance only.
* *
* @param robot Owner [Robot]
* @author Him188moe * @author Him188moe
*/ */
abstract class Contact(val number: Int) { abstract class Contact(val robot: Robot, val number: Long) {
/** /**
* Async * Async
......
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.Robot
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.utils.ContactList import net.mamoe.mirai.utils.ContactList
import java.io.Closeable
class Group(number: Int) : Contact(number) { class Group(robot: Robot, number: Long) : Contact(robot, number), Closeable {
val groupId = groupNumberToId(number) val groupId = groupNumberToId(number)
val members = ContactList<QQ>() val members = ContactList<QQ>()
init {
Instances.groups.add(this)
}
override fun sendMessage(message: Message) { override fun sendMessage(message: Message) {
robot.network.messageHandler.sendGroupMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override fun sendXMLMessage(message: String) {
} }
override fun close() {
this.members.clear()
}
companion object { companion object {
fun groupNumberToId(number: Int): Int { fun groupNumberToId(number: Long): Long {
val left: Int = number.toString().let { val left: Long = number.toString().let {
if (it.length < 6) { if (it.length < 6) {
return@groupNumberToId number return@groupNumberToId number
} }
it.substring(0, it.length - 6).toInt() it.substring(0, it.length - 6).toLong()
} }
val right: Int = number.toString().let { val right: Long = number.toString().let {
it.substring(it.length - 6).toInt() it.substring(it.length - 6).toLong()
} }
return when (left) { return when (left) {
in 1..10 -> { in 1..10 -> {
((left + 202).toString() + right.toString()).toInt() ((left + 202).toString() + right.toString()).toLong()
} }
in 11..19 -> { in 11..19 -> {
((left + 469).toString() + right.toString()).toInt() ((left + 469).toString() + right.toString()).toLong()
} }
in 20..66 -> { in 20..66 -> {
((left + 208).toString() + right.toString()).toInt() ((left + 208).toString() + right.toString()).toLong()
} }
in 67..156 -> { in 67..156 -> {
((left + 1943).toString() + right.toString()).toInt() ((left + 1943).toString() + right.toString()).toLong()
} }
in 157..209 -> { in 157..209 -> {
((left + 199).toString() + right.toString()).toInt() ((left + 199).toString() + right.toString()).toLong()
} }
in 210..309 -> { in 210..309 -> {
((left + 389).toString() + right.toString()).toInt() ((left + 389).toString() + right.toString()).toLong()
} }
in 310..499 -> { in 310..499 -> {
((left + 349).toString() + right.toString()).toInt() ((left + 349).toString() + right.toString()).toLong()
} }
else -> number else -> number
} }
} }
@JvmStatic fun groupIdToNumber(id: Long): Long {
fun main(args: Array<String>) { var left: Long = id.toString().let {
groupNumberToId(580266363)
}
fun groupIdToNumber(id: Int): Int {
var left: Int = id.toString().let {
if (it.length < 6) { if (it.length < 6) {
return@groupIdToNumber id return@groupIdToNumber id
} }
it.substring(0 until it.length - 6).toInt() it.substring(0 until it.length - 6).toLong()
} }
return when (left) { return when (left) {
in 203..212 -> { in 203..212 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 6).toInt() it.substring(it.length - 6).toLong()
} }
((left - 202).toString() + right.toString()).toInt() ((left - 202).toString() + right.toString()).toLong()
} }
in 480..488 -> { in 480..488 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 6).toInt() it.substring(it.length - 6).toLong()
} }
((left - 469).toString() + right.toString()).toInt() ((left - 469).toString() + right.toString()).toLong()
} }
in 2100..2146 -> { in 2100..2146 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 7).toInt() it.substring(it.length - 7).toLong()
} }
left = left.toString().substring(0 until 3).toInt() left = left.toString().substring(0 until 3).toLong()
((left - 208).toString() + right.toString()).toInt() ((left - 208).toString() + right.toString()).toLong()
} }
in 2010..2099 -> { in 2010..2099 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 6).toInt() it.substring(it.length - 6).toLong()
} }
((left - 1943).toString() + right.toString()).toInt() ((left - 1943).toString() + right.toString()).toLong()
} }
in 2147..2199 -> { in 2147..2199 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 7).toInt() it.substring(it.length - 7).toLong()
} }
left = left.toString().substring(0 until 3).toInt() left = left.toString().substring(0 until 3).toLong()
((left - 199).toString() + right.toString()).toInt() ((left - 199).toString() + right.toString()).toLong()
} }
in 4100..4199 -> { in 4100..4199 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 7).toInt() it.substring(it.length - 7).toLong()
} }
left = left.toString().substring(0 until 3).toInt() left = left.toString().substring(0 until 3).toLong()
((left - 389).toString() + right.toString()).toInt() ((left - 389).toString() + right.toString()).toLong()
} }
in 3800..3989 -> { in 3800..3989 -> {
val right: Int = id.toString().let { val right: Long = id.toString().let {
it.substring(it.length - 7).toInt() it.substring(it.length - 7).toLong()
} }
left = left.toString().substring(0 until 3).toInt() left = left.toString().substring(0 until 3).toLong()
((left - 349).toString() + right.toString()).toInt() ((left - 349).toString() + right.toString()).toLong()
} }
else -> id else -> id
} }
......
package net.mamoe.mirai.contact
fun Int.asQQ(): QQ = Instances.qqs.stream().filter { t: QQ? -> t?.number?.equals(this)!! }.findAny().orElse(QQ(this))!!
fun Int.asGroup(): Group = Instances.groups.stream().filter { t: Group? -> t?.number?.equals(this)!! }.findAny().orElse(Group(this))!!
fun String.withImage(id: String, type: String) = "{$id}.$type"
fun String.withAt(qq: Int) = qq.asQQ().at()
fun String.withAt(qq: QQ) = qq.at()
object Instances {
var qqs = arrayListOf<QQ>()
var groups = arrayListOf<Group>()
}
package net.mamoe.mirai.contact package net.mamoe.mirai.contact
import net.mamoe.mirai.Robot
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.At import net.mamoe.mirai.message.defaults.At
/** /**
* QQ 账号.
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Robot].
*
* A QQ instance helps you to receive message from or send message to.
* Notice that, one QQ instance belong to one [Robot], that is, QQ instances from different [Robot] are NOT the same.
*
* @author Him188moe * @author Him188moe
*/ */
class QQ(number: Int) : Contact(number) { class QQ(robot: Robot, number: Long) : Contact(robot, number) {
init {
Instances.qqs.add(this)
}
override fun sendMessage(message: Message) { override fun sendMessage(message: Message) {
robot.network.messageHandler.sendFriendMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override fun sendXMLMessage(message: String) {
...@@ -21,6 +24,8 @@ class QQ(number: Int) : Contact(number) { ...@@ -21,6 +24,8 @@ class QQ(number: Int) : Contact(number) {
/** /**
* At(@) this account. * At(@) this account.
*
* @return an instance of [Message].
*/ */
fun at(): At { fun at(): At {
return At(this) return At(this)
......
package net.mamoe.mirai.event;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/**
* 实现这个接口的事件可以被异步执行或阻塞执行
*
* @author Him188moe
* @see AsyncEventKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/
public interface AsyncEvent {
default CompletableFuture<? extends AsyncEvent> broadcastAsync() {
return MiraiEventManager.getInstance().broadcastEventAsync(this);
}
@SuppressWarnings("unchecked")
default <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(Consumer<E> callback) {
return MiraiEventManager.getInstance().broadcastEventAsync((E) this, callback);
}
@SuppressWarnings("unchecked")
default <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(Runnable callback) {
return MiraiEventManager.getInstance().broadcastEventAsync((E) this, callback);
}
}
@file:JvmName("AsyncEventKt")
package net.mamoe.mirai.event
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
fun <E : AsyncEvent> E.broadcastAsync(callback: Consumer<E>): CompletableFuture<E> {
return MiraiEventManager.getInstance().broadcastEventAsync(this, callback)
}
fun <E : AsyncEvent> E.broadcastAsync(callback: Runnable): CompletableFuture<E> {
return MiraiEventManager.getInstance().broadcastEventAsync(this, callback)
}
fun <E : AsyncEvent> E.broadcastAsyncSmart(): CompletableFuture<E> {
return MiraiEventManager.getInstance().broadcastEventAsync(this)
}
\ No newline at end of file
package net.mamoe.mirai.event.events; package net.mamoe.mirai.event;
/** /**
* @author NaturalHG * @author NaturalHG
......
package net.mamoe.mirai.event.events; package net.mamoe.mirai.event;
import net.mamoe.mirai.event.MiraiEventManager;
import net.mamoe.mirai.utils.EventException; import net.mamoe.mirai.utils.EventException;
import java.util.function.Consumer; /**
* @author NatrualHG
* @see AsyncEvent
*/
public abstract class MiraiEvent { public abstract class MiraiEvent {
private boolean cancelled; private boolean cancelled;
public boolean isCancelled() { public boolean isCancelled() {
if (!(this instanceof Cancellable)) { if (!(this instanceof Cancellable)) {
throw new EventException("Event is not Cancellable"); return false;
} }
return this.cancelled; return this.cancelled;
} }
...@@ -27,27 +28,8 @@ public abstract class MiraiEvent { ...@@ -27,27 +28,8 @@ public abstract class MiraiEvent {
this.cancelled = value; this.cancelled = value;
} }
protected String eventName;
public String getEventName() {
if (this.eventName == null) {
return this.getClass().getSimpleName();
}
return this.eventName;
}
public final MiraiEvent broadcast() { public final MiraiEvent broadcast() {
MiraiEventManager.getInstance().broadcastEvent(this); MiraiEventManager.getInstance().broadcastEvent(this);
return this; return this;
} }
@SuppressWarnings("unchecked")
public final <D extends MiraiEvent> void asyncBroadcast(Consumer<D> callback) {
MiraiEventManager.getInstance().asyncBroadcastEvent((D) this, callback);
}
@SuppressWarnings("unchecked")
public final <D extends MiraiEvent> void asyncBroadcast(Runnable callback) {
MiraiEventManager.getInstance().asyncBroadcastEvent((D) this, callback);
}
} }
...@@ -2,20 +2,21 @@ package net.mamoe.mirai.event; ...@@ -2,20 +2,21 @@ package net.mamoe.mirai.event;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.mamoe.mirai.event.events.Cancellable;
import net.mamoe.mirai.event.events.MiraiEvent;
import java.io.Closeable; import java.io.Closeable;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
/**
* @author NatrualHG
*/
public class MiraiEventHook<T extends MiraiEvent> implements Closeable { public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
@Getter @Getter
Class<T> eventClass; Class<T> eventClass;
@Getter @Getter
private volatile Consumer<T> handler; protected volatile Consumer<T> handler;
@Getter @Getter
private volatile int priority = 0; private volatile int priority = 0;
...@@ -31,7 +32,7 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { ...@@ -31,7 +32,7 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
* return true -> this hook need to be removed * return true -> this hook need to be removed
*/ */
@Getter @Getter
private Predicate<T> valid; protected Predicate<T> validChecker;
public MiraiEventHook(Class<T> eventClass) { public MiraiEventHook(Class<T> eventClass) {
this(eventClass,null); this(eventClass,null);
...@@ -58,26 +59,26 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { ...@@ -58,26 +59,26 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
} }
private MiraiEventHook<T> setValid(Predicate<T> valid) { private MiraiEventHook<T> setValidChecker(Predicate<T> validChecker) {
this.valid = valid; this.validChecker = validChecker;
return this; return this;
} }
public MiraiEventHook<T> setValidUntil(Predicate<T> valid) { public MiraiEventHook<T> setValidUntil(Predicate<T> valid) {
return this.setValid(valid); return this.setValidChecker(valid);
} }
public MiraiEventHook<T> setValidWhile(Predicate<T> valid) { public MiraiEventHook<T> setValidWhile(Predicate<T> valid) {
return this.setValid(valid.negate()); return this.setValidChecker(valid.negate());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean accept(MiraiEvent event) { public boolean accept(MiraiEvent event) {
if(!(event instanceof Cancellable && event.isCancelled() && this.isIgnoreCancelled())){ if(!(event instanceof Cancellable && event.isCancelled() && this.isIgnoreCancelled())){
this.getHandler().accept((T) event); this.getHandler().accept((T) event);
} }
return this.valid == null || this.valid.test((T) event); return this.validChecker == null || this.validChecker.test((T) event);
} }
/** /**
...@@ -103,6 +104,6 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { ...@@ -103,6 +104,6 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable {
@Override @Override
public void close(){ public void close(){
this.handler = null; this.handler = null;
this.valid = null; this.validChecker = null;
} }
} }
package net.mamoe.mirai.event
import java.util.function.Consumer
import java.util.function.Predicate
/**
* @author Him188moe
*/
class MiraiEventHookKt<E : MiraiEvent>(eventClass: Class<E>) : MiraiEventHook<E>(eventClass) {
fun onEvent(handler: (E) -> Unit) {
this@MiraiEventHookKt.handler = Consumer(handler)
}
fun validChecker(predicate: (E) -> Boolean) {
this@MiraiEventHookKt.validChecker = Predicate(predicate)
}
}
/**
* Kotlin 风格回调
* 你的代码可以这样(并且 validChecker 是可选的):
*
* event.hook {
* onEvent {}
* validChecker {}
* }
*/
fun <E : MiraiEvent> E.hook(handler: MiraiEventHookKt<E>.() -> Unit): MiraiEventHookKt<E> {
return MiraiEventHookKt(this.javaClass).apply(handler)
}
\ No newline at end of file
@file:JvmName("MiraiEventKt")
package net.mamoe.mirai.event
fun <E : MiraiEvent> E.broadcastSmart(): E {
MiraiEventManager.getInstance().broadcastEvent(this as MiraiEvent)
return this
}
\ No newline at end of file
package net.mamoe.mirai.event; package net.mamoe.mirai.event;
import net.mamoe.mirai.MiraiServer;
import net.mamoe.mirai.event.events.MiraiEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* 线程安全的事件管理器.
*
* @author NaturalHG
* @see MiraiEventManagerKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/
public class MiraiEventManager { public class MiraiEventManager {
private MiraiEventManager() { MiraiEventManager() {
} }
private static MiraiEventManager instance = new MiraiEventManager();
public static MiraiEventManager getInstance() { public static MiraiEventManager getInstance() {
return MiraiEventManager.instance; return EventManager.INSTANCE;//实例来自 kotlin 的 singleton
} }
private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock();
...@@ -117,23 +119,40 @@ public class MiraiEventManager { ...@@ -117,23 +119,40 @@ public class MiraiEventManager {
} }
public void asyncBroadcastEvent(MiraiEvent event) { public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event) {
this.asyncBroadcastEvent(event, a -> { Objects.requireNonNull(event);
if (!(event instanceof MiraiEvent)) {
throw new IllegalArgumentException("event must be instanceof MiraiEvent");
}
CompletableFuture<E> future = new CompletableFuture<>();
future.completeAsync(() -> {
MiraiEventManager.this.broadcastEvent((MiraiEvent) event);
return event;
}); });
return future;
} }
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Consumer<D> callback) { public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event, Consumer<E> callback) {
MiraiServer.getInstance().getTaskManager().ansycTask(() -> { Objects.requireNonNull(event);
MiraiEventManager.this.broadcastEvent(event); Objects.requireNonNull(callback);
if (!(event instanceof MiraiEvent)) {
throw new IllegalArgumentException("event must be instanceof MiraiEvent");
}
CompletableFuture<E> future = new CompletableFuture<>();
future.whenComplete((a, b) -> callback.accept(event));
future.completeAsync(() -> {
MiraiEventManager.this.broadcastEvent((MiraiEvent) event);
return event; return event;
}, callback); });
return future;
} }
public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Runnable callback) { public <D extends AsyncEvent> CompletableFuture<D> broadcastEventAsync(D event, Runnable callback) {
asyncBroadcastEvent(event, t -> callback.run()); return broadcastEventAsync(event, t -> callback.run());
} }
} }
......
@file:JvmName("MiraiEventManagerKt")
package net.mamoe.mirai.event package net.mamoe.mirai.event
import net.mamoe.mirai.event.events.MiraiEvent
import kotlin.reflect.KClass import kotlin.reflect.KClass
/**
* [MiraiEventManager] 的 kotlin 简易化实现.
* 若要 hook 一个事件, 你可以:
* FriendMessageEvent::class.hookOnce {}
* FriendMessageEvent::class.hookAlways {}
*
* @author Him188moe
*/
object EventManager : MiraiEventManager()
/**
* 每次事件触发时都会调用 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) { fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook)) MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook))
} }
/**
* 当下一次事件触发时调用 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook)) MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook))
} }
/**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/
fun <C : Class<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) { fun <C : Class<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple<E>(this, hook)) MiraiEventManager.getInstance().hookAlways(MiraiEventHookSimple(this, hook))
} }
/**
* 每次事件触发时都会调用 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) { fun <C : KClass<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
this.java.hookAlways(hook) this.java.hookAlways(hook)
} }
/**
* 当下一次事件触发时调用 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) {
this.java.hookOnce(hook) this.java.hookOnce(hook)
} }
/**
* 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook
*/
fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) { fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) {
this.java.hookWhile(hook) this.java.hookWhile(hook)
} }
......
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.Cancellable;
import net.mamoe.mirai.network.packet.ClientPacket;
import org.jetbrains.annotations.NotNull;
/**
* Packet 已经 {@link ClientPacket#encode()}, 即将被发送
*
* @author Him188moe
*/
public final class BeforePacketSendEvent extends ClientPacketEvent implements Cancellable {
public BeforePacketSendEvent(@NotNull ClientPacket packet) {
super(packet);
}
}
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.network.packet.ClientPacket;
import org.jetbrains.annotations.NotNull;
/**
* @author Him188moe
*/
public abstract class ClientPacketEvent extends PacketEvent {
public ClientPacketEvent(@NotNull ClientPacket packet) {
super(packet);
}
@Override
public ClientPacket getPacket() {
return (ClientPacket) super.getPacket();
}
}
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.MiraiEvent;
import net.mamoe.mirai.network.packet.Packet;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* @author Him188moe
*/
public abstract class PacketEvent extends MiraiEvent {
private final Packet packet;
public PacketEvent(@NotNull Packet packet) {
this.packet = Objects.requireNonNull(packet);
}
public Packet getPacket() {
return packet;
}
}
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.network.packet.ClientPacket;
import org.jetbrains.annotations.NotNull;
/**
* Packet 已经发出
*
* @author Him188moe
*/
public final class PacketSentEvent extends ClientPacketEvent {
public PacketSentEvent(@NotNull ClientPacket packet) {
super(packet);
}
}
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.network.packet.ServerPacket;
/**
* @author Him188moe
*/
public abstract class ServerPacketEvent extends PacketEvent {
public ServerPacketEvent(ServerPacket packet) {
super(packet);
}
@Override
public ServerPacket getPacket() {
return (ServerPacket) super.getPacket();
}
}
package net.mamoe.mirai.event.events.network;
import net.mamoe.mirai.event.Cancellable;
import net.mamoe.mirai.network.packet.ServerPacket;
import net.mamoe.mirai.network.packet.ServerVerificationCodePacket;
/**
* 服务器接到某数据包时触发这个事件.
* 注意, 当接收到数据包的加密包(如 {@link ServerVerificationCodePacket.Encrypted})也会触发这个事件, 随后才会
*
* @author Him188moe
*/
public final class ServerPacketReceivedEvent extends ServerPacketEvent implements Cancellable {
public ServerPacketReceivedEvent(ServerPacket packet) {
super(packet);
}
}
package net.mamoe.mirai.event.events.robot; 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.MiraiEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Objects; import java.util.Objects;
......
package net.mamoe.mirai.event.events.robot package net.mamoe.mirai.event.events.robot
import net.mamoe.mirai.event.events.MiraiEvent import net.mamoe.mirai.Robot
import net.mamoe.mirai.network.RobotNetworkHandler import net.mamoe.mirai.event.MiraiEvent
/** /**
* @author Him188moe * @author Him188moe
*/ */
class RobotLoginEvent(val robotNetworkHandler: RobotNetworkHandler) : MiraiEvent() class RobotLoginEvent(val robot: Robot) : MiraiEvent()
class RobotLogoutEvent(val robotNetworkHandler: RobotNetworkHandler) : MiraiEvent() class RobotLogoutEvent(val robot: Robot) : MiraiEvent()
class RobotMessageReceivedEvent(val robotNetworkHandler: RobotNetworkHandler, val type: Type, val message: String) : MiraiEvent() { class RobotMessageReceivedEvent(val robot: Robot, val type: Type, val message: String) : MiraiEvent() {
enum class Type { enum class Type {
FRIEND, FRIEND,
GROUP GROUP
} }
} }
...@@ -7,5 +7,4 @@ public final class RobotLoginSucceedEvent extends RobotEvent { ...@@ -7,5 +7,4 @@ public final class RobotLoginSucceedEvent extends RobotEvent {
public RobotLoginSucceedEvent(Robot robot) { public RobotLoginSucceedEvent(Robot robot) {
super(robot); super(robot);
} }
} }
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent;
public final class ServerDisableEvent extends MiraiEvent {
}
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.AsyncEvent;
import net.mamoe.mirai.event.MiraiEvent;
public final class ServerDisabledEvent extends MiraiEvent implements AsyncEvent {
}
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.events.MiraiEvent;
public final class ServerEnableEvent extends MiraiEvent {
}
package net.mamoe.mirai.event.events.server;
import net.mamoe.mirai.event.AsyncEvent;
import net.mamoe.mirai.event.MiraiEvent;
public final class ServerEnabledEvent extends MiraiEvent implements AsyncEvent {
}
package net.mamoe.mirai.message; package net.mamoe.mirai.message;
import net.mamoe.mirai.contact.Contact;
import net.mamoe.mirai.contact.QQ; import net.mamoe.mirai.contact.QQ;
import net.mamoe.mirai.message.defaults.At; import net.mamoe.mirai.message.defaults.At;
import net.mamoe.mirai.message.defaults.Image;
import net.mamoe.mirai.message.defaults.MessageChain; import net.mamoe.mirai.message.defaults.MessageChain;
import net.mamoe.mirai.message.defaults.PlainText; import net.mamoe.mirai.message.defaults.PlainText;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
...@@ -11,7 +13,12 @@ import java.io.File; ...@@ -11,7 +13,12 @@ import java.io.File;
import java.util.Objects; import java.util.Objects;
/** /**
* 可发送的或从服务器接收的消息.
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 {@linkplain PlainText 纯文本}, {@linkplain Image 图片} 等.
*
* @author Him188moe * @author Him188moe
* @see Contact#sendMessage(Message) 发送这个消息
* @see MessageKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现
*/ */
public abstract class Message { public abstract class Message {
@Override @Override
...@@ -38,7 +45,7 @@ public abstract class Message { ...@@ -38,7 +45,7 @@ public abstract class Message {
return new MessageChain(this, Objects.requireNonNull(tail)); return new MessageChain(this, Objects.requireNonNull(tail));
} }
public Message concat(String tail) { public final Message concat(String tail) {
return concat(new PlainText(tail)); return concat(new PlainText(tail));
} }
......
@file:JvmName("MessageKt")
package net.mamoe.mirai.message package net.mamoe.mirai.message
import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.message.defaults.PlainText
...@@ -12,4 +14,7 @@ infix operator fun Message.plus(another: Message): Message = this.concat(another ...@@ -12,4 +14,7 @@ infix operator fun Message.plus(another: Message): Message = this.concat(another
*/ */
infix operator fun Message.plus(another: String): Message = this.concat(another) infix operator fun Message.plus(another: String): Message = this.concat(another)
/**
* 连接 [String] 与 [Message]
*/
infix fun String.concat(another: Message): Message = PlainText(this).concat(another) infix fun String.concat(another: Message): Message = PlainText(this).concat(another)
\ No newline at end of file
...@@ -7,25 +7,28 @@ import org.jetbrains.annotations.NotNull; ...@@ -7,25 +7,28 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects; import java.util.Objects;
/** /**
* At 一个人的消息.
*
* @author Him188moe * @author Him188moe
*/ */
public final class At extends Message { public final class At extends Message {
private final int target; private final long target;
public At(@NotNull QQ target) { public At(@NotNull QQ target) {
this(Objects.requireNonNull(target).getNumber()); this(Objects.requireNonNull(target).getNumber());
} }
public At(int target) { public At(long target) {
this.target = target; this.target = target;
} }
public int getTarget() { public long getTarget() {
return target; return target;
} }
@Override @Override
public String toString() { public String toString() {
return null; // TODO: 2019/9/4 At.toString
throw new UnsupportedOperationException();
} }
} }
...@@ -4,6 +4,8 @@ import net.mamoe.mirai.message.FaceID; ...@@ -4,6 +4,8 @@ import net.mamoe.mirai.message.FaceID;
import net.mamoe.mirai.message.Message; import net.mamoe.mirai.message.Message;
/** /**
* QQ 自带表情
*
* @author Him188moe * @author Him188moe
*/ */
public final class Face extends Message { public final class Face extends Message {
......
...@@ -29,6 +29,9 @@ public final class MessageChain extends Message { ...@@ -29,6 +29,9 @@ public final class MessageChain extends Message {
list.add(message); list.add(message);
} }
/**
* @return An unmodifiable list
*/
public List<Message> toList() { public List<Message> toList() {
return List.copyOf(list); return List.copyOf(list);
} }
......
...@@ -7,70 +7,84 @@ import java.util.stream.Collectors ...@@ -7,70 +7,84 @@ import java.util.stream.Collectors
/** /**
* @author Him188moe * @author Him188moe
*/ */
interface Protocol { object Protocol {
companion object { val SERVER_IP: List<String> = object : ArrayList<String>() {
val SERVER_IP: ArrayList<String> = object : ArrayList<String>() { init {
init { add("183.60.56.29")
add("183.60.56.29")
arrayOf( arrayOf(
"sz2.tencent.com", "sz3.tencent.com",
"sz3.tencent.com", "sz4.tencent.com",
"sz4.tencent.com", "sz5.tencent.com",
"sz5.tencent.com", "sz6.tencent.com",
"sz6.tencent.com", "sz8.tencent.com",
"sz8.tencent.com", "sz9.tencent.com",
"sz9.tencent.com" "sz2.tencent.com"
).forEach { this.add(InetAddress.getByName(it).hostAddress) } ).forEach { this.add(InetAddress.getByName(it).hostAddress) }
}
} }
}
get() = Collections.unmodifiableList(field)
const val head = "02"
const val ver = "37 13"
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00"
const val tail = "03"
/**
* _fixVer
*/
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
/**
* 0825data1
*/
const val constantData0 = "00 18 00 16 00 01 "
/**
* 0825data2
*/
const val constantData1 = "00 00 04 53 00 00 00 01 00 00 15 85 "
const val key0825 = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
const val publicKey = "02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
const val fix0836 = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
const val head = "02" const val key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val ver = "37 13 " const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 "
const val tail = " 03"
const val _fixVer = "02 00 00 00 01 01 01 00 00 68 20 "
const val _0825data0 = "00 18 00 16 00 01 "
const val _0825data2 = "00 00 04 53 00 00 00 01 00 00 15 85 "
const val _0825key = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
const val publicKey = "02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
const val _0836fix = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
const val _00BaKey = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val _00BaFixKey = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
const val encryptKey = "“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”"
const val _0836_622_fix2 = "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"; /**
const val _0836_622_fix1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19"; * 0836_622_fix2
const val _0836key1 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA" */
const val passwordSubmissionKey2 = "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";
/**
* 0836_622_fix1
*/
const val passwordSubmissionKey1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19";
/**
* fix_0836_1
*/
const val key0836 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf() private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun hexToBytes(hex: String): ByteArray { fun hexToBytes(hex: String): ByteArray {
hex.hashCode().let { id -> hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) { if (hexToByteArrayCacheMap.containsKey(id)) {
return hexToByteArrayCacheMap[id]!!.clone() return hexToByteArrayCacheMap[id]!!.clone()
} else { } else {
hexToUBytes(hex).toByteArray().let { hexToUBytes(hex).toByteArray().let {
hexToByteArrayCacheMap[id] = it.clone(); hexToByteArrayCacheMap[id] = it.clone();
return it return it
}
} }
} }
} }
}
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun hexToUBytes(hex: String): UByteArray = Arrays fun hexToUBytes(hex: String): UByteArray = Arrays
.stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) .stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
.map { value -> value.trim { it <= ' ' } } .map { value -> value.trim { it <= ' ' } }
.map { s -> s.toUByte(16) } .map { s -> s.toUByte(16) }
.collect(Collectors.toList()).toUByteArray() .collect(Collectors.toList()).toUByteArray()
}
} }
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.TEACryptor import net.mamoe.mirai.utils.TEA
import java.io.DataInputStream import java.io.DataInputStream
/** /**
...@@ -12,13 +12,14 @@ import java.io.DataInputStream ...@@ -12,13 +12,14 @@ import java.io.DataInputStream
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("00 5C") @PacketId("00 5C")
class ClientAccountInfoRequestPacket( class ClientAccountInfoRequestPacket(
private val qq: Int, private val qq: Long,
private val sessionKey: ByteArray private val sessionKey: ByteArray
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol._fixVer) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
it.writeByte(0x88) it.writeByte(0x88)
it.writeQQ(qq) it.writeQQ(qq)
...@@ -27,6 +28,7 @@ class ClientAccountInfoRequestPacket( ...@@ -27,6 +28,7 @@ class ClientAccountInfoRequestPacket(
} }
} }
@PacketId("00 5C")
class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(input) { class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(input) {
//等级 //等级
//升级剩余活跃天数 //升级剩余活跃天数
...@@ -34,16 +36,13 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp ...@@ -34,16 +36,13 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp
override fun decode() { override fun decode() {
} }
}
class ServerAccountInfoResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
override fun decode() {
}
fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket { @PacketId("00 5C")
this.input goto 14 class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket {
return ServerAccountInfoResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream()); this.input goto 14
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
return ServerAccountInfoResponsePacket(TEA.decrypt(data, sessionKey).dataInputStream());
}
} }
} }
\ No newline at end of file
...@@ -2,7 +2,6 @@ package net.mamoe.mirai.network.packet ...@@ -2,7 +2,6 @@ package net.mamoe.mirai.network.packet
import lombok.Getter import lombok.Getter
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.IOException import java.io.IOException
...@@ -15,11 +14,13 @@ import java.security.MessageDigest ...@@ -15,11 +14,13 @@ import java.security.MessageDigest
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
@Getter @Getter
val packageId: String val idHex: String
var encoded: Boolean = false
init { init {
val annotation = this.javaClass.getAnnotation(PacketId::class.java) val annotation = this.javaClass.getAnnotation(PacketId::class.java)
packageId = annotation.value idHex = annotation.value
try { try {
this.writeHex(Protocol.head) this.writeHex(Protocol.head)
...@@ -33,7 +34,7 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { ...@@ -33,7 +34,7 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
@Throws(IOException::class) @Throws(IOException::class)
fun writePacketId() { fun writePacketId() {
this.writeHex(this@ClientPacket.packageId) this.writeHex(this@ClientPacket.idHex)
} }
/** /**
...@@ -43,11 +44,19 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { ...@@ -43,11 +44,19 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
* Before sending the packet, a [tail][Protocol.tail] will be added. * Before sending the packet, a [tail][Protocol.tail] will be added.
*/ */
@Throws(IOException::class) @Throws(IOException::class)
abstract fun encode() protected abstract fun encode()
fun encodePacket() {
if (encoded) {
return
}
encode()
writeHex(Protocol.tail)
}
@Throws(IOException::class) @Throws(IOException::class)
fun encodeToByteArray(): ByteArray { fun encodeToByteArray(): ByteArray {
encode() encodePacket()
return toByteArray() return toByteArray()
} }
...@@ -89,73 +98,52 @@ fun DataOutputStream.writeHex(hex: String) { ...@@ -89,73 +98,52 @@ fun DataOutputStream.writeHex(hex: String) {
} }
} }
@ExperimentalUnsignedTypes
fun DataOutputStream.writeVarInt(dec: UInt) {
/*.判断开始 (n < 256)
返回 (取文本右边 (“0” + 取十六进制文本 (n), 2))
.判断 (n ≥ 256)
hex = 取文本右边 (“0” + 取十六进制文本 (n), 4)
返回 (取文本左边 (hex, 2) + “ ” + 取文本右边 (hex, 2))
.默认
返回 (“”)
.判断结束*/
when {
dec < 256u -> this.writeByte(dec.toByte().toInt())//drop other bits
dec > 256u -> this.writeShort(dec.toShort().toInt())
else -> throw IllegalArgumentException(dec.toString())
}
}
fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, key: ByteArray) { fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, key: ByteArray) {
this.write(TEACryptor.encrypt(byteArray, key)) this.write(TEA.encrypt(byteArray, key))
} }
fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, cryptor: TEACryptor) { fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, cryptor: TEA) {
this.write(cryptor.encrypt(byteArray)) this.write(cryptor.encrypt(byteArray))
} }
fun DataOutputStream.encryptAndWrite(key: ByteArray, encoder: (ByteArrayDataOutputStream) -> Unit) { fun DataOutputStream.encryptAndWrite(key: ByteArray, encoder: (ByteArrayDataOutputStream) -> Unit) {
this.write(TEACryptor.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key)) this.write(TEA.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key))
} }
fun DataOutputStream.encryptAndWrite(cryptor: TEACryptor, encoder: (ByteArrayDataOutputStream) -> Unit) { @ExperimentalUnsignedTypes
fun DataOutputStream.encryptAndWrite(keyHex: String, encoder: (ByteArrayDataOutputStream) -> Unit) {
this.encryptAndWrite(keyHex.hexToBytes(), encoder)
}
fun DataOutputStream.encryptAndWrite(cryptor: TEA, encoder: (ByteArrayDataOutputStream) -> Unit) {
this.write(cryptor.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() })) this.write(cryptor.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }))
} }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@Throws(IOException::class) @Throws(IOException::class)
fun DataOutputStream.writeTLV0006(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray) { fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray) {
ByteArrayDataOutputStream().let { ByteArrayDataOutputStream().let {
it.writeHex("12 12 12 12")//it.writeRandom(4) todo it.writeRandom(4)
it.writeHex("00 02") it.writeHex("00 02")
it.writeQQ(qq) it.writeQQ(qq)
it.writeHex(Protocol._0825data2) it.writeHex(Protocol.constantData1)
it.writeHex("00 00 01") it.writeHex("00 00 01")
val md5_1 = md5(password); val firstMD5 = md5(password)
val md5_2 = md5(md5_1 + "00 00 00 00".hexToBytes() + qq.toByteArray()) val secondMD5 = md5(firstMD5 + "00 00 00 00".hexToBytes() + qq.toUInt().toByteArray())
println(md5_1.toUByteArray().toUHexString()) it.write(firstMD5)
println(md5_2.toUByteArray().toUHexString())
it.write(md5_1)
it.writeInt(loginTime) it.writeInt(loginTime)
it.writeByte(0); it.writeByte(0)
it.writeZero(4 * 3) it.writeZero(4 * 3)
it.writeIP(loginIP) it.writeIP(loginIP)
it.writeZero(8) it.writeZero(8)
it.writeHex("00 10") it.writeHex("00 10")
it.writeHex("15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B") it.writeHex("15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B")
it.write(tgtgtKey) it.write(tgtgtKey)
println() this.write(TEA.encrypt(it.toByteArray(), secondMD5))
println(it.toByteArray().toUHexString())
this.write(TEACryptor.encrypt(it.toByteArray(), md5_2))
} }
} }
fun main() {
}
/* /*
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun main() { fun main() {
...@@ -164,7 +152,7 @@ fun main() { ...@@ -164,7 +152,7 @@ fun main() {
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@TestedSuccessfully @TestedSuccessfully
fun DataOutputStream.writeCRC32() = writeCRC32(getRandomKey(16)) fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16))
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
...@@ -193,6 +181,17 @@ fun Int.toByteArray(): ByteArray = byteArrayOf( ...@@ -193,6 +181,17 @@ fun Int.toByteArray(): ByteArray = byteArrayOf(
(this.ushr(0) and 0xFF).toByte() (this.ushr(0) and 0xFF).toByte()
) )
/**
* 255u -> 00 00 00 FF
*/
@ExperimentalUnsignedTypes
fun UInt.toByteArray(): ByteArray = byteArrayOf(
(this.shr(24) and 255u).toByte(),
(this.shr(16) and 255u).toByte(),
(this.shr(8) and 255u).toByte(),
(this.shr(0) and 255u).toByte()
)
/** /**
* 255 -> FF 00 00 00 * 255 -> FF 00 00 00
*/ */
...@@ -204,7 +203,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf( ...@@ -204,7 +203,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf(
) )
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator); fun Int.toHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator)
internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray()) internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
...@@ -225,7 +224,14 @@ fun DataOutputStream.writeRandom(length: Int) { ...@@ -225,7 +224,14 @@ fun DataOutputStream.writeRandom(length: Int) {
} }
} }
@ExperimentalUnsignedTypes
@Throws(IOException::class)
fun DataOutputStream.writeQQ(qq: Long) {
this.write(qq.toUInt().toByteArray())
}
@ExperimentalUnsignedTypes
@Throws(IOException::class) @Throws(IOException::class)
fun DataOutputStream.writeQQ(qq: Int) { fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) {
this.writeInt(qq) this.write(groupIdOrGroupNumber.toUInt().toByteArray())
} }
\ No newline at end of file
...@@ -10,7 +10,7 @@ import java.io.IOException ...@@ -10,7 +10,7 @@ import java.io.IOException
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("00 58") @PacketId("00 58")
class ClientHeartbeatPacket( class ClientHeartbeatPacket(
private val qq: Int, private val qq: Long,
private val sessionKey: ByteArray private val sessionKey: ByteArray
) : ClientPacket() { ) : ClientPacket() {
@Throws(IOException::class) @Throws(IOException::class)
...@@ -24,8 +24,4 @@ class ClientHeartbeatPacket( ...@@ -24,8 +24,4 @@ class ClientHeartbeatPacket(
} }
} }
class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input) { class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input)
override fun decode() { \ No newline at end of file
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
/**
* 告知服务器已经收到数据
*/
@PacketId("")//随后写入
@ExperimentalUnsignedTypes
class ClientMessageResponsePacket(
private val qq: Int,
private val packetIdFromServer: ByteArray,
private val sessionKey: ByteArray,
private val eventIdentity: ByteArray
) : ClientPacket() {
override fun encode() {
this.write(packetIdFromServer)
this.writeQQ(qq)
this.writeHex(Protocol._fixVer)
this.encryptAndWrite(sessionKey) {
it.write(eventIdentity)
}
}
}
/**
* 群聊和好友消息分发
*/
@PacketId("00 17")
class ServerMessageEventPacketRaw(
input: DataInputStream,
private val dataLength: Int,
private val packetId: ByteArray
) : ServerPacket(input) {
lateinit var type: ByteArray;
lateinit var eventIdentity: ByteArray;
override fun decode() {
eventIdentity = this.input.readNBytes(16)
type = this.input.goto(18).readNBytes(2)
}
fun analyze(): ServerEventPacket = when (val typeHex = type.toUHexString()) {
"00 C4" -> {
if (this.input.goto(33).readBoolean()) {
ServerAndroidOnlineEventPacket(this.input, packetId, eventIdentity)
} else {
ServerAndroidOfflineEventPacket(this.input, packetId, eventIdentity)
}
}
"00 2D" -> ServerGroupUploadFileEventPacket(this.input, packetId, eventIdentity)
"00 52" -> ServerGroupMessageEventPacket(this.input, packetId, eventIdentity)
"00 A6" -> ServerFriendMessageEventPacket(this.input, packetId, eventIdentity)
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
else -> UnknownServerEventPacket(this.input, packetId, eventIdentity)
}
}
class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
@PacketId("00 17")
class ServerMessageEventPacketRawEncoded(input: DataInputStream, val packetId: ByteArray) : ServerPacket(input) {
override fun decode() {
}
fun decrypt(sessionKey: ByteArray): ServerMessageEventPacketRaw {
this.input goto 14
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
return ServerMessageEventPacketRaw(TEACryptor.decrypt(data, sessionKey).dataInputStream(), data.size, packetId);
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.TEACryptor import net.mamoe.mirai.utils.TEA
import java.io.DataInputStream import java.io.DataInputStream
...@@ -11,14 +11,14 @@ import java.io.DataInputStream ...@@ -11,14 +11,14 @@ import java.io.DataInputStream
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("00 1D") @PacketId("00 1D")
class ClientSKeyRequestPacket( class ClientSKeyRequestPacket(
private val qq: Int, private val qq: Long,
private val sessionKey: ByteArray private val sessionKey: ByteArray
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol._fixVer) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
it.writeHex("33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D") it.writeHex("33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D")
} }
...@@ -31,7 +31,7 @@ class ClientSKeyRequestPacket( ...@@ -31,7 +31,7 @@ class ClientSKeyRequestPacket(
@PacketId("00 1D") @PacketId("00 1D")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class ClientSKeyRefreshmentRequestPacket( class ClientSKeyRefreshmentRequestPacket(
private val qq: Int, private val qq: Long,
private val sessionKey: ByteArray private val sessionKey: ByteArray
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
...@@ -52,19 +52,13 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) { ...@@ -52,19 +52,13 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) {
override fun decode() { override fun decode() {
this.sKey = String(this.input.goto(4).readNBytes(10)) this.sKey = String(this.input.goto(4).readNBytes(10))
} }
}
/**
* @author Him188moe
*/
class ServerSKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
override fun decode() {
}
fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket { class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
this.input goto 14 fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket {
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } this.input goto 14
return ServerSKeyResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream()); val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
return ServerSKeyResponsePacket(TEA.decrypt(data, sessionKey).dataInputStream());
}
} }
} }
\ No newline at end of file
...@@ -2,28 +2,65 @@ package net.mamoe.mirai.network.packet ...@@ -2,28 +2,65 @@ package net.mamoe.mirai.network.packet
import net.mamoe.mirai.message.defaults.MessageChain import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.message.defaults.PlainText import net.mamoe.mirai.message.defaults.PlainText
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.MiraiLogger
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
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
/** /**
* Packet id: `00 CE` or `00 17`
*
* @author Him188moe * @author Him188moe
*/ */
open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) { open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
@PacketId("00 17")
class Raw(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
@ExperimentalUnsignedTypes
fun distribute(): ServerEventPacket {
val eventIdentity = this.input.readNBytes(16)
val type = this.input.goto(18).readNBytes(2)
return when (type.toUHexString()) {
"00 C4" -> {
if (this.input.goto(33).readBoolean()) {
ServerAndroidOnlineEventPacket(this.input, packetId, eventIdentity)
} else {
ServerAndroidOfflineEventPacket(this.input, packetId, eventIdentity)
}
}
"00 2D" -> ServerGroupUploadFileEventPacket(this.input, packetId, eventIdentity)
override fun decode() { "00 52" -> ServerGroupMessageEventPacket(this.input, packetId, eventIdentity)
"00 A6" -> ServerFriendMessageEventPacket(this.input, packetId, eventIdentity)
//"02 10", "00 12" -> ServerUnknownEventPacket(this.input, packetId, eventIdentity)
else -> UnknownServerEventPacket(this.input, packetId, eventIdentity)
}
}
@PacketId("00 17")
class Encrypted(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
fun decrypt(sessionKey: ByteArray): Raw = Raw(decryptBy(sessionKey), packetId)
}
} }
} }
/**
* Unknown event
*/
class UnknownServerEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
/** /**
* Android 客户端上线 * Android 客户端上线
*/ */
class ServerAndroidOnlineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) class ServerAndroidOnlineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
/** /**
* Android 客户端线 * Android 客户端线
*/ */
class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity)
...@@ -31,7 +68,7 @@ class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArra ...@@ -31,7 +68,7 @@ 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 xmlMessage: String private lateinit var xmlMessage: String
override fun decode() { override fun decode() {
xmlMessage = 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()))
...@@ -39,8 +76,8 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr ...@@ -39,8 +76,8 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr
} }
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 groupNumber: Int = 0 var groupNumber: Long = 0
var qq: Int = 0 var qq: Long = 0
lateinit var message: String lateinit var message: String
lateinit var messageType: MessageType lateinit var messageType: MessageType
...@@ -58,9 +95,10 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -58,9 +95,10 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
OTHER, OTHER,
} }
@ExperimentalUnsignedTypes
override fun decode() { override fun decode() {
groupNumber = this.input.goto(51).readInt() groupNumber = this.input.goto(51).readInt().toLong()
qq = this.input.goto(56).readInt() qq = this.input.goto(56).readLong().toUInt().toLong()
val fontLength = this.input.goto(108).readShort() val fontLength = this.input.goto(108).readShort()
//println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00 //println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00
...@@ -76,7 +114,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -76,7 +114,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
25 -> MessageType.ANONYMOUS 25 -> MessageType.ANONYMOUS
else -> { else -> {
println("id=$id") MiraiLogger debug ("ServerGroupMessageEventPacket id=$id")
MessageType.OTHER MessageType.OTHER
} }
} }
...@@ -142,10 +180,9 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, ...@@ -142,10 +180,9 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
} }
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: Long = 0
lateinit var message: MessageChain lateinit var message: MessageChain
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
override fun decode() { override fun decode() {
//start at Sep1.0:27 //start at Sep1.0:27
...@@ -153,7 +190,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray ...@@ -153,7 +190,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
println(input.readAllBytes().toUHexString()) println(input.readAllBytes().toUHexString())
input.goto(0) input.goto(0)
qq = input.readIntAt(0) qq = input.readIntAt(0).toLong()
val msgLength = input.readShortAt(22) val msgLength = input.readShortAt(22)
val fontLength = input.readShortAt(93 + msgLength) val fontLength = input.readShortAt(93 + msgLength)
val offset = msgLength + fontLength val offset = msgLength + fontLength
...@@ -165,13 +202,42 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray ...@@ -165,13 +202,42 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
} }
/**
* 告知服务器已经收到数据
*/
@PacketId("")//随后写入
@ExperimentalUnsignedTypes
class ClientMessageResponsePacket(
private val qq: Long,
private val packetIdFromServer: ByteArray,//4bytes
private val sessionKey: ByteArray,
private val eventIdentity: ByteArray
) : ClientPacket() {
override fun encode() {
this.write(packetIdFromServer)//packet id 4bytes
this.writeQQ(qq)
this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) {
it.write(eventIdentity)
}
}
}
/*
3E 03 3F A2 76 E4 B8 DD 00 09 7C 3F 64 5C 2A 60 1F 40 00 A6 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 02 38 03 3E 03 3F A2 76 E4 B8 DD 01 10 9D D6 12 EA BC 07 91 EF DC 29 75 67 A9 1E 00 0B 2F E4 5D 6B A8 F6 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D 6B A8 F6 08 7E 90 CE 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E7 89 9B E9 80 BC 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
3E 03 3F A2 76 E4 B8 DD 00 03 5F 85 64 5C 2A A4 1F 40 00 A6 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 02 38 03 3E 03 3F A2 76 E4 B8 DD 01 10 9D D6 12 EA BC 07 91 EF DC 29 75 67 A9 1E 00 0B 2F E5 5D 6B A9 16 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D 6B A9 17 1B B3 4D D7 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E7 89 9B E9 80 BC 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
*/
/* /*
backup 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: Long = 0
lateinit var message: String lateinit var message: String
......
package net.mamoe.mirai.network.packet package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.login.* import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket import net.mamoe.mirai.utils.*
import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted
import net.mamoe.mirai.utils.getAllDeclaredFields
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
...@@ -14,7 +11,9 @@ import java.io.DataInputStream ...@@ -14,7 +11,9 @@ import java.io.DataInputStream
*/ */
abstract class ServerPacket(val input: DataInputStream) : Packet { abstract class ServerPacket(val input: DataInputStream) : Packet {
abstract fun decode() open fun decode() {
}
companion object { companion object {
...@@ -28,55 +27,56 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -28,55 +27,56 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
return when (val idHex = stream.readInt().toHexString(" ")) { return when (val idHex = stream.readInt().toHexString(" ")) {
"08 25 31 01" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream) "08 25 31 01" -> ServerTouchResponsePacket.Encrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream)
"08 25 31 02" -> ServerTouchResponsePacket.Encrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, stream)
"08 25 31 02" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, stream)
"08 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> { "08 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> {
when (bytes.size) { when (bytes.size) {
271, 207 -> return ServerLoginResponseResendPacketEncrypted(stream, when (idHex) { 271, 207 -> return ServerLoginResponseResendPacket.Encrypted(stream, when (idHex) {
"08 36 31 03" -> ServerLoginResponseResendPacket.Flag.`08 36 31 03` "08 36 31 03" -> ServerLoginResponseResendPacket.Flag.`08 36 31 03`
else -> { else -> {
println("flag=$idHex"); ServerLoginResponseResendPacket.Flag.OTHER MiraiLogger debug ("ServerLoginResponseResendPacketEncrypted: flag=$idHex"); ServerLoginResponseResendPacket.Flag.OTHER
} }
}) })
871 -> return ServerLoginResponseVerificationCodePacketEncrypted(stream) 871 -> return ServerLoginResponseVerificationCodeInitPacket.Encrypted(stream)
} }
if (bytes.size > 700) { if (bytes.size > 700) {
return ServerLoginResponseSuccessPacketEncrypted(stream) return ServerLoginResponseSuccessPacket.Encrypted(stream)
} }
return ServerLoginResponseFailedPacket(when (bytes.size) { return ServerLoginResponseFailedPacket(when (bytes.size) {
319 -> ServerLoginResponseFailedPacket.State.WRONG_PASSWORD 319, 135 -> LoginState.WRONG_PASSWORD
135 -> ServerLoginResponseFailedPacket.State.RETYPE_PASSWORD //135 -> LoginState.RETYPE_PASSWORD
279 -> ServerLoginResponseFailedPacket.State.BLOCKED 279 -> LoginState.BLOCKED
263 -> ServerLoginResponseFailedPacket.State.UNKNOWN_QQ_NUMBER 263 -> LoginState.UNKNOWN_QQ_NUMBER
551, 487 -> ServerLoginResponseFailedPacket.State.DEVICE_LOCK 551, 487 -> LoginState.DEVICE_LOCK
359 -> ServerLoginResponseFailedPacket.State.TAKEN_BACK 359 -> LoginState.TAKEN_BACK
else -> LoginState.UNKNOWN
/*
//unknown //unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Already logged in)")//可能是已经完成登录, 服务器拒绝第二次登录 63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data)")//包数据有误 351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data or Unknown error)")//包数据有误
else -> throw IllegalArgumentException(bytes.size.toString()) else -> throw IllegalArgumentException(bytes.size.toString())*/
}, stream) }, stream)
} }
"08 28 04 34" -> ServerSessionKeyResponsePacketEncrypted(stream) "08 28 04 34" -> ServerSessionKeyResponsePacket.Encrypted(stream)
else -> when (idHex.substring(0, 5)) { else -> when (idHex.substring(0, 5)) {
"00 EC" -> ServerLoginSuccessPacket(stream) "00 EC" -> ServerLoginSuccessPacket(stream)
"00 1D" -> ServerSKeyResponsePacketEncrypted(stream) "00 1D" -> ServerSKeyResponsePacket.Encrypted(stream)
"00 5C" -> ServerAccountInfoResponsePacketEncrypted(stream) "00 5C" -> ServerAccountInfoResponsePacket.Encrypted(stream)
"00 58" -> ServerHeartbeatResponsePacket(stream) "00 58" -> ServerHeartbeatResponsePacket(stream)
"00 BA" -> ServerVerificationCodePacketEncrypted(stream) "00 BA" -> ServerVerificationCodePacket.Encrypted(stream)
"00 CE", "00 17" -> ServerMessageEventPacketRawEncoded(stream, idHex.hexToBytes()) "00 CE", "00 17" -> ServerEventPacket.Raw.Encrypted(stream, idHex.hexToBytes())
"00 81" -> UnknownServerPacket(stream) "00 81" -> UnknownServerPacket(stream)
...@@ -89,6 +89,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -89,6 +89,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
} }
} }
@ExperimentalUnsignedTypes
override fun toString(): String { override fun toString(): String {
return this.javaClass.simpleName + this.getAllDeclaredFields().joinToString(", ", "{", "}") { return this.javaClass.simpleName + this.getAllDeclaredFields().joinToString(", ", "{", "}") {
it.trySetAccessible(); it.name + "=" + it.get(this).let { value -> it.trySetAccessible(); it.name + "=" + it.get(this).let { value ->
...@@ -100,20 +101,19 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -100,20 +101,19 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
} }
} }
} }
}
fun decryptBy(key: ByteArray): DataInputStream {
input.goto(14)
return DataInputStream(TEA.decrypt(input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, key).inputStream())
}
fun DataInputStream.readUntil(byte: Byte): ByteArray { @ExperimentalUnsignedTypes
var buff = byteArrayOf() fun decryptBy(keyHex: String): DataInputStream {
var b: Byte return this.decryptBy(keyHex.hexToBytes())
b = readByte()
while (b != byte) {
buff += b
b = readByte()
} }
return buff
} }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun DataInputStream.readIP(): String { fun DataInputStream.readIP(): String {
var buff = "" var buff = ""
...@@ -150,6 +150,15 @@ fun <N : Number> DataInputStream.readNBytesAt(position: N, length: Int): ByteArr ...@@ -150,6 +150,15 @@ fun <N : Number> DataInputStream.readNBytesAt(position: N, length: Int): ByteArr
return this.readNBytes(length) return this.readNBytes(length)
} }
fun <N : Number> DataInputStream.readNBytes(length: N): ByteArray {
return this.readNBytes(length.toInt())
}
fun DataInputStream.readNBytesIn(range: IntRange): ByteArray {
this.goto(range.first)
return this.readNBytes(range.last - range.first + 1)
}
fun <N : Number> DataInputStream.readIntAt(position: N): Int { fun <N : Number> DataInputStream.readIntAt(position: N): Int {
this.goto(position) this.goto(position)
return this.readInt(); return this.readInt();
...@@ -163,4 +172,6 @@ fun <N : Number> DataInputStream.readByteAt(position: N): Byte { ...@@ -163,4 +172,6 @@ fun <N : Number> DataInputStream.readByteAt(position: N): Byte {
fun <N : Number> DataInputStream.readShortAt(position: N): Short { fun <N : Number> DataInputStream.readShortAt(position: N): Short {
this.goto(position) this.goto(position)
return this.readShort(); return this.readShort();
} }
\ No newline at end of file
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
\ No newline at end of file
...@@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet ...@@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.ByteArrayDataOutputStream import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.getRandomKey import net.mamoe.mirai.utils.getRandomByteArray
import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream import java.io.DataInputStream
import java.net.InetAddress import java.net.InetAddress
...@@ -14,10 +14,8 @@ import java.net.InetAddress ...@@ -14,10 +14,8 @@ import java.net.InetAddress
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("08 28 04 34") @PacketId("08 28 04 34")
class ClientSessionRequestPacket( class ClientSessionRequestPacket(
private val qq: Int, private val qq: Long,
private val serverIp: String, private val serverIp: String,
private val loginIP: String,
private val md5_32: ByteArray,
private val token38: ByteArray, private val token38: ByteArray,
private val token88: ByteArray, private val token88: ByteArray,
private val encryptionKey: ByteArray, private val encryptionKey: ByteArray,
...@@ -28,7 +26,7 @@ class ClientSessionRequestPacket( ...@@ -28,7 +26,7 @@ class ClientSessionRequestPacket(
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A") this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
this.writeHex("00 38") this.writeHex("00 38")
this.write(token38) this.write(token38)
this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
override fun toByteArray(): ByteArray { override fun toByteArray(): ByteArray {
this.writeHex("00 07 00 88") this.writeHex("00 07 00 88")
this.write(token88) this.write(token88)
...@@ -36,22 +34,22 @@ class ClientSessionRequestPacket( ...@@ -36,22 +34,22 @@ class ClientSessionRequestPacket(
this.writeIP(serverIp) this.writeIP(serverIp)
this.writeHex("1F 40 00 00 00 00 00 15 00 30 00 01")//fix1 this.writeHex("1F 40 00 00 00 00 00 15 00 30 00 01")//fix1
this.writeHex("01 92 A5 D2 59 00 10 54 2D CF 9B 60 BF BB EC 0D D4 81 CE 36 87 DE 35 02 AE 6D ED DC 00 10 ") this.writeHex("01 92 A5 D2 59 00 10 54 2D CF 9B 60 BF BB EC 0D D4 81 CE 36 87 DE 35 02 AE 6D ED DC 00 10 ")
this.writeHex(Protocol._0836fix) this.writeHex(Protocol.fix0836)
this.writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00") this.writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00")
this.writeHex(Protocol._0825data0) this.writeHex(Protocol.constantData0)
this.writeHex(Protocol._0825data2) this.writeHex(Protocol.constantData1)
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex("00 00 00 00 00 1F 00 22 00 01") this.writeHex("00 00 00 00 00 1F 00 22 00 01")
this.writeHex("1A 68 73 66 E4 BA 79 92 CC C2 D4 EC 14 7C 8B AF 43 B0 62 FB 65 58 A9 EB 37 55 1D 26 13 A8 E5 3D")//device ID this.writeHex("1A 68 73 66 E4 BA 79 92 CC C2 D4 EC 14 7C 8B AF 43 B0 62 FB 65 58 A9 EB 37 55 1D 26 13 A8 E5 3D")//device ID
this.write(tlv0105) this.write(tlv0105)
this.writeHex("01 0B 00 85 00 02") this.writeHex("01 0B 00 85 00 02")
this.writeHex("B9 ED EF D7 CD E5 47 96 7A B5 28 34 CA 93 6B 5C")//fix2 this.writeHex("B9 ED EF D7 CD E5 47 96 7A B5 28 34 CA 93 6B 5C")//fix2
this.write(getRandomKey(1)) this.write(getRandomByteArray(1))
this.writeHex("10 00 00 00 00 00 00 00 02") this.writeHex("10 00 00 00 00 00 00 00 02")
//fix3 //fix3
this.writeHex("00 63 3E 00 63 02 04 03 06 02 00 04 00 52 D9 00 00 00 00 A9 58 3E 6D 6D 49 AA F6 A6 D9 33 0A E7 7E 36 84 03 01 00 00 68 20 15 8B 00 00 01 02 00 00 03 00 07 DF 00 0A 00 0C 00 01 00 04 00 03 00 04 20 5C 00") this.writeHex("00 63 3E 00 63 02 04 03 06 02 00 04 00 52 D9 00 00 00 00 A9 58 3E 6D 6D 49 AA F6 A6 D9 33 0A E7 7E 36 84 03 01 00 00 68 20 15 8B 00 00 01 02 00 00 03 00 07 DF 00 0A 00 0C 00 01 00 04 00 03 00 04 20 5C 00")
this.write(md5_32) this.write(getRandomByteArray(32))//md5 32
this.writeHex("68") this.writeHex("68")
this.writeHex("00 00 00 00 00 2D 00 06 00 01") this.writeHex("00 00 00 00 00 2D 00 06 00 01")
...@@ -64,8 +62,6 @@ class ClientSessionRequestPacket( ...@@ -64,8 +62,6 @@ class ClientSessionRequestPacket(
} }
/** /**
* Dispose_0828
*
* @author Him188moe * @author Him188moe
*/ */
class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val dataLength: Int) : ServerPacket(inputStream) { class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val dataLength: Int) : ServerPacket(inputStream) {
...@@ -106,16 +102,12 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d ...@@ -106,16 +102,12 @@ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val d
//tlv0105 = "01 05 00 88 00 01 01 02 00 40 02 01 03 3C 01 03 00 00" + 取文本中间(data, 取文本长度(data) - 367, 167) + “00 40 02 02 03 3C 01 03 00 00 ” + 取文本中间 (data, 取文本长度 (data) - 166, 167) //tlv0105 = "01 05 00 88 00 01 01 02 00 40 02 01 03 3C 01 03 00 00" + 取文本中间(data, 取文本长度(data) - 367, 167) + “00 40 02 02 03 3C 01 03 00 00 ” + 取文本中间 (data, 取文本长度 (data) - 166, 167)
} }
}
class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
override fun decode() {
} class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) {
fun decrypt(sessionResponseDecryptionKey: ByteArray): ServerSessionKeyResponsePacket {
fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket { this.input goto 14
this.input goto 14 val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }
val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } return ServerSessionKeyResponsePacket(TEA.decrypt(data, sessionResponseDecryptionKey).dataInputStream(), data.size)
return ServerSessionKeyResponsePacket(TEACryptor.decrypt(data, _0828_rec_decr_key).dataInputStream(), data.size); }
} }
} }
\ No newline at end of file
...@@ -40,7 +40,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp ...@@ -40,7 +40,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
loginTime = input.readInt() loginTime = input.readInt()
loginIP = input.readIP() loginIP = input.readIP()
tgtgtKey = getRandomKey(16) tgtgtKey = getRandomByteArray(16)
} }
else -> { else -> {
...@@ -48,24 +48,13 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp ...@@ -48,24 +48,13 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
} }
} }
} }
}
class ServerTouchResponsePacketEncrypted(private val type: ServerTouchResponsePacket.Type, inputStream: DataInputStream) : ServerPacket(inputStream) {
override fun decode() {
}
@ExperimentalUnsignedTypes
fun decrypt(): ServerTouchResponsePacket {
input.skip(7)
var bytes = input.readAllBytes();
bytes = bytes.copyOfRange(0, bytes.size - 1);
println(bytes.toUByteArray().toUHexString())
return ServerTouchResponsePacket(DataInputStream(TEACryptor.decrypt(bytes, when (type) { class Encrypted(private val type: Type, inputStream: DataInputStream) : ServerPacket(inputStream) {
ServerTouchResponsePacket.Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes() @ExperimentalUnsignedTypes
ServerTouchResponsePacket.Type.TYPE_08_25_31_01 -> Protocol._0825key.hexToBytes() fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(when (type) {
}).inputStream())); Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes()
Type.TYPE_08_25_31_01 -> Protocol.key0825.hexToBytes()
}))
} }
} }
...@@ -76,19 +65,19 @@ class ServerTouchResponsePacketEncrypted(private val type: ServerTouchResponsePa ...@@ -76,19 +65,19 @@ class ServerTouchResponsePacketEncrypted(private val type: ServerTouchResponsePa
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("08 25 31 01") @PacketId("08 25 31 01")
class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() { class ClientTouchPacket(val qq: Long, val serverIp: String) : ClientPacket() {
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@Throws(IOException::class) @Throws(IOException::class)
override fun encode() { override fun encode() {
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol.fixVer) this.writeHex(Protocol.fixVer)
this.writeHex(Protocol._0825key) this.writeHex(Protocol.key0825)
this.write(TEACryptor.CRYPTOR_0825KEY.encrypt(object : ByteArrayDataOutputStream() { this.write(TEA.CRYPTOR_0825KEY.encrypt(object : ByteArrayDataOutputStream() {
@Throws(IOException::class) @Throws(IOException::class)
override fun toByteArray(): ByteArray { override fun toByteArray(): ByteArray {
this.writeHex(Protocol._0825data0) this.writeHex(Protocol.constantData0)
this.writeHex(Protocol._0825data2) this.writeHex(Protocol.constantData1)
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex("00 00 00 00 03 09 00 08 00 01") this.writeHex("00 00 00 00 03 09 00 08 00 01")
this.writeIP(serverIp); this.writeIP(serverIp);
...@@ -108,7 +97,7 @@ class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() { ...@@ -108,7 +97,7 @@ class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() {
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("08 25 31 02") @PacketId("08 25 31 02")
class ClientServerRedirectionPacket(private val serverIP: String, private val qq: Int) : ClientPacket() { class ClientServerRedirectionPacket(private val serverIP: String, private val qq: Long) : ClientPacket() {
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
override fun encode() { override fun encode() {
this.writeQQ(qq) this.writeQQ(qq)
...@@ -116,11 +105,11 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq ...@@ -116,11 +105,11 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq
this.writeHex(Protocol.redirectionKey) this.writeHex(Protocol.redirectionKey)
this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
@Throws(IOException::class) @Throws(IOException::class)
override fun toByteArray(): ByteArray { override fun toByteArray(): ByteArray {
this.writeHex(Protocol._0825data0) this.writeHex(Protocol.constantData0)
this.writeHex(Protocol._0825data2) this.writeHex(Protocol.constantData1)
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex("00 01 00 00 03 09 00 0C 00 01") this.writeHex("00 01 00 00 03 09 00 0C 00 01")
this.writeIP(serverIP) this.writeIP(serverIP)
......
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
/**
* 客户端请求验证码图片数据的第几部分
*/
@ExperimentalUnsignedTypes
@PacketId("00 BA 31")
class ClientVerificationCodeTransmissionRequestPacket(
private val count: Int,
private val qq: Long,
private val token0825: ByteArray,
private val verificationSequence: Int,
private val token00BA: ByteArray
) : ClientPacket() {
@TestedSuccessfully
override fun encode() {
this.writeByte(count)//part of packet id
this.writeQQ(qq)
this.writeHex(Protocol.fixVer2)
this.writeHex(Protocol.key00BA)
this.encryptAndWrite(Protocol.key00BA) {
it.writeHex("00 02 00 00 08 04 01 E0")
it.writeHex(Protocol.constantData1)
it.writeHex("00 00 38")
it.write(token0825)
it.writeHex("01 03 00 19")
it.writeHex(Protocol.publicKey)
it.writeHex("13 00 05 00 00 00 00")
it.writeByte(verificationSequence)
it.writeHex("00 28")
it.write(token00BA)
it.writeHex("00 10")
it.writeHex(Protocol.key00BAFix)
}
}
}
/**
* 服务器发送验证码图片文件一部分过来
*
* @author Him188moe
*/
class ServerVerificationCodeTransmissionPacket(input: DataInputStream, private val dataSize: Int, private val packetId: ByteArray) : ServerVerificationCodePacket(input) {
lateinit var verificationCodePartN: ByteArray
lateinit var verificationToken: ByteArray//56bytes
var transmissionCompleted: Boolean = false//验证码是否已经传输完成
lateinit var token00BA: ByteArray//40 bytes
var count: Int = 0
@ExperimentalUnsignedTypes
override fun decode() {
this.verificationToken = this.input.readNBytesAt(10, 56)
val length = this.input.readShortAt(66)
this.verificationCodePartN = this.input.readNBytes(length)
this.input.skip(2)
this.transmissionCompleted = this.input.readBoolean().not()
this.token00BA = this.input.readNBytesAt(dataSize - 57, 40)
this.count = byteArrayOf(0, 0, packetId[2], packetId[3]).toUHexString().hexToInt()
}
}
/**
* 暂不了解意义
*
* @author Him188moe
*/
class ServerVerificationCodeRepeatPacket(input: DataInputStream) : ServerVerificationCodePacket(input) {
lateinit var token00BA: ByteArray//56 bytes
lateinit var tgtgtKeyUpdate: ByteArray
@ExperimentalUnsignedTypes
override fun decode() {
token00BA = this.input.readNBytesAt(10, 56)
tgtgtKeyUpdate = getRandomByteArray(16)
}
}
abstract class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) {
@PacketId("00 BA")
class Encrypted(input: DataInputStream) : ServerPacket(input) {
@ExperimentalUnsignedTypes
fun decrypt(): ServerVerificationCodePacket {
this.input goto 14
val data = TEA.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, Protocol.key00BA.hexToBytes())
return if (data.size == 95) {
ServerVerificationCodeRepeatPacket(data.dataInputStream())
} else {
ServerVerificationCodeTransmissionPacket(data.dataInputStream(), data.size, this.input.readNBytesAt(3, 4))
}
}
}
}
package net.mamoe.mirai.network.packet.message package net.mamoe.mirai.network.packet.action
import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.network.packet.*
...@@ -11,15 +11,15 @@ import java.io.DataInputStream ...@@ -11,15 +11,15 @@ import java.io.DataInputStream
@PacketId("00 CD") @PacketId("00 CD")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class ClientSendFriendMessagePacket( class ClientSendFriendMessagePacket(
val robotQQ: Int, private val robotQQ: Long,
val targetQQ: Int, private val targetQQ: Long,
val sessionKey: ByteArray, private val sessionKey: ByteArray,
val message: String private val message: String
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(robotQQ) this.writeQQ(robotQQ)
this.writeHex(Protocol._fixVer) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
it.writeQQ(robotQQ) it.writeQQ(robotQQ)
...@@ -52,13 +52,10 @@ class ClientSendFriendMessagePacket( ...@@ -52,13 +52,10 @@ class ClientSendFriendMessagePacket(
it.writeByte(0x01) it.writeByte(0x01)
it.writeShort(bytes.size) it.writeShort(bytes.size)
it.write(bytes) it.write(bytes)
}//todo check }
} }
} }
} }
@PacketId("00 CD") @PacketId("00 CD")
class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) { class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input)
override fun decode() { \ No newline at end of file
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet.message package net.mamoe.mirai.network.packet.action
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 net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
...@@ -12,20 +11,20 @@ import java.io.DataInputStream ...@@ -12,20 +11,20 @@ import java.io.DataInputStream
@PacketId("00 02") @PacketId("00 02")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class ClientSendGroupMessagePacket( class ClientSendGroupMessagePacket(
private val groupId: Int,//不是 number private val groupId: Long,//不是 number
private val qq: Int, private val robotQQ: Long,
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
private val message: String private val message: String
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(qq) this.writeQQ(robotQQ)
this.writeHex(Protocol._fixVer) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
val bytes = message.toByteArray() val bytes = message.toByteArray()
it.writeByte(0x2A) it.writeByte(0x2A)
it.writeInt(groupId) it.writeGroup(groupId)
it.writeShort(56 + bytes.size) 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")
...@@ -46,24 +45,5 @@ class ClientSendGroupMessagePacket( ...@@ -46,24 +45,5 @@ class ClientSendGroupMessagePacket(
} }
} }
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() { \ No newline at end of file
}
}
\ No newline at end of file
...@@ -5,12 +5,14 @@ import net.mamoe.mirai.network.packet.* ...@@ -5,12 +5,14 @@ import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.ClientLoginStatus import net.mamoe.mirai.utils.ClientLoginStatus
/** /**
* 改变在线状态: "我在线上", "隐身" 等
*
* @author Him188moe * @author Him188moe
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@PacketId("00 EC") @PacketId("00 EC")
class ClientLoginStatusPacket( class ClientChangeOnlineStatusPacket(
private val qq: Int, private val qq: Long,
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
private val loginStatus: ClientLoginStatus private val loginStatus: ClientLoginStatus
...@@ -18,7 +20,7 @@ class ClientLoginStatusPacket( ...@@ -18,7 +20,7 @@ class ClientLoginStatusPacket(
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol._fixVer) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
it.writeHex("01 00") it.writeHex("01 00")
it.writeByte(loginStatus.id) it.writeByte(loginStatus.id)
......
...@@ -2,11 +2,7 @@ package net.mamoe.mirai.network.packet.login ...@@ -2,11 +2,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.* import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataOutputStream import java.io.DataOutputStream
/** /**
...@@ -18,7 +14,7 @@ import java.io.DataOutputStream ...@@ -18,7 +14,7 @@ import java.io.DataOutputStream
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@TestedSuccessfully @TestedSuccessfully
class ClientPasswordSubmissionPacket( class ClientPasswordSubmissionPacket(
private val qq: Int, private val qq: Long,
private val password: String, private val password: String,
private val loginTime: Int, private val loginTime: Int,
private val loginIP: String, private val loginIP: String,
...@@ -28,10 +24,10 @@ class ClientPasswordSubmissionPacket( ...@@ -28,10 +24,10 @@ class ClientPasswordSubmissionPacket(
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
override fun encode() { override fun encode() {
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol._0836_622_fix1) this.writeHex(Protocol.passwordSubmissionKey1)
this.writeHex(Protocol.publicKey) this.writeHex(Protocol.publicKey)
this.writeHex("00 00 00 10") this.writeHex("00 00 00 10")
this.writeHex(Protocol._0836key1) this.writeHex(Protocol.key0836)
this.encryptAndWrite(Protocol.shareKey.hexToBytes()) { this.encryptAndWrite(Protocol.shareKey.hexToBytes()) {
it.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825) it.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825)
...@@ -43,17 +39,22 @@ class ClientPasswordSubmissionPacket( ...@@ -43,17 +39,22 @@ class ClientPasswordSubmissionPacket(
@PacketId("08 36 31 04") @PacketId("08 36 31 04")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class ClientLoginResendPacket3104(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null) class ClientLoginResendPacket3104(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr) : ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr)
@PacketId("08 36 31 05")
@ExperimentalUnsignedTypes
class ClientLoginResendPacket3105(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, null)
@PacketId("08 36 31 06") @PacketId("08 36 31 06")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class ClientLoginResendPacket3106(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null) class ClientLoginResendPacket3106(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv_0006_encr: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr) : ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv_0006_encr)
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
open class ClientLoginResendPacket internal constructor( open class ClientLoginResendPacket internal constructor(
val qq: Int, val qq: Long,
val password: String, val password: String,
val loginTime: Int, val loginTime: Int,
val loginIP: String, val loginIP: String,
...@@ -64,12 +65,12 @@ open class ClientLoginResendPacket internal constructor( ...@@ -64,12 +65,12 @@ open class ClientLoginResendPacket internal constructor(
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol._0836_622_fix1) this.writeHex(Protocol.passwordSubmissionKey1)
this.writeHex(Protocol.publicKey) this.writeHex(Protocol.publicKey)
this.writeHex("00 00 00 10") this.writeHex("00 00 00 10")
this.writeHex(Protocol._0836key1) this.writeHex(Protocol.key0836)
this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
override fun toByteArray(): ByteArray { override fun toByteArray(): ByteArray {
this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv_0006_encr) this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv_0006_encr)
...@@ -91,7 +92,7 @@ open class ClientLoginResendPacket internal constructor( ...@@ -91,7 +92,7 @@ open class ClientLoginResendPacket internal constructor(
* @author Him188moe * @author Him188moe
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
private fun DataOutputStream.writePart1(qq: Int, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, tlv_0006_encr: ByteArray? = null) { private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, tlv_0006_encr: ByteArray? = null) {
//this.writeInt(System.currentTimeMillis().toInt()) //this.writeInt(System.currentTimeMillis().toInt())
this.writeHex("01 12")//tag this.writeHex("01 12")//tag
...@@ -110,12 +111,12 @@ private fun DataOutputStream.writePart1(qq: Int, password: String, loginTime: In ...@@ -110,12 +111,12 @@ private fun DataOutputStream.writePart1(qq: Int, password: String, loginTime: In
this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey) this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey)
} }
//fix //fix
this.writeHex(Protocol._0836_622_fix2) this.writeHex(Protocol.passwordSubmissionKey2)
this.writeHex("00 1A")//tag this.writeHex("00 1A")//tag
this.writeHex("00 40")//length this.writeHex("00 40")//length
this.write(TEACryptor.encrypt(Protocol._0836_622_fix2.hexToBytes(), tgtgtKey)) this.write(TEA.encrypt(Protocol.passwordSubmissionKey2.hexToBytes(), tgtgtKey))
this.writeHex(Protocol._0825data0) this.writeHex(Protocol.constantData0)
this.writeHex(Protocol._0825data2) this.writeHex(Protocol.constantData1)
this.writeQQ(qq) this.writeQQ(qq)
this.writeZero(4) this.writeZero(4)
......
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEACryptor
/**
* @author Him188moe
*/
@PacketId("00 BA 31 01")
@ExperimentalUnsignedTypes
class ClientLoginVerificationCodePacket(
private val qq: Int,
private val token0825: ByteArray,
private val sequence: Int,
private val token00BA: ByteArray
) : ClientPacket() {
override fun encode() {
this.writeQQ(qq)
this.writeHex(Protocol.fixVer)
this.writeHex(Protocol._00BaKey)
this.write(TEACryptor.CRYPTOR_00BAKEY.encrypt(object : ByteArrayDataOutputStream() {
override fun toByteArray(): ByteArray {
this.writeHex("00 02 00 00 08 04 01 E0")
this.writeHex(Protocol._0825data2)
this.writeHex("00 00 38")
this.write(token0825)
this.writeHex("01 03 00 19")
this.writeHex(Protocol.publicKey)
this.writeHex("13 00 05 00 00 00 00")
this.writeVarInt(sequence.toUInt())
this.writeHex("00 28")
this.write(token00BA)
this.writeHex("00 10")
this.writeHex(Protocol._00BaFixKey)
return super.toByteArray()
}
}.toByteArray()))
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet.login
/**
* @author Him188moe
*/
enum class LoginState {
/**
* 登录成功
*/
SUCCEED,
/**
* 密码错误
*/
WRONG_PASSWORD,
/**
* 被冻结
*/
BLOCKED,
/**
* QQ 号码输入有误
*/
UNKNOWN_QQ_NUMBER,
/**
* 账号开启了设备锁. 暂不支持设备锁登录
*/
DEVICE_LOCK,
/**
* 账号被回收
*/
TAKEN_BACK,
/**
* 需要验证码登录
*/
VERIFICATION_CODE,
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
*/
UNKNOWN,
}
\ No newline at end of file
...@@ -6,19 +6,7 @@ import java.io.DataInputStream ...@@ -6,19 +6,7 @@ import java.io.DataInputStream
/** /**
* @author Him188moe * @author Him188moe
*/ */
class ServerLoginResponseFailedPacket(val state: State, input: DataInputStream) : ServerPacket(input) { class ServerLoginResponseFailedPacket(val loginState: LoginState, input: DataInputStream) : ServerPacket(input) {
enum class State {
WRONG_PASSWORD,
// UNKNOWN,//? 要再次发送某数据包
RETYPE_PASSWORD,//similar to [WRONG_PASSWORD]
BLOCKED,//你的帐号存在被盗风险,已进入保护模式
UNKNOWN_QQ_NUMBER,//你输入的帐号不存在
DEVICE_LOCK,//设备锁
TAKEN_BACK,//被回收
// VERIFICATION_CODE,//需要验证码
// SUCCEED,
}
override fun decode() { override fun decode() {
} }
} }
\ No newline at end of file
package net.mamoe.mirai.network.packet.login 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.*
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.network.packet.readNBytesAt import net.mamoe.mirai.utils.TestedSuccessfully
import net.mamoe.mirai.network.packet.readVarString
import net.mamoe.mirai.util.TestedSuccessfully
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToBytes
import net.mamoe.mirai.utils.toUHexString import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
...@@ -15,8 +11,8 @@ import java.io.DataInputStream ...@@ -15,8 +11,8 @@ import java.io.DataInputStream
* @author NaturalHG * @author NaturalHG
*/ */
class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(input) { class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(input) {
lateinit var _0828_rec_decr_key: ByteArray//16 bytes| lateinit var sessionResponseDecryptionKey: ByteArray//16 bytes|
lateinit var nick: String lateinit var nickname: String
lateinit var token38: ByteArray lateinit var token38: ByteArray
lateinit var token88: ByteArray lateinit var token88: ByteArray
...@@ -26,72 +22,6 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in ...@@ -26,72 +22,6 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
@TestedSuccessfully @TestedSuccessfully
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
override fun decode() { override fun decode() {
//测试完成 @NaturalHG
/**
* Version 1 @Deprecated
this.input.skip(7)//8
encryptionKey = this.input.readNBytesAt(16)//24
this.input.skip(2)//25->26
token38 = this.input.readNBytesAt(56)//81->82
this.input.skip(60L)//142
//??
var b = this.input.readNBytesAt(2)
val msgLength = when (b.toUByteArray().toUHexString()) {
"01 07" -> 0
"00 33" -> 28
"01 10" -> 65
else -> throw IllegalStateException()
}//144
System.out.println(msgLength)
this.input.skip(17L + msgLength)//161+msgLength
this.input.skip(10)//171+msgLength
_0828_rec_decr_key = this.input.readNBytesAt(16)//187+msgLength
this.input.skip(2)
token88 = this.input.readNBytesAt(136)//325+ // msgLength
this.input.skip(299L)//624+msgLength
//varString (nickLength bytes)
val nickLength = this.input.readByteAt().toInt()//625+msgLength
System.out.println(nickLength)
nick = this.input.readVarString(nickLength)//625+msgLength+nickLength
val dataIndex = packetDataLength - 31
/*
this.input.skip((dataIndex - (625 + msgLength + nickLength)) + 0L)//-31
gender = this.input.readByteAt().toUByte().toInt()//-30
this.input.skip(9)//-27
age = this.input.readShortAt()//-25
*/
age = 0
gender = 0
/*
age = HexToDec(取文本中间(data, 取文本长度(data) - 82, 5))
gender = 取文本中间(data, 取文本长度(data) - 94, 2)
*/
* **/
/** version 2 */
this.input.skip(7)//8 this.input.skip(7)//8
this.encryptionKey = this.input.readNBytes(16)//24 this.encryptionKey = this.input.readNBytes(16)//24
...@@ -106,32 +36,25 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in ...@@ -106,32 +36,25 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
else -> throw IllegalStateException(id) else -> throw IllegalStateException(id)
} }
this._0828_rec_decr_key = this.input.readNBytesAt(171 + msgLength, 16) this.sessionResponseDecryptionKey = this.input.readNBytesAt(171 + msgLength, 16)
this.token88 = this.input.readNBytesAt(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.nickname = this.input.readVarString(nickLength)
//this.age = this.input.goto(packetDataLength - 28).readShortAt() //this.age = this.input.goto(packetDataLength - 28).readShortAt()
//this.gender = this.input.goto(packetDataLength - 32).readByteAt().toInt() //this.gender = this.input.goto(packetDataLength - 32).readByteAt().toInt()
} }
}
class ServerLoginResponseSuccessPacketEncrypted(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
class Encrypted(input: DataInputStream) : ServerPacket(input) {
@ExperimentalUnsignedTypes
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {
input goto 14
return ServerLoginResponseSuccessPacket(TEA.decrypt(TEA.decrypt(input.readAllBytes().cutTail(1), Protocol.shareKey), tgtgtKey).dataInputStream());
}
} }
@ExperimentalUnsignedTypes }
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket { \ No newline at end of file
input goto 14
var bytes = input.readAllBytes()
bytes = bytes.copyOfRange(0, bytes.size - 1)
println(bytes.toUByteArray().toUHexString())
return ServerLoginResponseSuccessPacket(DataInputStream(TEACryptor.decrypt(TEACryptor.decrypt(bytes, Protocol.shareKey.hexToBytes()), tgtgtKey).inputStream()));
//TeaDecrypt(取文本中间(data, 43, 取文本长度(data) - 45), m_0828_rec_decr_key)
}
}
...@@ -2,12 +2,8 @@ package net.mamoe.mirai.network.packet.login ...@@ -2,12 +2,8 @@ package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.packet.PacketId import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.utils.TestedSuccessfully
import net.mamoe.mirai.utils.TEACryptor
import net.mamoe.mirai.utils.hexToUBytes
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream import java.io.DataInputStream
/** /**
...@@ -45,33 +41,9 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) : ...@@ -45,33 +41,9 @@ class ServerLoginResponseResendPacket(input: DataInputStream, val flag: Flag) :
} }
} }
} }
}
class ServerLoginResponseResendPacketEncrypted(input: DataInputStream, private val flag: ServerLoginResponseResendPacket.Flag) : ServerPacket(input) {
override fun decode() {
} class Encrypted(input: DataInputStream, private val flag: Flag) : ServerPacket(input) {
@TestedSuccessfully
@TestedSuccessfully fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket = ServerLoginResponseResendPacket(decryptBy(tgtgtKey), flag)
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket {
//this.input.skip(7)
this.input goto 14
var data: ByteArray = this.input.readAllBytes()
data = TEACryptor.CRYPTOR_SHARE_KEY.decrypt(data.let { it.copyOfRange(0, it.size - 1) });
data = TEACryptor.decrypt(data, tgtgtKey)
return ServerLoginResponseResendPacket(data.dataInputStream(), flag)
} }
} }
fun main() {
val tgtgtkey = "9E 83 61 FF 18 61 4B 77 34 FE 1C 9C E2 03 B4 F2".hexToUBytes()
ServerLoginResponseResendPacketEncrypted("02 37 13 08 36 31 03 76 E4 B8 DD 00 00 00 94 9B 87 00 87 7F 9E D0 E5 6A F6 17 41 02 0C AA F3 AC C8 CF 4E C6 9D EC FA 6C BD F8 7C 4B A5 28 80 CC DE B5 0A 41 8E 63 CE 5E 30 D8 A6 83 92 0E 2E 5C 35 E5 6E 62 3D FE 17 DD 7C 47 9A AD EF F0 F7 2A 6F 21 32 99 1B 6D E1 DA BE 68 2F 26 A9 93 DE 1B 4F 11 F0 AF A1 06 7B 85 53 46 D2 A3 DD A6 BE F2 76 8A 61 BF 15 FD 17 C4 45 DB EC 05 51 56 46 63 48 87 49 79 0D 40 DF 9D D9 99 93 EC D0 44 7B 4A 79 EB BD 08 10 18 29 0E 85 EE 26 A0 CD 40 00 2F 3E ED F4 A4 C3 01 5E 82 F5 A8 02 FA 70 EB F2 07 AD FF 0E DA 08 7A 3A FE B6 F4 5D 98 18 F7 58 C2 19 21 AF 29 D2 95 16 CE C4 A3 5F B0 E6 23 C2 B2 C6 5F 03 42 C2 44 C2 B0 A0 3F 95 8E 89 EF FC EC E4 BF 03 CB DA 9C D3 84 3F 9B A0 F1 B4 14 6E 23 D5 74 79 6F 89 DA B8 33 DB EF 0B 21 E1 27 27 57 8B 56 CB D9 BF C2 A8 25 6E 48 23 EB 31 9D 03".hexToUBytes().toByteArray().dataInputStream(), ServerLoginResponseResendPacket.Flag.`08 36 31 03`).decrypt(tgtgtkey.toByteArray()).let { it.decode();println(it._0836_tlv0006_encr.toUHexString()) }
val data = "94 9B 87 00 87 7F 9E D0 E5 6A F6 17 41 02 0C AA F3 AC C8 CF 4E C6 9D EC FA 6C BD F8 7C 4B A5 28 80 CC DE B5 0A 41 8E 63 CE 5E 30 D8 A6 83 92 0E 2E 5C 35 E5 6E 62 3D FE 17 DD 7C 47 9A AD EF F0 F7 2A 6F 21 32 99 1B 6D E1 DA BE 68 2F 26 A9 93 DE 1B 4F 11 F0 AF A1 06 7B 85 53 46 D2 A3 DD A6 BE F2 76 8A 61 BF 15 FD 17 C4 45 DB EC 05 51 56 46 63 48 87 49 79 0D 40 DF 9D D9 99 93 EC D0 44 7B 4A 79 EB BD 08 10 18 29 0E 85 EE 26 A0 CD 40 00 2F 3E ED F4 A4 C3 01 5E 82 F5 A8 02 FA 70 EB F2 07 AD FF 0E DA 08 7A 3A FE B6 F4 5D 98 18 F7 58 C2 19 21 AF 29 D2 95 16 CE C4 A3 5F B0 E6 23 C2 B2 C6 5F 03 42 C2 44 C2 B0 A0 3F 95 8E 89 EF FC EC E4 BF 03 CB DA 9C D3 84 3F 9B A0 F1 B4 14 6E 23 D5 74 79 6F 89 DA B8 33 DB EF 0B 21 E1 27 27 57 8B 56 CB D9 BF C2 A8 25 6E 48 23 EB 31 9D".hexToUBytes()
val d1 = TEACryptor.CRYPTOR_SHARE_KEY.decrypt(data.toByteArray())
ServerLoginResponseResendPacket(TEACryptor.decrypt(d1, tgtgtkey.toByteArray()).dataInputStream(), ServerLoginResponseResendPacket.Flag.`08 36 31 03`).let { it.decode();println(it._0836_tlv0006_encr.toUHexString()) }
}
\ No newline at end of file
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.cutTail
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.TestedSuccessfully
import net.mamoe.mirai.utils.hexToUBytes
import java.io.DataInputStream
/**
* 收到这个包意味着需要验证码登录, 并且能得到验证码图片文件的一半
*
* @author Him188moe
*/
class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, private val packetLength: Int) : ServerPacket(input) {
lateinit var verifyCodePart1: ByteArray
lateinit var token00BA: ByteArray
var unknownBoolean: Boolean? = null
@TestedSuccessfully
@ExperimentalUnsignedTypes
override fun decode() {
val verifyCodeLength = this.input.goto(78).readShort()//2bytes
this.verifyCodePart1 = this.input.readNBytes(verifyCodeLength.toInt())
this.input.skip(1)
this.unknownBoolean = this.input.readByte().toInt() == 1
this.token00BA = this.input.goto(packetLength - 60).readNBytes(40)
}
class Encrypted(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
fun decrypt(): ServerLoginResponseVerificationCodeInitPacket {
this.input goto 14
val data = TEA.CRYPTOR_SHARE_KEY.decrypt(this.input.readAllBytes().cutTail(1));
return ServerLoginResponseVerificationCodeInitPacket(data.dataInputStream(), data.size)
}
}
}
fun main() {
val data = "FB 01 04 03 33 00 01 00 BA 02 03 2C 13 00 05 01 00 00 01 23 00 38 D5 01 05 8B 67 4D 52 5A FA 92 DB 99 18 D4 F0 72 03 E0 17 71 7C 8A 45 74 1F C3 2D F8 61 96 0D 93 0D 8C 51 95 70 F8 F9 CB B9 2D 5D BC 4F 5D 89 5F E7 59 8C E4 E5 A2 04 56 02 BC 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D 00 01 00 28 42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8 01 15 00 10 F6 F0 50 03 74 BB 18 91 D3 55 8D 7F BB 53 15 7A".hexToUBytes().toByteArray();
ServerLoginResponseVerificationCodeInitPacket(
data.dataInputStream(),
data.size
).let { it.decode(); println(it) }
}
/*
data
FB 01 04 03 33 00 01 00 BA 02 03 2C 13 00 05 01 00 00 01 23 00 38 D5 01 05 8B 67 4D 52 5A FA 92 DB 99 18 D4 F0 72 03 E0 17 71 7C 8A 45 74 1F C3 2D F8 61 96 0D 93 0D 8C 51 95 70 F8 F9 CB B9 2D 5D BC 4F 5D 89 5F E7 59 8C E4 E5 A2 04 56 02 BC 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D 00 01 00 28 42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8 01 15 00 10 F6 F0 50 03 74 BB 18 91 D3 55 8D 7F BB 53 15 7A
length 700
verify code
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 45 50 4C 54 45 FE F6 ED E2 F1 DF F3 FF F2 11 77 48 FE FE F3 F1 F9 EA D7 FD E7 F8 F9 EC FC EF E7 E8 FF EE 2D 69 48 2A 8A 5D 29 7A 52 F0 ED E1 A9 C7 B1 65 96 79 AB E0 C2 C3 F0 D5 42 7D 5C 4A 99 72 89 AA 93 51 73 5C 6E BA 94 42 BD 7A 0B 00 00 09 C5 49 44 41 54 58 C3 AC 99 8B 76 AB 3A 12 44 91 D0 1B 10 08 04 FF FF A9 B3 5B 60 C7 AF 38 77 EE 0C EB C4 76 6C 07 95 BA AB AB AB 75 BA EE 1F 5D DE CB 63 08 C1 C7 A8 AD BC B6 31 46 6B 3B A3 79 94 E7 F3 63 79 E4 D9 98 89 37 7D F7 FF BB 3C 10 82 6F B7 F7 2A B6 5B 07 EF B5 16 60 DE 1B 63 3A 1F 7E BE 6D 0C 50 EC 09 E1 FE 76 B8 3F FC 6B 0C 1D 5B 37 1F 3E E1 AE 61 9A D8 B6 07 93 B7 56 4D 66 22 06 9D FC EE 5F BF F9 3F 81 90 BB 99 F3 49 E2 1D DA 43 38 13 44 14 8C 9D B4 8E E7 65 40 11 E4 BB FE 8C 5B 78 41 F1 AF 01 48 2E 08 6D 8B AD F1 BA 05 F9 C4 E0 25 E6 31 BA EB 2A 90 C3 74 26 FC 1E 80 CF 14 91 44 7F FC 39 FF D4 37 10 82 C0 B3 B0 67 BF 2E 5E 5B F4 46 E5 3A 5F D7 BE A6 54 17 2D 7C 0D D7 DA E1 7E 93 C7 E5 DB 9E 6E 9F FD A3 14 9C 7F 23 97 55 FA DC 6F 74 8A CC 04 49 03 3F DE 4E 5C 4A 95 9C 53 8D 7A EA 82 F9 10 77 7F EE E5 43 28 C2 9F 00 BA 5B C0 59 A4 CE DB BA AE DB BC 14 D5 2A 82 A5 83 E0 30 92 19 A8 99 36 A7 15 7C BC DF F6 BE D7 86 C0 7F C9 C5 ED 93 C7 1F B9 E0 56 CB AC 61 F5 5D 2E 00 F0 93 D2 5A 9D B6 B0 3F 4B 4D 5C B9 0F C1 EE 29 46 65 4E 40 46 E2 E4 1B 91 C3 A5 2E FE D3 7A 3C 84 0B E3 F3 E2 57 16 2F 08 B9 E6 5A 73 29 65 29 4E B9 25 CF 6B 9A 97 48 20 B2 35 46 1B 6D AD D6 9A 57 69 45 B2 1A 00 90 05 51 8D C7 24 F8 1B B9 FE A6 41 B8 83 6C 59 05 84 9F 44 0F 85 05 9A 97 5A 97 BC A6 6D 81 FE 59 DE 2F 4B 5E E4 DF B2 A4 19 AA 06 D9 FE F9 33 4D 7E 6A 40 FC 97 34 BF 84 E4 81 81 ED E9 DC 85 32 56 47 E5 A4 F0 2D 6F 4D 2A BA 65 4B 73 89 B6 58 5E D7 35 8D 69 E4 4A 6B 76 50 C1 5C 3A D9 59 11 CF 37 99 FA 48 88 70 7F F4 9F 22 12 F2 24 91 3E 2B BF 28 A5 34 68 C0 50 A3 55 DD A4 E3 9C 6E 85 99 95 B6 24 2E 18 D9 3C 5C B1 4D AA 2F 08 E1 75 F1 F0 6B 49 FC BC E3 8D
token00ba
42 E6 18 57 D4 B1 4D AE 51 27 D5 EF A2 38 91 39 15 37 6C 5A FE 75 93 49 DB FC 57 3C 12 3F 26 D9 16 1D 83 45 8B 78 39 D8
*/
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream
/**
* @author Him188moe
*/
class ServerLoginResponseVerificationCodePacket(input: DataInputStream, private val packetLength: Int) : ServerPacket(input) {
lateinit var verifyCode: ByteArray
lateinit var token00BA: ByteArray
var unknownBoolean: Boolean? = null
@ExperimentalUnsignedTypes
override fun decode() {
val verifyCodeLength = this.input.goto(78).readShort()//2bytes
this.verifyCode = this.input.readNBytes(verifyCodeLength.toInt())
this.input.skip(1)
this.unknownBoolean = this.input.readByte().toInt() == 1
this.token00BA = this.input.goto(packetLength - 60).readNBytes(40)
}
}
class ServerLoginResponseVerificationCodePacketEncrypted(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
fun decrypt(): ServerLoginResponseVerificationCodePacket {
this.input goto 14
val data = TEACryptor.CRYPTOR_SHARE_KEY.decrypt(this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) });
return ServerLoginResponseVerificationCodePacket(data.dataInputStream(), data.size)
}
}
\ No newline at end of file
...@@ -4,10 +4,8 @@ import net.mamoe.mirai.network.packet.ServerPacket ...@@ -4,10 +4,8 @@ import net.mamoe.mirai.network.packet.ServerPacket
import java.io.DataInputStream import java.io.DataInputStream
/** /**
* Congratulations!
*
* @author Him188moe * @author Him188moe
*/ */
class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input) { class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input)
override fun decode() { \ No newline at end of file
}
}
\ No newline at end of file
package net.mamoe.mirai.network.packet.verification
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.utils.TEACryptor
import java.io.DataInputStream
/**
* @author Him188moe
*/
class ServerVerificationCodePacket(input: DataInputStream) : ServerPacket(input) {
lateinit var verifyCode: ByteArray
lateinit var verifyToken: ByteArray
var unknownBoolean: Boolean? = null
lateinit var token00BA: ByteArray
var count: Int = 0
@ExperimentalUnsignedTypes
override fun decode() {
TODO()
val verifyCodeLength = this.input.goto(78).readShort()//2bytes
this.verifyCode = this.input.readNBytes(verifyCodeLength.toInt())
this.input.skip(1)
this.unknownBoolean = this.input.readByte().toInt() == 1
//this.token00BA = this.input.goto(packetLength - 60).readNBytesAt(40)
}
}
class ServerVerificationCodePacketEncrypted(input: DataInputStream) : ServerPacket(input) {
override fun decode() {
}
fun decrypt(token00BA: ByteArray): ServerVerificationCodePacket {
this.input goto 14
val data = TEACryptor.decrypt(token00BA, this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) });
return ServerVerificationCodePacket(data.dataInputStream())
}
}
\ No newline at end of file
package net.mamoe.mirai.plugin; package net.mamoe.mirai.plugin;
public class MiraiPluginBase { import net.mamoe.mirai.Robot;
/**
* 插件基类.
* <p>
* 插件属于整个 Mirai, 而不是属于单个 {@link Robot}.
*
* @see net.mamoe.mirai.event.MiraiEventManager
* @see net.mamoe.mirai.event.MiraiEventManagerKt
*/
public abstract class MiraiPluginBase {
} }
...@@ -4,15 +4,12 @@ package net.mamoe.mirai.task; ...@@ -4,15 +4,12 @@ package net.mamoe.mirai.task;
public interface MiraiTaskExceptionHandler { public interface MiraiTaskExceptionHandler {
void onHandle(Throwable e); void onHandle(Throwable e);
static MiraiTaskExceptionHandler byDefault(){ static MiraiTaskExceptionHandler printing() {
return byPrint(); return Throwable::printStackTrace;
}
static MiraiTaskExceptionHandler byIgnore(){
return a -> {};
} }
static MiraiTaskExceptionHandler byPrint(){ static MiraiTaskExceptionHandler ignoring() {
return Throwable::printStackTrace; return a -> {
};
} }
} }
...@@ -2,7 +2,7 @@ package net.mamoe.mirai.task; ...@@ -2,7 +2,7 @@ package net.mamoe.mirai.task;
import net.mamoe.mirai.event.MiraiEventHook; import net.mamoe.mirai.event.MiraiEventHook;
import net.mamoe.mirai.event.events.server.ServerDisableEvent; import net.mamoe.mirai.event.events.server.ServerDisabledEvent;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Future; import java.util.concurrent.Future;
...@@ -24,7 +24,7 @@ public final class MiraiTaskManager { ...@@ -24,7 +24,7 @@ public final class MiraiTaskManager {
this.pool = new MiraiThreadPool(); this.pool = new MiraiThreadPool();
MiraiEventHook MiraiEventHook
.onEvent(ServerDisableEvent.class) .onEvent(ServerDisabledEvent.class)
.handler(a -> this.pool.close()) .handler(a -> this.pool.close())
.mount(); .mount();
...@@ -35,7 +35,7 @@ public final class MiraiTaskManager { ...@@ -35,7 +35,7 @@ public final class MiraiTaskManager {
*/ */
public void execute(Runnable runnable) { public void execute(Runnable runnable) {
this.execute(runnable, MiraiTaskExceptionHandler.byDefault()); this.execute(runnable, MiraiTaskExceptionHandler.printing());
} }
public void execute(Runnable runnable, MiraiTaskExceptionHandler handler) { public void execute(Runnable runnable, MiraiTaskExceptionHandler handler) {
...@@ -51,7 +51,7 @@ public final class MiraiTaskManager { ...@@ -51,7 +51,7 @@ public final class MiraiTaskManager {
public <D> Future<D> submit(Callable<D> callable) { public <D> Future<D> submit(Callable<D> callable) {
return this.submit(callable, MiraiTaskExceptionHandler.byDefault()); return this.submit(callable, MiraiTaskExceptionHandler.printing());
} }
public <D> Future<D> submit(Callable<D> callable, MiraiTaskExceptionHandler handler) { public <D> Future<D> submit(Callable<D> callable, MiraiTaskExceptionHandler handler) {
...@@ -69,7 +69,7 @@ public final class MiraiTaskManager { ...@@ -69,7 +69,7 @@ public final class MiraiTaskManager {
* 异步任务 * 异步任务
*/ */
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback) { public <D> void ansycTask(Callable<D> callable, Consumer<D> callback) {
this.ansycTask(callable, callback, MiraiTaskExceptionHandler.byDefault()); this.ansycTask(callable, callback, MiraiTaskExceptionHandler.printing());
} }
public <D> void ansycTask(Callable<D> callable, Consumer<D> callback, MiraiTaskExceptionHandler handler) { public <D> void ansycTask(Callable<D> callable, Consumer<D> callback, MiraiTaskExceptionHandler handler) {
...@@ -87,7 +87,7 @@ public final class MiraiTaskManager { ...@@ -87,7 +87,7 @@ public final class MiraiTaskManager {
*/ */
public void repeatingTask(Runnable runnable, long intervalMillis) { public void repeatingTask(Runnable runnable, long intervalMillis) {
this.repeatingTask(runnable, intervalMillis, MiraiTaskExceptionHandler.byDefault()); this.repeatingTask(runnable, intervalMillis, MiraiTaskExceptionHandler.printing());
} }
public void repeatingTask(Runnable runnable, long intervalMillis, MiraiTaskExceptionHandler handler) { public void repeatingTask(Runnable runnable, long intervalMillis, MiraiTaskExceptionHandler handler) {
...@@ -95,7 +95,7 @@ public final class MiraiTaskManager { ...@@ -95,7 +95,7 @@ public final class MiraiTaskManager {
} }
public void repeatingTask(Runnable runnable, long intervalMillis, int times) { public void repeatingTask(Runnable runnable, long intervalMillis, int times) {
this.repeatingTask(runnable, intervalMillis, times, MiraiTaskExceptionHandler.byDefault()); this.repeatingTask(runnable, intervalMillis, times, MiraiTaskExceptionHandler.printing());
} }
public void repeatingTask(Runnable runnable, long intervalMillis, int times, MiraiTaskExceptionHandler handler) { public void repeatingTask(Runnable runnable, long intervalMillis, int times, MiraiTaskExceptionHandler handler) {
......
...@@ -4,10 +4,14 @@ package net.mamoe.mirai.utils; ...@@ -4,10 +4,14 @@ package net.mamoe.mirai.utils;
* @author Him188moe * @author Him188moe
*/ */
public enum ClientLoginStatus { public enum ClientLoginStatus {
/**
* 我在线上
*/
ONLINE(0x0A); ONLINE(0x0A);
// TODO: 2019/8/31 add more // TODO: 2019/8/31 add more ClientLoginStatus
public final int id;//1byte
public final int id;//1 ubyte
ClientLoginStatus(int id) { ClientLoginStatus(int id) {
this.id = id; this.id = id;
......
...@@ -6,5 +6,5 @@ import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap; ...@@ -6,5 +6,5 @@ import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap;
/** /**
* @author Him188moe * @author Him188moe
*/ */
public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedListMap<Integer, C> { public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedListMap<Long, C> {
} }
package net.mamoe.mirai.utils;
import lombok.Data;
/**
* @author Him188moe
*/
@Data
public final class RobotAccount {
public final long qqNumber;
public final String password;
public RobotAccount(long qqNumber, String password) {
this.qqNumber = qqNumber;
this.password = password;
}
}
...@@ -6,12 +6,13 @@ import java.nio.ByteBuffer; ...@@ -6,12 +6,13 @@ import java.nio.ByteBuffer;
import java.util.Random; import java.util.Random;
/** /**
* TEA 加密
*
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java * @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/ */
public class TEACryptor { public final class TEA {
public static final TEACryptor CRYPTOR_SHARE_KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol.shareKey)); public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.shareKey));
public static final TEACryptor CRYPTOR_0825KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._0825key)); public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.key0825));
public static final TEACryptor CRYPTOR_00BAKEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._00BaKey));
private static final long UINT32_MASK = 0xffffffffL; private static final long UINT32_MASK = 0xffffffffL;
private final long[] mKey; private final long[] mKey;
...@@ -23,26 +24,33 @@ public class TEACryptor { ...@@ -23,26 +24,33 @@ public class TEACryptor {
private int mOutPos; private int mOutPos;
private int mPreOutPos; private int mPreOutPos;
private boolean isFirstBlock; private boolean isFirstBlock;
private boolean isRand;
public TEACryptor(byte[] key) { public TEA(byte[] key) {
mKey = new long[4]; mKey = new long[4];
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
mKey[i] = pack(key, i * 4, 4); mKey[i] = pack(key, i * 4, 4);
} }
isRand = true;
mRandom = new Random(); mRandom = new Random();
isFirstBlock = true; isFirstBlock = true;
} }
public static byte[] encrypt(byte[] source, byte[] key) { public static byte[] encrypt(byte[] source, byte[] key) {
return new TEACryptor(key).encrypt(source); return new TEA(key).encrypt(source);
}
public static byte[] encrypt(byte[] source, String keyHex) {
return encrypt(source, UtilsKt.hexToBytes(keyHex));
} }
public static byte[] decrypt(byte[] source, byte[] key) { public static byte[] decrypt(byte[] source, byte[] key) {
return new TEACryptor(key).decrypt(source); return new TEA(key).decrypt(source);
}
public static byte[] decrypt(byte[] source, String keyHex) {
return decrypt(source, UtilsKt.hexToBytes(keyHex));
} }
@SuppressWarnings("SameParameterValue")
private static long pack(byte[] bytes, int offset, int len) { private static long pack(byte[] bytes, int offset, int len) {
long result = 0; long result = 0;
int max_offset = len > 8 ? offset + 8 : offset + len; int max_offset = len > 8 ? offset + 8 : offset + len;
...@@ -53,11 +61,7 @@ public class TEACryptor { ...@@ -53,11 +61,7 @@ public class TEACryptor {
} }
private int rand() { private int rand() {
return isRand ? mRandom.nextInt() : 0xff00ff; return mRandom.nextInt();
}
public void enableRandom(boolean rand) {
isRand = rand;
} }
private byte[] encode(byte[] bytes) { private byte[] encode(byte[] bytes) {
...@@ -109,6 +113,7 @@ public class TEACryptor { ...@@ -109,6 +113,7 @@ public class TEACryptor {
isFirstBlock = false; isFirstBlock = false;
} }
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean decodeOneBlock(byte[] ciphertext, int offset, int len) { private boolean decodeOneBlock(byte[] ciphertext, int offset, int len) {
for (mIndexPos = 0; mIndexPos < 8; mIndexPos++) { for (mIndexPos = 0; mIndexPos < 8; mIndexPos++) {
if (mOutPos + mIndexPos < len) { if (mOutPos + mIndexPos < len) {
...@@ -125,6 +130,7 @@ public class TEACryptor { ...@@ -125,6 +130,7 @@ public class TEACryptor {
} }
@SuppressWarnings("SameParameterValue")
private byte[] encrypt(byte[] plaintext, int offset, int len) { private byte[] encrypt(byte[] plaintext, int offset, int len) {
mInBlock = new byte[8]; mInBlock = new byte[8];
mIV = new byte[8]; mIV = new byte[8];
...@@ -175,11 +181,12 @@ public class TEACryptor { ...@@ -175,11 +181,12 @@ public class TEACryptor {
return mOutput; return mOutput;
} }
private byte[] decrypt(byte[] ciphertext, int offset, int len) { @SuppressWarnings("SameParameterValue")
private byte[] decrypt(byte[] cipherText, int offset, int len) {
if (len % 8 != 0 || len < 16) { if (len % 8 != 0 || len < 16) {
throw new IllegalArgumentException("must len % 8 == 0 && len >= 16"); throw new IllegalArgumentException("must len % 8 == 0 && len >= 16");
} }
mIV = decode(ciphertext, offset); mIV = decode(cipherText, offset);
mIndexPos = mIV[0] & 7; mIndexPos = mIV[0] & 7;
int plen = len - mIndexPos - 10; int plen = len - mIndexPos - 10;
isFirstBlock = true; isFirstBlock = true;
...@@ -198,7 +205,7 @@ public class TEACryptor { ...@@ -198,7 +205,7 @@ public class TEACryptor {
} }
if (mIndexPos == 8) { if (mIndexPos == 8) {
isFirstBlock = false; isFirstBlock = false;
if (!decodeOneBlock(ciphertext, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw new RuntimeException("Unable to decode"); throw new RuntimeException("Unable to decode");
} }
} }
...@@ -208,20 +215,20 @@ public class TEACryptor { ...@@ -208,20 +215,20 @@ public class TEACryptor {
if (mIndexPos < 8) { if (mIndexPos < 8) {
mOutput[outpos++] = isFirstBlock ? mOutput[outpos++] = isFirstBlock ?
mIV[mIndexPos] : mIV[mIndexPos] :
(byte) (ciphertext[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]); (byte) (cipherText[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]);
++mIndexPos; ++mIndexPos;
} }
if (mIndexPos == 8) { if (mIndexPos == 8) {
mPreOutPos = mOutPos - 8; mPreOutPos = mOutPos - 8;
isFirstBlock = false; isFirstBlock = false;
if (!decodeOneBlock(ciphertext, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw new RuntimeException("Unable to decode"); throw new RuntimeException("Unable to decode");
} }
} }
} }
for (g = 0; g < 7; g++) { for (g = 0; g < 7; g++) {
if (mIndexPos < 8) { if (mIndexPos < 8) {
if ((ciphertext[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]) != 0) { if ((cipherText[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]) != 0) {
throw new RuntimeException(); throw new RuntimeException();
} else { } else {
++mIndexPos; ++mIndexPos;
...@@ -230,7 +237,7 @@ public class TEACryptor { ...@@ -230,7 +237,7 @@ public class TEACryptor {
if (mIndexPos == 8) { if (mIndexPos == 8) {
mPreOutPos = mOutPos; mPreOutPos = mOutPos;
if (!decodeOneBlock(ciphertext, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw new RuntimeException("Unable to decode"); throw new RuntimeException("Unable to decode");
} }
} }
......
package net.mamoe.mirai.util package net.mamoe.mirai.utils
/** /**
* @author Him188moe * @author Him188moe
......
...@@ -8,7 +8,9 @@ import java.lang.reflect.Field ...@@ -8,7 +8,9 @@ import java.lang.reflect.Field
import java.util.* import java.util.*
import java.util.zip.CRC32 import java.util.zip.CRC32
@JvmSynthetic
fun ByteArray.toHexString(): String = toHexString(" ") fun ByteArray.toHexString(): String = toHexString(" ")
fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) { fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) {
var ret = it.toString(16).toUpperCase() var ret = it.toString(16).toUpperCase()
if (ret.length == 1) { if (ret.length == 1) {
...@@ -21,18 +23,23 @@ fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(s ...@@ -21,18 +23,23 @@ fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(s
fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator) fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator)
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@JvmSynthetic
fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString() fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString()
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString(separator) { @JvmSynthetic
var ret = it.toString(16).toUpperCase() fun UByteArray.toUHexString(separator: String = " "): String {
if (ret.length == 1) { return this.joinToString(separator) {
ret = "0$ret" var ret = it.toString(16).toUpperCase()
if (ret.length == 1) {
ret = "0$ret"
}
return@joinToString ret
} }
return@joinToString ret
} }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
@JvmSynthetic
fun UByteArray.toUHexString(): String = this.toUHexString(" ") fun UByteArray.toUHexString(): String = this.toUHexString(" ")
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
...@@ -51,7 +58,7 @@ fun String.hexToUBytes(): UByteArray = Protocol.hexToUBytes(this) ...@@ -51,7 +58,7 @@ fun String.hexToUBytes(): UByteArray = Protocol.hexToUBytes(this)
fun String.hexToShort(): Short = hexToBytes().let { ((it[1].toInt() shl 8) + it[0]).toShort() } fun String.hexToShort(): Short = hexToBytes().let { ((it[1].toInt() shl 8) + it[0]).toShort() }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun String.hexToInt(): Int = hexToBytes().let { ((it[3].toInt() shl 24) + (it[2].toInt() shl 16) + (it[1].toInt() shl 8) + it[0]) } fun String.hexToInt(): Int = hexToBytes().let { ((it[0].toInt() shl 24) + (it[1].toInt() shl 16) + (it[2].toInt() shl 8) + it[3]) }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun String.hexToByte(): Byte = hexToBytes()[0] fun String.hexToByte(): Byte = hexToBytes()[0]
...@@ -65,17 +72,18 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream()) ...@@ -65,17 +72,18 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream())
fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().let { t(it); return it.toByteArray() } fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().let { t(it); return it.toByteArray() }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun getRandomKey(length: Int): ByteArray { fun getRandomByteArray(length: Int): ByteArray {
val bytes = LinkedList<Byte>() val bytes = LinkedList<Byte>()
repeat(length) { bytes.add((Math.random() * 255).toByte()) } repeat(length) { bytes.add((Math.random() * 255).toByte()) }
return bytes.toByteArray() return bytes.toByteArray()
} }
@JvmSynthetic
operator fun File.plus(child: String): File = File(this, child) operator fun File.plus(child: String): File = File(this, child)
private const val GTK_BASE_VALUE: Int = 5381 private const val GTK_BASE_VALUE: Int = 5381
fun getGTK(sKey: String): Int { internal fun getGTK(sKey: String): Int {
var value = GTK_BASE_VALUE var value = GTK_BASE_VALUE
for (c in sKey.toCharArray()) { for (c in sKey.toCharArray()) {
value += (value shl 5) + c.toInt() value += (value shl 5) + c.toInt()
...@@ -85,7 +93,7 @@ fun getGTK(sKey: String): Int { ...@@ -85,7 +93,7 @@ fun getGTK(sKey: String): Int {
return value return value
} }
fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() } internal fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
/** /**
...@@ -118,3 +126,13 @@ fun Any.getAllDeclaredFields(): List<Field> { ...@@ -118,3 +126,13 @@ fun Any.getAllDeclaredFields(): List<Field> {
return list return list
} }
private const val ZERO_BYTE: Byte = 0
fun ByteArray.removeZeroTail(): ByteArray {
var i = this.size - 1
while (this[i] == ZERO_BYTE) {
--i
}
return this.copyOfRange(0, i + 1)
}
\ No newline at end of file
...@@ -126,10 +126,10 @@ public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedListMap<String ...@@ -126,10 +126,10 @@ public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedListMap<String
return result==null?defaultV:result.toString(); return result==null?defaultV:result.toString();
} }
public String getStringOrThrow(String key, Callable<Throwable> throwableCallable) throws Throwable { public String getStringOrThrow(String key, Supplier<Throwable> exceptionSupplier) throws Throwable {
Object result = this.getOrDefault(key, null); Object result = this.getOrDefault(key, null);
if(result == null){ if(result == null){
throw throwableCallable.call(); throw exceptionSupplier.get();
} }
return result.toString(); return result.toString();
} }
......
package net.mamoe.mirai.utils.setting; package net.mamoe.mirai.utils.setting;
import net.mamoe.mirai.plugin.MiraiPluginBase;
import org.ini4j.Config; import org.ini4j.Config;
import org.ini4j.Ini; import org.ini4j.Ini;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* Mirai Config * Thread-safe Mirai Config <br>
* Only support {INI} format * Only supports <code>INI</code> format <br>
* Support MAP and LIST * Supports {@link Map} and {@link List}
* Thread safe
*/ */
public class MiraiSetting { public class MiraiSettings {
private File file; private File file;
...@@ -23,14 +24,21 @@ public class MiraiSetting { ...@@ -23,14 +24,21 @@ public class MiraiSetting {
private volatile Map<String, MiraiSettingSection> cacheSection = new ConcurrentHashMap<>(); private volatile Map<String, MiraiSettingSection> cacheSection = new ConcurrentHashMap<>();
public MiraiSetting(File file){ public MiraiSettings(MiraiPluginBase pluginBase, String filename) {
// TODO: 2019/9/6 每个插件独立文件夹存放
this(new File(filename));
}
public MiraiSettings(File file) {
if(!file.getName().contains(".")){ if(!file.getName().contains(".")){
file = new File(file.getParent() + file.getName() + ".ini"); file = new File(file.getPath() + ".ini");
} }
this.file = file; this.file = file;
try { try {
if(file.exists()){ if(file.exists()){
file.createNewFile(); if (!file.createNewFile()) {
throw new RuntimeException("cannot create config file " + file);
}
} }
Config config = new Config(); Config config = new Config();
config.setMultiSection(true); config.setMultiSection(true);
...@@ -42,12 +50,12 @@ public class MiraiSetting { ...@@ -42,12 +50,12 @@ public class MiraiSetting {
} }
} }
public void setSection(String key, MiraiSettingSection section){ public synchronized void setSection(String key, MiraiSettingSection section) {
cacheSection.put(key, section); cacheSection.put(key, section);
} }
public MiraiSettingMapSection getMapSection(String key){ public synchronized MiraiSettingMapSection getMapSection(String key) {
if(!cacheSection.containsKey(key)) { if(!cacheSection.containsKey(key)) {
MiraiSettingMapSection section = new MiraiSettingMapSection(); MiraiSettingMapSection section = new MiraiSettingMapSection();
if(ini.containsKey(key)){ if(ini.containsKey(key)){
...@@ -58,7 +66,7 @@ public class MiraiSetting { ...@@ -58,7 +66,7 @@ public class MiraiSetting {
return (MiraiSettingMapSection) cacheSection.get(key); return (MiraiSettingMapSection) cacheSection.get(key);
} }
public MiraiSettingListSection getListSection(String key){ public synchronized MiraiSettingListSection getListSection(String key) {
if(!cacheSection.containsKey(key)) { if(!cacheSection.containsKey(key)) {
MiraiSettingListSection section = new MiraiSettingListSection(); MiraiSettingListSection section = new MiraiSettingListSection();
if(ini.containsKey(key)){ if(ini.containsKey(key)){
...@@ -85,7 +93,7 @@ public class MiraiSetting { ...@@ -85,7 +93,7 @@ public class MiraiSetting {
} }
} }
public void clearCache(){ public synchronized void clearCache() {
cacheSection.clear(); cacheSection.clear();
} }
} }
......
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import net.mamoe.mirai.Robot
import net.mamoe.mirai.network.packet.login.LoginState
import net.mamoe.mirai.utils.RobotAccount
import java.util.*
/**
* @author Him188moe
*/
val qqList = "2258868346----123456789.\n" +
"1545483785----yuk7k1dxnf3jn5\n" +
"2948786488----123123123\n" +
"3059674084----qq123456\n" +
"1918079979----123456789.\n" +
"3050478794----18872590321\n" +
"3331537204----123456789.\n" +
"2128659972----123456789.\n" +
"3435376516----abc123456\n" +
"2980527804----a123456\n" +
"2752195782----qq123456789\n" +
"3130257966----13415986622\n" +
"1802730396----123456789\n" +
"3021732783----15866103923\n" +
"306499606----abc123456\n" +
"2893904328----abc123456\n" +
"1765904806----123456789\n" +
"3254202261----15223045268\n" +
"2947707697----abc123456\n" +
"3500959200----123456789.\n" +
"2169513531----123456789.\n" +
"2983688661----a123456\n" +
"1246882194----pz49779866\n" +
"2315275635----147258369\n" +
"2802294904----123456789\n" +
"2955364492----1234567890\n" +
"1753325115----123456789\n" +
"2642725191----qq123456\n" +
"2152972686----123456789.\n" +
"2845953617----123456789.\n" +
"3329641753----123456789.\n" +
"1458302685----123456789a\n" +
"2351156352----987654321\n" +
"2304786984----fkhwt53787\n" +
"3322756212----123456789.\n" +
"3187253283----123456789.\n" +
"3168715730----147258369\n" +
"2189916732----18831892323\n" +
"2965337631----123456789.\n" +
"1901802165----123456789.\n" +
"414015319----abc123456\n" +
"3400636089----123456789a\n" +
"3530336304----seoua80060\n" +
"3147312971----123456789.\n" +
"3011083526----yp70y9\n" +
"286888078----abc123456\n" +
"3126754112----1234567890\n" +
"2924643025----123123123\n" +
"341870356----ncvhZtQD\n" +
"3358177328----123456789a\n" +
"1396419201----eakuj14475\n" +
"3541159580----123456789.\n" +
"2540245592----1234567890\n" +
"2024802855----123456789.\n" +
"2578309660----1234567890\n" +
"1934965091----123456789.\n" +
"3449408956----a123456789\n" +
"2509348670----123456789.\n" +
"2305961679----123456789.\n" +
"3532858521----123456789.\n" +
"3308276898----123456789a\n" +
"1760897490----123456789\n" +
"2920800012----123123123\n" +
"2923942248----123123123\n" +
"3216600579----13882755274\n" +
"3100259299----qq123456\n" +
"3242723735----1234567890\n" +
"2142733062----123456789.\n" +
"1557689693----123456789\n" +
"3505693439----sb2662vqy6q\n" +
"3231125974----123456789.\n" +
"3433048975----13893690883\n" +
"3168017129----18780999209\n" +
"2922045831----123123123\n" +
"3578152022----a123456789\n" +
"2116254935----147258369\n" +
"3158479284----1234567890\n" +
"3149394424----qq123456789\n" +
"2829521712----123456789.\n" +
"3218671461----123456789.\n" +
"3035873094----123456789a\n" +
"2224518667----147258369\n" +
"3175801590----123456789.\n" +
"3203228181----123456789a\n" +
"3213497536----123456789a\n" +
"3377317115----123456789\n" +
"2672537341----qq123456789\n" +
"2945957617----123123123\n" +
"2763390197----123456789.\n" +
"3322711709----123456789."
fun main() {
val goodRobotList = Collections.synchronizedList(mutableListOf<Robot>())
qqList.split("\n").forEach {
GlobalScope.launch {
val strings = it.split("----")
val robot = Robot(RobotAccount(strings[0].toLong(), strings[1].let { password ->
if (password.endsWith(".")) {
return@let password.substring(0, password.length - 1)
}
return@let password
}), listOf())
robot.network.tryLogin().whenComplete { state, _ ->
if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) {
goodRobotList.add(robot)
}
}
}
}
Thread.sleep(9 * 3000)
println(goodRobotList.joinToString("\n") { it.account.qqNumber.toString() + " " + it.account.password })
}
...@@ -3,11 +3,17 @@ import lombok.AllArgsConstructor; ...@@ -3,11 +3,17 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import net.mamoe.mirai.network.Protocol; import net.mamoe.mirai.network.Protocol;
import net.mamoe.mirai.network.packet.ClientPacketKt;
import net.mamoe.mirai.utils.UtilsKt;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Scanner;
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.function.BiConsumer; import java.util.function.BiConsumer;
...@@ -32,15 +38,24 @@ public class HexComparator { ...@@ -32,15 +38,24 @@ public class HexComparator {
private static final String BLUE = "\033[34m"; private static final String BLUE = "\033[34m";
public static final List<HexReader> consts = new LinkedList<>(){{ public static final List<HexReader> consts = new LinkedList<>() {{
add(new HexReader("90 5E 39 DF 00 02 76 E4 B8 DD 00")); add(new HexReader("90 5E 39 DF 00 02 76 E4 B8 DD 00"));
}}; }};
private static class ConstMatcher { private static class ConstMatcher {
private static final List<Field> CONST_FIELDS = new LinkedList<>() {{ private static final List<Field> CONST_FIELDS = new LinkedList<>() {{
List.of(Protocol.class).forEach(aClass -> Arrays.stream(aClass.getDeclaredFields()).peek(this::add).forEach(Field::trySetAccessible)); List.of(Protocol.class).forEach(aClass -> Arrays.stream(aClass.getDeclaredFields()).peek(this::add).forEach(Field::trySetAccessible));
List.of(TestConsts.class).forEach(aClass -> Arrays.stream(aClass.getDeclaredFields()).peek(this::add).forEach(Field::trySetAccessible));
}}; }};
@SuppressWarnings({"unused", "NonAsciiCharacters"})
private static class TestConsts {
private static final String 牛逼 = UtilsKt.toUHexString("牛逼".getBytes(), " ");
private static final String _1994701021 = ClientPacketKt.toHexString(1994701021, " ");
private static final String _1040400290 = ClientPacketKt.toHexString(1040400290, " ");
private static final String _580266363 = ClientPacketKt.toHexString(580266363, " ");
}
private final List<Match> matches = new LinkedList<>(); private final List<Match> matches = new LinkedList<>();
private ConstMatcher(String hex) { private ConstMatcher(String hex) {
...@@ -89,6 +104,23 @@ public class HexComparator { ...@@ -89,6 +104,23 @@ public class HexComparator {
} }
} }
private static void buildConstNameChain(int length, ConstMatcher constMatcher, StringBuilder constNameBuilder) {
for (int i = 0; i < length; i++) {
constNameBuilder.append(" ");
String match = constMatcher.getMatchedConstName(i / 4);
if (match != null) {
int appendedNameLength = match.length();
constNameBuilder.append(match);
while (constMatcher.getMatchedConstName(i++ / 4) != null) {
if (appendedNameLength-- <= 0) {
constNameBuilder.append(" ");
}
}
}
}
}
private static String compare(String hex1s, String hex2s) { private static String compare(String hex1s, String hex2s) {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
...@@ -105,10 +137,17 @@ public class HexComparator { ...@@ -105,10 +137,17 @@ public class HexComparator {
StringBuilder numberLine = new StringBuilder(); StringBuilder numberLine = new StringBuilder();
StringBuilder hex1ConstName = new StringBuilder();
StringBuilder hex1b = new StringBuilder(); StringBuilder hex1b = new StringBuilder();
StringBuilder hex2b = new StringBuilder(); StringBuilder hex2b = new StringBuilder();
StringBuilder hex2ConstName = new StringBuilder();
int dif = 0; int dif = 0;
int length = Math.max(hex1.length, hex2.length) * 4;
buildConstNameChain(length, constMatcher1, hex1ConstName);
buildConstNameChain(length, constMatcher2, hex2ConstName);
for (int i = 0; i < Math.max(hex1.length, hex2.length); ++i) { for (int i = 0; i < Math.max(hex1.length, hex2.length); ++i) {
String h1 = null; String h1 = null;
String h2 = null; String h2 = null;
...@@ -152,7 +191,7 @@ public class HexComparator { ...@@ -152,7 +191,7 @@ public class HexComparator {
} }
} }
numberLine.append(UNKNOWN).append(getNumber(i)).append(" "); numberLine.append(UNKNOWN).append(getFixedNumber(i)).append(" ");
hex1b.append(" ").append(h1).append(" "); hex1b.append(" ").append(h1).append(" ");
hex2b.append(" ").append(h2).append(" "); hex2b.append(" ").append(h2).append(" ");
if (isDif) { if (isDif) {
...@@ -165,42 +204,46 @@ public class HexComparator { ...@@ -165,42 +204,46 @@ public class HexComparator {
return (builder.append(" ").append(dif).append(" 个不同").append("\n") return (builder.append(" ").append(dif).append(" 个不同").append("\n")
.append(numberLine).append("\n") .append(numberLine).append("\n")
.append(hex1ConstName).append("\n")
.append(hex1b).append("\n") .append(hex1b).append("\n")
.append(hex2b)) .append(hex2b).append("\n")
.append(hex2ConstName).append("\n")
)
.toString(); .toString();
} }
private static void doConstReplacement(StringBuilder builder){ private static void doConstReplacement(StringBuilder builder) {
String mirror = builder.toString(); String mirror = builder.toString();
HexReader hexs = new HexReader(mirror); HexReader hexs = new HexReader(mirror);
for (AtomicInteger i=new AtomicInteger(0);i.get()<builder.length();i.addAndGet(1)){ for (AtomicInteger i = new AtomicInteger(0); i.get() < builder.length(); i.addAndGet(1)) {
hexs.setTo(i.get()); hexs.setTo(i.get());
consts.forEach(a -> { consts.forEach(a -> {
hexs.setTo(i.get()); hexs.setTo(i.get());
List<Integer> posToPlaceColor = new LinkedList<>(); List<Integer> posToPlaceColor = new LinkedList<>();
AtomicBoolean is = new AtomicBoolean(false); AtomicBoolean is = new AtomicBoolean(false);
a.readFully((c,d) -> { a.readFully((c, d) -> {
if(c.equals(hexs.readHex())){ if (c.equals(hexs.readHex())) {
posToPlaceColor.add(d); posToPlaceColor.add(d);
}else{ } else {
is.set(false); is.set(false);
} }
}); });
if(is.get()){ if (is.get()) {
AtomicInteger adder = new AtomicInteger(); AtomicInteger adder = new AtomicInteger();
posToPlaceColor.forEach(e -> { posToPlaceColor.forEach(e -> {
builder.insert(e + adder.getAndAdd(BLUE.length()),BLUE); builder.insert(e + adder.getAndAdd(BLUE.length()), BLUE);
}); });
} }
}); });
} }
} }
private static String getNumber(int number) {
private static String getFixedNumber(int number) {
if (number < 10) { if (number < 10) {
return "00" + number; return "00" + number;
} }
...@@ -210,8 +253,31 @@ public class HexComparator { ...@@ -210,8 +253,31 @@ public class HexComparator {
return String.valueOf(number); return String.valueOf(number);
} }
public static void main(String[] args) { private static String getClipboardString() {
Transferable trans = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
if (trans.isDataFlavorSupported(DataFlavor.stringFlavor)) {
try {
return (String) trans.getTransferData(DataFlavor.stringFlavor);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("Hex1: ");
var hex1 = scanner.nextLine();
System.out.println("Hex2: ");
var hex2 = scanner.nextLine();
System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
System.out.println(HexComparator.compare(hex1, hex2));
System.out.println();
}
/*
System.out.println(HexComparator.compare( System.out.println(HexComparator.compare(
//mirai //mirai
...@@ -230,66 +296,63 @@ public class HexComparator { ...@@ -230,66 +296,63 @@ public class HexComparator {
"6F 0B DF 92 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9 E9 E9 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC\n\n\n" "6F 0B DF 92 00 02 76 E4 B8 DD 00 00 04 53 00 00 00 01 00 00 15 85 00 00 01 55 35 05 8E C9 BA 16 D0 01 63 5B 59 4B 59 52 31 01 B9 00 00 00 00 00 00 00 00 00 00 00 00 00 E9 E9 E9 E9 00 00 00 00 00 00 00 00 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B AA BB CC DD EE FF AA BB CC\n\n\n"
));*/ ));*/
} }
} }
class HexReader{ class HexReader {
private String s; private String s;
private int pos = 0; private int pos = 0;
private int lastHaxPos = 0; private int lastHaxPos = 0;
public HexReader(String s){ public HexReader(String s) {
this.s = s; this.s = s;
} }
public String readHex(){ public String readHex() {
boolean isStr = false; boolean isStr = false;
String next = ""; String next = "";
for (;pos<s.length()-2;++pos){ for (; pos < s.length() - 2; ++pos) {
char s1 = ' '; char s1 = ' ';
if(pos != 0){ if (pos != 0) {
s1 = this.s.charAt(0); s1 = this.s.charAt(0);
} }
char s2 = this.s.charAt(pos+1); char s2 = this.s.charAt(pos + 1);
char s3 = this.s.charAt(pos+2); char s3 = this.s.charAt(pos + 2);
char s4 = ' '; char s4 = ' ';
if(this.s.length() != (this.pos+3)){ if (this.s.length() != (this.pos + 3)) {
s4 = this.s.charAt(pos+3); s4 = this.s.charAt(pos + 3);
} }
if( if (
Character.isSpaceChar(s1) && Character.isSpaceChar(s4) Character.isSpaceChar(s1) && Character.isSpaceChar(s4)
&& &&
(Character.isDigit(s2) || Character.isAlphabetic(s2)) (Character.isDigit(s2) || Character.isAlphabetic(s2))
&& &&
(Character.isDigit(s3) || Character.isAlphabetic(s3)) (Character.isDigit(s3) || Character.isAlphabetic(s3))
){ ) {
this.pos+=2; this.pos += 2;
this.lastHaxPos = this.pos+1; this.lastHaxPos = this.pos + 1;
return String.valueOf(s2) + String.valueOf(s3); return String.valueOf(s2) + s3;
} }
} }
return ""; return "";
} }
public void readFully(BiConsumer<String, Integer> processor){ public void readFully(BiConsumer<String, Integer> processor) {
this.reset(); this.reset();
String nextHax = this.readHex(); String nextHax = this.readHex();
while (!nextHax.equals(" ")){ while (!nextHax.equals(" ")) {
processor.accept(nextHax,this.lastHaxPos); processor.accept(nextHax, this.lastHaxPos);
nextHax = this.readHex(); nextHax = this.readHex();
} }
} }
public void setTo(int pos){ public void setTo(int pos) {
this.pos = pos; this.pos = pos;
} }
public void reset(){ public void reset() {
this.pos = 0; this.pos = 0;
} }
} }
import net.mamoe.mirai.utils.toUHexString
/**
* @author Him188moe
*/
fun main() {
println("牛逼".toByteArray().toUHexString())
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment