diff --git a/README.md b/README.md index 66c0555cf..21191dd68 100644 --- a/README.md +++ b/README.md @@ -15,23 +15,25 @@ The project is all for <b>learning proposes</b> and still in <b>developing stage ### 代码结构 Network部分使用 Kotlin 完成(因为kt有对 unsigned byte 的支持). -与插件相关性强(或其他在二次开发中容易接触的部分)均使用 Java 完成, +与插件相关性强(或其他在二次开发中容易接触)的部分使用 Java 完成, 同时也会针对kotlin提供优化的方法调用. 例如对'+'操作符的重载: `String+BufferedImage+QQ.At+Face+URL+String+File` 将会被自动处理为String消息. ### TODO - [x] 事件(Event)模块 -- [ ] 插件(Plugin)模块 **(Working on)** +- [ ] 插件(Plugin)模块 - [x] Network - Touch - [X] Network - Login - [X] Network - Session -- [ ] Network - Verification Code (Low priority) +- [ ] Network - Verification Code **(Working on)** - [X] Network - Message Receiving - [X] Network - Message Sending - [ ] Network - Events **(Working on)** - [ ] Robot - Friend/group list +- [ ] Robot - Actions(joining group, adding friend, etc.) - [ ] Message Section **(Working on)** - [ ] Contact +- [ ] UI **(Working on)** <br> diff --git a/mirai-core/pom.xml b/mirai-core/pom.xml index 3a43788e1..7f61f476c 100644 --- a/mirai-core/pom.xml +++ b/mirai-core/pom.xml @@ -56,6 +56,12 @@ <artifactId>snakeyaml</artifactId> <version>1.18</version> </dependency> + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-reflect</artifactId> + <version>1.3.41</version> + <scope>compile</scope> + </dependency> </dependencies> diff --git a/mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java b/mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java index 77c77cbfd..d959f504a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/MiraiMain.java @@ -1,28 +1,14 @@ package net.mamoe.mirai; -import net.mamoe.mirai.utils.config.MiraiConfig; -import net.mamoe.mirai.utils.config.MiraiConfigSection; - /** * @author Him188moe */ public final class MiraiMain { private static MiraiServer server; + public static void main(String[] args) { server = new MiraiServer(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - 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(); - } - - + Runtime.getRuntime().addShutdownHook(new Thread(() -> server.shutdown())); } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java index 6ba54eb76..81ba8639c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java @@ -2,24 +2,25 @@ package net.mamoe.mirai; import lombok.Getter; import net.mamoe.mirai.event.MiraiEventManager; -import net.mamoe.mirai.event.events.server.ServerDisableEvent; -import net.mamoe.mirai.event.events.server.ServerEnableEvent; -import net.mamoe.mirai.network.RobotNetworkHandler; -import net.mamoe.mirai.network.packet.ClientTouchPacket; +import net.mamoe.mirai.event.events.server.ServerDisabledEvent; +import net.mamoe.mirai.event.events.server.ServerEnabledEvent; +import net.mamoe.mirai.network.packet.login.LoginState; import net.mamoe.mirai.task.MiraiTaskManager; import net.mamoe.mirai.utils.LoggerTextFormat; import net.mamoe.mirai.utils.MiraiLogger; import net.mamoe.mirai.utils.config.MiraiConfig; 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.MiraiSettingMapSection; +import net.mamoe.mirai.utils.setting.MiraiSettings; import java.io.File; import java.io.IOException; -import java.util.LinkedList; import java.util.Scanner; +/** + * @author NaturalHG + */ public class MiraiServer { private static MiraiServer instance; @@ -27,17 +28,15 @@ public class MiraiServer { return instance; } - //mirai version private final static String MIRAI_VERSION = "1.0.0"; - //qq version private final static String QQ_VERSION = "4.9.0"; @Getter //is running under UNIX private boolean unix; - @Getter//file path + @Getter//file pathq public File parentFolder; @Getter @@ -48,30 +47,30 @@ public class MiraiServer { @Getter MiraiLogger logger; - MiraiSetting setting; + MiraiSettings settings; MiraiConfig qqs; - protected MiraiServer() { + MiraiServer() { instance = this; - this.onLoad(); - this.onEnable(); + this.onLoaded(); + this.onEnabled(); } private boolean enabled; - protected void shutdown() { + void shutdown() { if (this.enabled) { getLogger().info("About to shutdown Mirai"); - this.getEventManager().broadcastEvent(new ServerDisableEvent()); + this.eventManager.broadcastEventAsync(new ServerDisabledEvent()); getLogger().info("Data have been saved"); } } - private void onLoad() { + private void onLoaded() { this.parentFolder = new File(System.getProperty("user.dir")); this.unix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS"); @@ -88,7 +87,7 @@ public class MiraiServer { if (!setting.exists()) { this.initSetting(setting); } else { - this.setting = new MiraiSetting(setting); + this.settings = new MiraiSettings(setting); } File qqs = new File(this.parentFolder + "/QQ.yml"); @@ -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) { @@ -187,21 +127,21 @@ public class MiraiServer { } catch (IOException e) { e.printStackTrace(); } - this.setting = new MiraiSetting(setting); - MiraiSettingMapSection network = this.setting.getMapSection("network"); + this.settings = new MiraiSettings(setting); + MiraiSettingMapSection network = this.settings.getMapSection("network"); 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:100"); - MiraiSettingMapSection worker = this.setting.getMapSection("worker"); + MiraiSettingMapSection worker = this.settings.getMapSection("worker"); worker.set("core_task_pool_worker_amount", 5); - MiraiSettingMapSection plugin = this.setting.getMapSection("plugin"); + MiraiSettingMapSection plugin = this.settings.getMapSection("plugin"); plugin.set("debug", false); - this.setting.save(); + this.settings.save(); getLogger().info("initialized; changing can be made in setting file: " + setting.toString()); } @@ -227,11 +167,35 @@ public class MiraiServer { getLogger().info("QQ account initialized; changing can be made in Config file: " + qqConfig.toString()); } - private void onEnable() { - this.eventManager.broadcastEvent(new ServerEnableEvent()); + private void onEnabled() { this.enabled = true; + this.eventManager.broadcastEventAsync(new ServerEnabledEvent()); getLogger().info(LoggerTextFormat.GREEN + "Server enabled; Welcome to Mirai"); 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); + } + }); } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/Robot.java b/mirai-core/src/main/java/net/mamoe/mirai/Robot.java index a1d273d32..d4e711deb 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/Robot.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/Robot.java @@ -1,24 +1,77 @@ package net.mamoe.mirai; -import kotlin.jvm.internal.MagicApiIntrinsics; import lombok.Getter; import net.mamoe.mirai.contact.Group; import net.mamoe.mirai.contact.QQ; import net.mamoe.mirai.network.RobotNetworkHandler; import net.mamoe.mirai.utils.ContactList; -import net.mamoe.mirai.utils.config.MiraiConfig; +import net.mamoe.mirai.utils.RobotAccount; import net.mamoe.mirai.utils.config.MiraiConfigSection; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.io.Closeable; +import java.util.*; -public class Robot { +/** + * 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; + + /** + * Robot 联系人管理. + * + * @see Robot#contacts + */ + public final class ContactSystem { + private final ContactList<Group> groups = new ContactList<>(); + private final ContactList<QQ> qqs = new ContactList<>(); + + 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 @@ -26,66 +79,35 @@ public class Robot { @Getter private final List<String> owners; - private final ContactList<Group> groups = new ContactList<>(); - private final ContactList<QQ> qqs = new ContactList<>(); - public boolean isOwnBy(String ownerName) { return owners.contains(ownerName); } - public Robot(MiraiConfigSection<Object> data) throws Throwable { this( - data.getIntOrThrow("account", () -> new IllegalArgumentException("account")), - data.getStringOrThrow("password", () -> new IllegalArgumentException("password")), + new RobotAccount( + data.getLongOrThrow("account", () -> new IllegalArgumentException("account")), + data.getStringOrThrow("password", () -> new IllegalArgumentException("password")) + ), data.getAsOrDefault("owners", ArrayList::new) ); - } - public Robot(int qqNumber, String password, List<String> owners) { - this.qqNumber = qqNumber; - this.password = password; + public Robot(@NotNull RobotAccount account, @NotNull List<String> owners) { + Objects.requireNonNull(account); + Objects.requireNonNull(owners); + this.account = account; 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 void close() { + this.network.close(); + this.contacts.groups.values().forEach(Group::close); + this.contacts.groups.clear(); + this.contacts.qqs.clear(); } - 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){ - - } - - private MiraiConfigSection<Object> data;//late init - -} - -*/ diff --git a/mirai-core/src/main/java/net/mamoe/mirai/contact/Contact.kt b/mirai-core/src/main/java/net/mamoe/mirai/contact/Contact.kt index 844c5c050..ab33c4d10 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/contact/Contact.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/contact/Contact.kt @@ -1,14 +1,16 @@ package net.mamoe.mirai.contact +import net.mamoe.mirai.Robot import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.defaults.PlainText /** * A contact is a [QQ] or a [Group] for one particular [Robot] instance only. * + * @param robot Owner [Robot] * @author Him188moe */ -abstract class Contact(val number: Int) { +abstract class Contact(val robot: Robot, val number: Long) { /** * Async diff --git a/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt b/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt index 835a1b0a2..93640bacd 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/contact/Group.kt @@ -1,121 +1,118 @@ package net.mamoe.mirai.contact +import net.mamoe.mirai.Robot import net.mamoe.mirai.message.Message 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 members = ContactList<QQ>() - init { - Instances.groups.add(this) - } - override fun sendMessage(message: Message) { - + robot.network.messageHandler.sendGroupMessage(this, message) } override fun sendXMLMessage(message: String) { } + override fun close() { + this.members.clear() + } + companion object { - fun groupNumberToId(number: Int): Int { - val left: Int = number.toString().let { + fun groupNumberToId(number: Long): Long { + val left: Long = number.toString().let { if (it.length < 6) { return@groupNumberToId number } - it.substring(0, it.length - 6).toInt() + it.substring(0, it.length - 6).toLong() } - val right: Int = number.toString().let { - it.substring(it.length - 6).toInt() + val right: Long = number.toString().let { + it.substring(it.length - 6).toLong() } return when (left) { in 1..10 -> { - ((left + 202).toString() + right.toString()).toInt() + ((left + 202).toString() + right.toString()).toLong() } in 11..19 -> { - ((left + 469).toString() + right.toString()).toInt() + ((left + 469).toString() + right.toString()).toLong() } in 20..66 -> { - ((left + 208).toString() + right.toString()).toInt() + ((left + 208).toString() + right.toString()).toLong() } in 67..156 -> { - ((left + 1943).toString() + right.toString()).toInt() + ((left + 1943).toString() + right.toString()).toLong() } in 157..209 -> { - ((left + 199).toString() + right.toString()).toInt() + ((left + 199).toString() + right.toString()).toLong() } in 210..309 -> { - ((left + 389).toString() + right.toString()).toInt() + ((left + 389).toString() + right.toString()).toLong() } in 310..499 -> { - ((left + 349).toString() + right.toString()).toInt() + ((left + 349).toString() + right.toString()).toLong() } else -> number } } - @JvmStatic - fun main(args: Array<String>) { - groupNumberToId(580266363) - } - - fun groupIdToNumber(id: Int): Int { - var left: Int = id.toString().let { + fun groupIdToNumber(id: Long): Long { + var left: Long = id.toString().let { if (it.length < 6) { return@groupIdToNumber id } - it.substring(0 until it.length - 6).toInt() + it.substring(0 until it.length - 6).toLong() } return when (left) { in 203..212 -> { - val right: Int = id.toString().let { - it.substring(it.length - 6).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 6).toLong() } - ((left - 202).toString() + right.toString()).toInt() + ((left - 202).toString() + right.toString()).toLong() } in 480..488 -> { - val right: Int = id.toString().let { - it.substring(it.length - 6).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 6).toLong() } - ((left - 469).toString() + right.toString()).toInt() + ((left - 469).toString() + right.toString()).toLong() } in 2100..2146 -> { - val right: Int = id.toString().let { - it.substring(it.length - 7).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 7).toLong() } - left = left.toString().substring(0 until 3).toInt() - ((left - 208).toString() + right.toString()).toInt() + left = left.toString().substring(0 until 3).toLong() + ((left - 208).toString() + right.toString()).toLong() } in 2010..2099 -> { - val right: Int = id.toString().let { - it.substring(it.length - 6).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 6).toLong() } - ((left - 1943).toString() + right.toString()).toInt() + ((left - 1943).toString() + right.toString()).toLong() } in 2147..2199 -> { - val right: Int = id.toString().let { - it.substring(it.length - 7).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 7).toLong() } - left = left.toString().substring(0 until 3).toInt() - ((left - 199).toString() + right.toString()).toInt() + left = left.toString().substring(0 until 3).toLong() + ((left - 199).toString() + right.toString()).toLong() } in 4100..4199 -> { - val right: Int = id.toString().let { - it.substring(it.length - 7).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 7).toLong() } - left = left.toString().substring(0 until 3).toInt() - ((left - 389).toString() + right.toString()).toInt() + left = left.toString().substring(0 until 3).toLong() + ((left - 389).toString() + right.toString()).toLong() } in 3800..3989 -> { - val right: Int = id.toString().let { - it.substring(it.length - 7).toInt() + val right: Long = id.toString().let { + it.substring(it.length - 7).toLong() } - left = left.toString().substring(0 until 3).toInt() - ((left - 349).toString() + right.toString()).toInt() + left = left.toString().substring(0 until 3).toLong() + ((left - 349).toString() + right.toString()).toLong() } else -> id } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/contact/Lazy.kt b/mirai-core/src/main/java/net/mamoe/mirai/contact/Lazy.kt deleted file mode 100644 index cecd6001c..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/contact/Lazy.kt +++ /dev/null @@ -1,17 +0,0 @@ -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>() -} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/contact/QQ.kt b/mirai-core/src/main/java/net/mamoe/mirai/contact/QQ.kt index 5638a2de2..af009045e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/contact/QQ.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/contact/QQ.kt @@ -1,18 +1,21 @@ package net.mamoe.mirai.contact +import net.mamoe.mirai.Robot import net.mamoe.mirai.message.Message 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 */ -class QQ(number: Int) : Contact(number) { - init { - Instances.qqs.add(this) - } - +class QQ(robot: Robot, number: Long) : Contact(robot, number) { override fun sendMessage(message: Message) { - + robot.network.messageHandler.sendFriendMessage(this, message) } override fun sendXMLMessage(message: String) { @@ -21,6 +24,8 @@ class QQ(number: Int) : Contact(number) { /** * At(@) this account. + * + * @return an instance of [Message]. */ fun at(): At { return At(this) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEvent.java new file mode 100644 index 000000000..583f0dc2a --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEvent.java @@ -0,0 +1,27 @@ +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); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEventKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEventKt.kt new file mode 100644 index 000000000..10008103d --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/AsyncEventKt.kt @@ -0,0 +1,18 @@ +@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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/Cancellable.java b/mirai-core/src/main/java/net/mamoe/mirai/event/Cancellable.java similarity index 79% rename from mirai-core/src/main/java/net/mamoe/mirai/event/events/Cancellable.java rename to mirai-core/src/main/java/net/mamoe/mirai/event/Cancellable.java index 59167fda0..e12d0cb9b 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/Cancellable.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/Cancellable.java @@ -1,4 +1,4 @@ -package net.mamoe.mirai.event.events; +package net.mamoe.mirai.event; /** * @author NaturalHG diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEvent.java new file mode 100644 index 000000000..efa532ff0 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEvent.java @@ -0,0 +1,35 @@ +package net.mamoe.mirai.event; + +import net.mamoe.mirai.utils.EventException; + +/** + * @author NatrualHG + * @see AsyncEvent + */ +public abstract class MiraiEvent { + + private boolean cancelled; + + public boolean isCancelled() { + if (!(this instanceof Cancellable)) { + return false; + } + return this.cancelled; + } + + public void cancel() { + cancel(true); + } + + public void cancel(boolean value) { + if (!(this instanceof Cancellable)) { + throw new EventException("Event is not Cancellable"); + } + this.cancelled = value; + } + + public final MiraiEvent broadcast() { + MiraiEventManager.getInstance().broadcastEvent(this); + return this; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java index c8088be6e..d46deb7d8 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHook.java @@ -2,20 +2,21 @@ package net.mamoe.mirai.event; import lombok.Getter; import lombok.Setter; -import net.mamoe.mirai.event.events.Cancellable; -import net.mamoe.mirai.event.events.MiraiEvent; import java.io.Closeable; import java.util.function.Consumer; import java.util.function.Predicate; +/** + * @author NatrualHG + */ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { @Getter Class<T> eventClass; @Getter - private volatile Consumer<T> handler; + protected volatile Consumer<T> handler; @Getter private volatile int priority = 0; @@ -31,7 +32,7 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { * return true -> this hook need to be removed */ @Getter - private Predicate<T> valid; + protected Predicate<T> validChecker; public MiraiEventHook(Class<T> eventClass) { this(eventClass,null); @@ -58,26 +59,26 @@ public class MiraiEventHook<T extends MiraiEvent> implements Closeable { } - private MiraiEventHook<T> setValid(Predicate<T> valid) { - this.valid = valid; + private MiraiEventHook<T> setValidChecker(Predicate<T> validChecker) { + this.validChecker = validChecker; return this; } public MiraiEventHook<T> setValidUntil(Predicate<T> valid) { - return this.setValid(valid); + return this.setValidChecker(valid); } public MiraiEventHook<T> setValidWhile(Predicate<T> valid) { - return this.setValid(valid.negate()); + return this.setValidChecker(valid.negate()); } @SuppressWarnings("unchecked") public boolean accept(MiraiEvent event) { 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 { @Override public void close(){ this.handler = null; - this.valid = null; + this.validChecker = null; } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHookKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHookKt.kt new file mode 100644 index 000000000..4a072a2be --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventHookKt.kt @@ -0,0 +1,31 @@ +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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventKt.kt new file mode 100644 index 000000000..8183738b0 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventKt.kt @@ -0,0 +1,8 @@ +@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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManager.java b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManager.java index 385d83632..90622b1e2 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManager.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManager.java @@ -1,23 +1,25 @@ package net.mamoe.mirai.event; -import net.mamoe.mirai.MiraiServer; -import net.mamoe.mirai.event.events.MiraiEvent; - import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; +/** + * 线程安全的事件管理器. + * + * @author NaturalHG + * @see MiraiEventManagerKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现 + */ public class MiraiEventManager { - private MiraiEventManager() { + MiraiEventManager() { } - private static MiraiEventManager instance = new MiraiEventManager(); - public static MiraiEventManager getInstance() { - return MiraiEventManager.instance; + return EventManager.INSTANCE;//实例来自 kotlin 的 singleton } private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock(); @@ -117,23 +119,40 @@ public class MiraiEventManager { } - public void asyncBroadcastEvent(MiraiEvent event) { - this.asyncBroadcastEvent(event, a -> { - }); - } + public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event) { + Objects.requireNonNull(event); + if (!(event instanceof MiraiEvent)) { + throw new IllegalArgumentException("event must be instanceof MiraiEvent"); + } - public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Consumer<D> callback) { - MiraiServer.getInstance().getTaskManager().ansycTask(() -> { - MiraiEventManager.this.broadcastEvent(event); + CompletableFuture<E> future = new CompletableFuture<>(); + future.completeAsync(() -> { + MiraiEventManager.this.broadcastEvent((MiraiEvent) event); return event; - }, callback); + }); + return future; + } + + public <E extends AsyncEvent> CompletableFuture<E> broadcastEventAsync(E event, Consumer<E> callback) { + Objects.requireNonNull(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 future; } - public <D extends MiraiEvent> void asyncBroadcastEvent(D event, Runnable callback) { - asyncBroadcastEvent(event, t -> callback.run()); + public <D extends AsyncEvent> CompletableFuture<D> broadcastEventAsync(D event, Runnable callback) { + return broadcastEventAsync(event, t -> callback.run()); } - } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt index 1ae0337c2..e3cd769a1 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt @@ -1,30 +1,59 @@ +@file:JvmName("MiraiEventManagerKt") + package net.mamoe.mirai.event -import net.mamoe.mirai.event.events.MiraiEvent 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) { MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook)) } +/** + * 当下一次事件触发时调用 hook + */ fun <C : Class<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { MiraiEventManager.getInstance().hookOnce(MiraiEventHook<E>(this, hook)) } +/** + * 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook + */ 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) { this.java.hookAlways(hook) } +/** + * 当下一次事件触发时调用 hook + */ fun <C : KClass<E>, E : MiraiEvent> C.hookOnce(hook: (E) -> Unit) { this.java.hookOnce(hook) } +/** + * 每次事件触发时都会调用 hook, 直到 hook 返回 false 时停止 hook + */ fun <C : KClass<E>, E : MiraiEvent> C.hookWhile(hook: (E) -> Boolean) { this.java.hookWhile(hook) } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/MiraiEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/MiraiEvent.java deleted file mode 100644 index 0915b523a..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/MiraiEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.mamoe.mirai.event.events; - -import net.mamoe.mirai.event.MiraiEventManager; -import net.mamoe.mirai.utils.EventException; - -import java.util.function.Consumer; - -public abstract class MiraiEvent { - - private boolean cancelled; - - public boolean isCancelled() { - if (!(this instanceof Cancellable)) { - throw new EventException("Event is not Cancellable"); - } - return this.cancelled; - } - - public void cancel() { - cancel(true); - } - - public void cancel(boolean value) { - if (!(this instanceof Cancellable)) { - throw new EventException("Event is not Cancellable"); - } - this.cancelled = value; - } - - protected String eventName; - - public String getEventName() { - if (this.eventName == null) { - return this.getClass().getSimpleName(); - } - return this.eventName; - } - - public final MiraiEvent broadcast() { - MiraiEventManager.getInstance().broadcastEvent(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); - } -} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/BeforePacketSendEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/BeforePacketSendEvent.java new file mode 100644 index 000000000..97672e5d8 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/BeforePacketSendEvent.java @@ -0,0 +1,16 @@ +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); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ClientPacketEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ClientPacketEvent.java new file mode 100644 index 000000000..e25920e95 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ClientPacketEvent.java @@ -0,0 +1,18 @@ +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(); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java new file mode 100644 index 000000000..08d4b6a79 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java @@ -0,0 +1,22 @@ +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; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketSentEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketSentEvent.java new file mode 100644 index 000000000..963812029 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketSentEvent.java @@ -0,0 +1,15 @@ +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); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java new file mode 100644 index 000000000..c90e6a284 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java @@ -0,0 +1,17 @@ +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(); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java new file mode 100644 index 000000000..79a302b46 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java @@ -0,0 +1,17 @@ +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); + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java index ab410af4a..70d85ed78 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvent.java @@ -1,7 +1,7 @@ package net.mamoe.mirai.event.events.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 java.util.Objects; diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvents.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvents.kt index 581f92e87..9b452b1fd 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvents.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotEvents.kt @@ -1,19 +1,18 @@ package net.mamoe.mirai.event.events.robot -import net.mamoe.mirai.event.events.MiraiEvent -import net.mamoe.mirai.network.RobotNetworkHandler +import net.mamoe.mirai.Robot +import net.mamoe.mirai.event.MiraiEvent /** * @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 { FRIEND, GROUP } } - diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotLoginSucceedEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotLoginSucceedEvent.java index 15151ea91..a49d5f213 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotLoginSucceedEvent.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/robot/RobotLoginSucceedEvent.java @@ -7,5 +7,4 @@ public final class RobotLoginSucceedEvent extends RobotEvent { public RobotLoginSucceedEvent(Robot robot) { super(robot); } - } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java deleted file mode 100644 index dd0be1c57..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisableEvent.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.mamoe.mirai.event.events.server; - -import net.mamoe.mirai.event.events.MiraiEvent; - -public final class ServerDisableEvent extends MiraiEvent { - -} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisabledEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisabledEvent.java new file mode 100644 index 000000000..294498f1d --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerDisabledEvent.java @@ -0,0 +1,8 @@ +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 { + +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java deleted file mode 100644 index 18aeefccf..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnableEvent.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.mamoe.mirai.event.events.server; - -import net.mamoe.mirai.event.events.MiraiEvent; - -public final class ServerEnableEvent extends MiraiEvent { - - -} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnabledEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnabledEvent.java new file mode 100644 index 000000000..83b4772b9 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/server/ServerEnabledEvent.java @@ -0,0 +1,9 @@ +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 { + + +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/Message.java b/mirai-core/src/main/java/net/mamoe/mirai/message/Message.java index e56f328b6..16954515c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/Message.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/Message.java @@ -1,7 +1,9 @@ package net.mamoe.mirai.message; +import net.mamoe.mirai.contact.Contact; import net.mamoe.mirai.contact.QQ; 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.PlainText; import org.jetbrains.annotations.NotNull; @@ -11,7 +13,12 @@ import java.io.File; import java.util.Objects; /** + * 可发送的或从服务器接收的消息. + * 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 {@linkplain PlainText 纯文本}, {@linkplain Image 图片} 等. + * * @author Him188moe + * @see Contact#sendMessage(Message) 发送这个消息 + * @see MessageKt 若你使用 kotlin, 请查看针对 kotlin 的优化实现 */ public abstract class Message { @Override @@ -38,7 +45,7 @@ public abstract class Message { return new MessageChain(this, Objects.requireNonNull(tail)); } - public Message concat(String tail) { + public final Message concat(String tail) { return concat(new PlainText(tail)); } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/MessageKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/message/MessageKt.kt index 6ae5be4b9..0257cbf57 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/MessageKt.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/MessageKt.kt @@ -1,3 +1,5 @@ +@file:JvmName("MessageKt") + package net.mamoe.mirai.message import net.mamoe.mirai.message.defaults.PlainText @@ -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) +/** + * 连接 [String] 与 [Message] + */ infix fun String.concat(another: Message): Message = PlainText(this).concat(another) \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/At.java b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/At.java index 088a98a30..dbae4f45a 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/At.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/At.java @@ -7,25 +7,28 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; /** + * At 一个人的消息. + * * @author Him188moe */ public final class At extends Message { - private final int target; + private final long target; public At(@NotNull QQ target) { this(Objects.requireNonNull(target).getNumber()); } - public At(int target) { + public At(long target) { this.target = target; } - public int getTarget() { + public long getTarget() { return target; } @Override public String toString() { - return null; + // TODO: 2019/9/4 At.toString + throw new UnsupportedOperationException(); } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.java b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.java index b1708fd11..3311de4e5 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/Face.java @@ -4,6 +4,8 @@ import net.mamoe.mirai.message.FaceID; import net.mamoe.mirai.message.Message; /** + * QQ 自带表情 + * * @author Him188moe */ public final class Face extends Message { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java index d0fe6465f..9adfed317 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/message/defaults/MessageChain.java @@ -29,6 +29,9 @@ public final class MessageChain extends Message { list.add(message); } + /** + * @return An unmodifiable list + */ public List<Message> toList() { return List.copyOf(list); } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt index 258aff770..dea09bf68 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt @@ -7,70 +7,84 @@ import java.util.stream.Collectors /** * @author Him188moe */ -interface Protocol { - companion object { - val SERVER_IP: ArrayList<String> = object : ArrayList<String>() { - init { - add("183.60.56.29") +object Protocol { + val SERVER_IP: List<String> = object : ArrayList<String>() { + init { + add("183.60.56.29") - arrayOf( - "sz2.tencent.com", - "sz3.tencent.com", - "sz4.tencent.com", - "sz5.tencent.com", - "sz6.tencent.com", - "sz8.tencent.com", - "sz9.tencent.com" - ).forEach { this.add(InetAddress.getByName(it).hostAddress) } + arrayOf( + "sz3.tencent.com", + "sz4.tencent.com", + "sz5.tencent.com", + "sz6.tencent.com", + "sz8.tencent.com", + "sz9.tencent.com", + "sz2.tencent.com" + ).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 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" - 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 key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94" + const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A" - 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" + /** + * 0836_622_fix2 + */ + 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" - const val encryptKey = "“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”" + private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf() - 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"; - const val _0836key1 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA" - - private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf() - - @ExperimentalUnsignedTypes - fun hexToBytes(hex: String): ByteArray { - hex.hashCode().let { id -> - if (hexToByteArrayCacheMap.containsKey(id)) { - return hexToByteArrayCacheMap[id]!!.clone() - } else { - hexToUBytes(hex).toByteArray().let { - hexToByteArrayCacheMap[id] = it.clone(); - return it - } + @ExperimentalUnsignedTypes + fun hexToBytes(hex: String): ByteArray { + hex.hashCode().let { id -> + if (hexToByteArrayCacheMap.containsKey(id)) { + return hexToByteArrayCacheMap[id]!!.clone() + } else { + hexToUBytes(hex).toByteArray().let { + hexToByteArrayCacheMap[id] = it.clone(); + return it } } } - - @ExperimentalUnsignedTypes - fun hexToUBytes(hex: String): UByteArray = Arrays - .stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) - .map { value -> value.trim { it <= ' ' } } - .map { s -> s.toUByte(16) } - .collect(Collectors.toList()).toUByteArray() - } + + @ExperimentalUnsignedTypes + fun hexToUBytes(hex: String): UByteArray = Arrays + .stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) + .map { value -> value.trim { it <= ' ' } } + .map { s -> s.toUByte(16) } + .collect(Collectors.toList()).toUByteArray() + } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt index 7d28047fc..bd533f20e 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt @@ -1,306 +1,544 @@ +@file:JvmMultifileClass +@file:JvmName("RobotNetworkHandler") package net.mamoe.mirai.network -import net.mamoe.mirai.MiraiServer import net.mamoe.mirai.Robot import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.event.events.network.BeforePacketSendEvent +import net.mamoe.mirai.event.events.network.PacketSentEvent +import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent import net.mamoe.mirai.event.events.qq.FriendMessageEvent import net.mamoe.mirai.event.events.robot.RobotLoginSucceedEvent +import net.mamoe.mirai.event.hookWhile +import net.mamoe.mirai.message.Message +import net.mamoe.mirai.network.RobotNetworkHandler.* import 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.message.ClientSendGroupMessagePacket -import net.mamoe.mirai.network.packet.message.ServerSendFriendMessageResponsePacket -import net.mamoe.mirai.network.packet.message.ServerSendGroupMessageResponsePacket -import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacket -import net.mamoe.mirai.network.packet.verification.ServerVerificationCodePacketEncrypted import net.mamoe.mirai.task.MiraiThreadPool import net.mamoe.mirai.utils.* -import java.io.ByteArrayInputStream -import java.io.FileOutputStream +import java.io.Closeable import java.net.DatagramPacket import java.net.DatagramSocket import java.net.InetSocketAddress +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ScheduledFuture import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException +import kotlin.reflect.KClass /** + * Mirai 的网络处理器, 它处理所有数据包([Packet])的发送和接收. + * [RobotNetworkHandler] 是全程异步和线程安全的. + * + * [RobotNetworkHandler] 由 2 个模块构成: + * - [SocketHandler]: 处理数据包底层的发送([ByteArray]) + * - [PacketHandler]: 制作 [Packet] 并传递给 [SocketHandler] 继续处理; 分析来自服务器的数据包并处理 + * + * 其中, [PacketHandler] 由 4 个子模块构成: + * - [DebugHandler] 输出 [Packet.toString] + * - [LoginHandler] 处理 touch/login/verification code 相关 + * - [MessageHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket]) + * - [ActionHandler] 处理动作相关(踢人/加入群/好友列表等) + * * A RobotNetworkHandler is used to connect with Tencent servers. * * @author Him188moe */ -@ExperimentalUnsignedTypes -class RobotNetworkHandler(val robot: Robot, val number: Int, private val password: String) { +@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code +class RobotNetworkHandler(private val robot: Robot) : Closeable { + private val socketHandler: SocketHandler = SocketHandler() - private var sequence: Int = 0 + val debugHandler = DebugHandler() + val loginHandler = LoginHandler() + val messageHandler = MessageHandler() + val actionHandler = ActionHandler() - var socket: DatagramSocket = DatagramSocket((15314 + Math.random() * 100).toInt()) + private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = linkedMapOf( + DebugHandler::class to debugHandler, + LoginHandler::class to loginHandler, + MessageHandler::class to messageHandler, + ActionHandler::class to actionHandler + ) - var serverIP: String = "" - set(value) { - serverAddress = InetSocketAddress(value, 8000) - field = value + /** + * Not async + */ + @ExperimentalUnsignedTypes + fun sendPacket(packet: ClientPacket) { + socketHandler.sendPacket(packet) + } - socket.close() - socket = DatagramSocket((15314 + Math.random() * 100).toInt()) - socket.connect(this.serverAddress) - val zeroByte: Byte = 0 + override fun close() { + this.packetHandlers.values.forEach { + it.close() + } + this.socketHandler.close() + } + + + //private | internal + + internal fun tryLogin(): CompletableFuture<LoginState> = this.tryLogin(500, TimeUnit.MILLISECONDS) + + + /** + * 仅当 [LoginState] 非 [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook]. + * 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCEED] 调用 [loginHandler] + * + * @param connectingTimeout 连接每个服务器的 timeout + */ + internal fun tryLogin(connectingTimeout: Long, unit: TimeUnit = TimeUnit.MILLISECONDS): CompletableFuture<LoginState> { + val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP) + val future = CompletableFuture<LoginState>() + + fun login() { + val ip = ipQueue.poll() + if (ip != null) { + // val future = this@RobotNetworkHandler.socketHandler.touch(ip) + + this@RobotNetworkHandler.socketHandler.touch(ip).runCatching { + this@runCatching.get(connectingTimeout, unit).let { state -> + if (state == LoginState.UNKNOWN) { + login() + } else { + future.complete(state) + } + } + }.onFailure { + when (it) { + is TimeoutException -> login() + else -> throw it + } + } + } else { + future.complete(LoginState.UNKNOWN)//所有服务器均返回 UNKNOWN + } + } + login() + return future + } + + /** + * 分配收到的数据包 + */ + @ExperimentalUnsignedTypes + internal fun distributePacket(packet: ServerPacket) { + packet.decode() + if (ServerPacketReceivedEvent(packet).broadcast().isCancelled) { + debugHandler.onPacketReceived(packet) + return + } + this.packetHandlers.values.forEach { + it.onPacketReceived(packet) + } + } + + + private inner class SocketHandler : Closeable { + private var socket: DatagramSocket? = null + + internal var serverIP: String = "" + set(value) { + field = value + + restartSocket() + } + + internal var loginFuture: CompletableFuture<LoginState>? = null + + private fun restartSocket() { + socket?.close() + socket = DatagramSocket(0) + socket!!.connect(InetSocketAddress(serverIP, 8000)) Thread { - while (true) { - val dp1 = DatagramPacket(ByteArray(2048), 2048) - try { - socket.receive(dp1) - } catch (e: Exception) { - if (e.message == "socket closed") { - return@Thread - } - } - MiraiThreadPool.getInstance().submit { - var i = dp1.data.size - 1; - while (dp1.data[i] == zeroByte) { - --i - } - try { - onPacketReceived(ServerPacket.ofByteArray(dp1.data.copyOfRange(0, i + 1))) - } catch (e: Exception) { - e.printStackTrace() - } - } + while (socket!!.isConnected) { + val packet = DatagramPacket(ByteArray(2048), 2048) + kotlin.runCatching { socket!!.receive(packet) } + .onSuccess { + MiraiThreadPool.getInstance().submit { + try { + distributePacket(ServerPacket.ofByteArray(packet.data.removeZeroTail())) + } catch (e: Exception) { + e.printStackTrace() + } + } + }.onFailure { + if (it.message == "Socket closed" || it.message == "socket closed") { + return@Thread + } + it.printStackTrace() + } + } }.start() } - private lateinit var serverAddress: InetSocketAddress + /** + * Start network and touch the server + */ + internal fun touch(serverAddress: String): CompletableFuture<LoginState> { + MiraiLogger.info("Connecting server: $serverAddress") + this.loginFuture = CompletableFuture() - private lateinit var token00BA: ByteArray //这些数据全部是login用的 - private lateinit var token0825: ByteArray - private var loginTime: Int = 0 - private lateinit var loginIP: String - private var tgtgtKey: ByteArray? = null - private var tlv0105: ByteArray - private lateinit var _0828_rec_decr_key: ByteArray + socketHandler.serverIP = serverAddress + sendPacket(ClientTouchPacket(robot.account.qqNumber, socketHandler.serverIP)) + + return this.loginFuture!! + } + + /** + * Not async + */ + @ExperimentalUnsignedTypes + internal fun sendPacket(packet: ClientPacket) { + checkNotNull(socket) { "socket closed" } + + try { + packet.encodePacket() + + if (BeforePacketSendEvent(packet).broadcast().isCancelled) { + return + } + + val data = packet.toByteArray() + socket!!.send(DatagramPacket(data, data.size)) + MiraiLogger info "Packet sent: $packet" + + PacketSentEvent(packet).broadcast() + } catch (e: Throwable) { + e.printStackTrace() + } + } + + @Suppress("UNCHECKED_CAST") + private fun <P : ServerPacket> waitForPacket(packetClass: KClass<P>, timeoutMillis: Long, timeout: () -> Unit) { + var got = false + ServerPacketReceivedEvent::class.hookWhile { + if (packetClass.isInstance(it.packet)) { + got = true + true + } else { + false + } + } + + MiraiThreadPool.getInstance().submit { + val startingTime = System.currentTimeMillis() + while (!got) { + if (System.currentTimeMillis() - startingTime > timeoutMillis) { + timeout.invoke() + return@submit + } + Thread.sleep(10) + } + } + } + + override fun close() { + this.socket?.close() + if (this.loginFuture != null) { + if (!this.loginFuture!!.isDone) { + this.loginFuture!!.cancel(true) + } + this.loginFuture = null + } + } + } - private lateinit var sessionKey: ByteArray//这两个是登录成功后得到的 - private lateinit var sKey: String + private lateinit var sessionKey: ByteArray + + abstract inner class PacketHandler : Closeable { + abstract fun onPacketReceived(packet: ServerPacket) + + override fun close() { + + } + } /** - * Used to access web API(for friends list etc.) + * Kind of [PacketHandler] that prints all packets received in the format of hex byte array. */ - private lateinit var cookies: String - private var gtk: Int = 0 - private var ignoreMessage: Boolean = false + inner class DebugHandler : PacketHandler() { + override fun onPacketReceived(packet: ServerPacket) { + MiraiLogger info "Packet received: $packet" + if (packet is ServerEventPacket) { + sendPacket(ClientMessageResponsePacket(robot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity)) + } + } + } - init { - tlv0105 = lazyEncode { + /** + * 处理登录过程 + */ + inner class LoginHandler : PacketHandler() { + private lateinit var token00BA: ByteArray + private lateinit var token0825: ByteArray + private var loginTime: Int = 0 + private lateinit var loginIP: String + private var tgtgtKey: ByteArray? = null + + private var tlv0105: ByteArray = lazyEncode { it.writeHex("01 05 00 30") it.writeHex("00 01 01 02 00 14 01 01 00 10") it.writeRandom(16) it.writeHex("00 14 01 02 00 10") it.writeRandom(16) } - } + + /** + * 0828_decr_key + */ + private lateinit var sessionResponseDecryptionKey: ByteArray + + private var verificationCodeSequence: Int = 0//这两个验证码使用 + private var verificationCodeCache: ByteArray? = null//每次包只发一部分验证码来 + private var verificationCodeCacheCount: Int = 1// + private lateinit var verificationToken: ByteArray - @ExperimentalUnsignedTypes - private var md5_32: ByteArray = getRandomKey(32) + private var heartbeatFuture: ScheduledFuture<*>? = null + private var sKeyRefresherFuture: ScheduledFuture<*>? = null - - @ExperimentalUnsignedTypes - internal fun onPacketReceived(packet: ServerPacket) { - packet.decode() - MiraiLogger info "Packet received: $packet" - if (packet is ServerEventPacket) { - sendPacket(ClientMessageResponsePacket(this.number, packet.packetId, this.sessionKey, packet.eventIdentity)) - } - when (packet) { - is ServerTouchResponsePacket -> { - if (packet.serverIP != null) {//redirection - serverIP = packet.serverIP!! - //connect(packet.serverIP!!) - sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, number)) - } else {//password submission - this.loginIP = packet.loginIP - this.loginTime = packet.loginTime - this.token0825 = packet.token0825 - this.tgtgtKey = packet.tgtgtKey - sendPacket(ClientPasswordSubmissionPacket(this.number, this.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825)) - } - } - - is ServerLoginResponseFailedPacket -> { - MiraiLogger error "Login failed: " + packet.state.toString() - return - } - - is ServerLoginResponseVerificationCodePacket -> { - //[token00BA]来源之一: 验证码 - this.token00BA = packet.token00BA - - with(MiraiServer.getInstance().parentFolder + "verifyCode.png") { - ByteArrayInputStream(packet.verifyCode).transferTo(FileOutputStream(this)) - println("验证码已写入到 " + this.path) + override fun onPacketReceived(packet: ServerPacket) { + when (packet) { + is ServerTouchResponsePacket -> { + if (packet.serverIP != null) {//redirection + socketHandler.serverIP = packet.serverIP!! + //connect(packet.serverIP!!) + sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, robot.account.qqNumber)) + } else {//password submission + this.loginIP = packet.loginIP + this.loginTime = packet.loginTime + this.token0825 = packet.token0825 + this.tgtgtKey = packet.tgtgtKey + sendPacket(ClientPasswordSubmissionPacket(robot.account.qqNumber, robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825)) + } } - if (packet.unknownBoolean != null && packet.unknownBoolean!!) { - this.sequence = 1 - sendPacket(ClientLoginVerificationCodePacket(this.number, this.token0825, this.sequence, this.token00BA)) - } - - } - - is ServerLoginResponseSuccessPacket -> { - this._0828_rec_decr_key = packet._0828_rec_decr_key - sendPacket(ClientSessionRequestPacket(this.number, this.serverIP, this.loginIP, this.md5_32, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) - } - - //是ClientPasswordSubmissionPacket之后服务器回复的 - is ServerLoginResponseResendPacket -> { - if (packet.tokenUnknown != null) { - //this.token00BA = packet.token00BA!! - //println("token00BA changed!!! to " + token00BA.toUByteArray()) - } - if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) { - this.tgtgtKey = packet.tgtgtKey - sendPacket(ClientLoginResendPacket3104( - this.number, - this.password, - this.loginTime, - this.loginIP, - this.tgtgtKey!!, - this.token0825, - when (packet.tokenUnknown != null) { - true -> packet.tokenUnknown!! - false -> this.token00BA - }, - packet._0836_tlv0006_encr - )) - } else { - sendPacket(ClientLoginResendPacket3106( - this.number, - this.password, - this.loginTime, - this.loginIP, - this.tgtgtKey!!, - this.token0825, - when (packet.tokenUnknown != null) { - true -> packet.tokenUnknown!! - false -> this.token00BA - }, - packet._0836_tlv0006_encr - )) - } - } - - is ServerVerificationCodePacket -> { - this.sequence++ - - } - - is ServerSessionKeyResponsePacket -> { - this.sessionKey = packet.sessionKey - MiraiThreadPool.getInstance().scheduleWithFixedDelay({ - sendPacket(ClientHeartbeatPacket(this.number, this.sessionKey)) - }, 90000, 90000, TimeUnit.MILLISECONDS) - RobotLoginSucceedEvent(robot).broadcast() - - MiraiThreadPool.getInstance().schedule({ - ignoreMessage = false - }, 2, TimeUnit.SECONDS) - - this.tlv0105 = packet.tlv0105 - sendPacket(ClientLoginStatusPacket(this.number, this.sessionKey, ClientLoginStatus.ONLINE)) - } - - is ServerLoginSuccessPacket -> { - sendPacket(ClientSKeyRequestPacket(this.number, this.sessionKey)) - } - - is ServerSKeyResponsePacket -> { - this.sKey = packet.sKey - this.cookies = "uin=o" + this.number + ";skey=" + this.sKey + ";" - - MiraiThreadPool.getInstance().scheduleWithFixedDelay({ - sendPacket(ClientSKeyRefreshmentRequestPacket(this.number, this.sessionKey)) - }, 1800000, 1800000, TimeUnit.MILLISECONDS) - - this.gtk = getGTK(sKey) - sendPacket(ClientAccountInfoRequestPacket(this.number, this.sessionKey)) - } - - is ServerHeartbeatResponsePacket -> { - - } - - is ServerAccountInfoResponsePacket -> { - - } - - - is ServerFriendMessageEventPacket -> { - if (ignoreMessage) { + is ServerLoginResponseFailedPacket -> { + socketHandler.loginFuture!!.complete(packet.loginState) return } - FriendMessageEvent(this.robot, this.robot.getQQ(packet.qq), packet.message) - } + is ServerLoginResponseVerificationCodeInitPacket -> { + //[token00BA]来源之一: 验证码 + this.token00BA = packet.token00BA + this.verificationCodeCache = packet.verifyCodePart1 - is ServerGroupMessageEventPacket -> { - //group message - if (packet.message == "牛逼") { - sendPacket(ClientSendGroupMessagePacket(Group.groupNumberToId(packet.groupNumber), this.number, this.sessionKey, "牛逼!")) + if (packet.unknownBoolean != null && packet.unknownBoolean!!) { + this.verificationCodeSequence = 1 + sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA)) + } } - //todo - //GroupMessageEvent(this.robot, this.robot.getGroup(packet.groupNumber), this.robot.getQQ(packet.qq), packet.message) - } + is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么 + this.tgtgtKey = packet.tgtgtKeyUpdate + this.token00BA = packet.token00BA + sendPacket(ClientLoginResendPacket3105(robot.account.qqNumber, robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA)) + } - is UnknownServerEventPacket -> { - //unknown message event - } + is ServerVerificationCodeTransmissionPacket -> { + this.verificationCodeSequence++ + this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN - is UnknownServerPacket -> { + this.verificationToken = packet.verificationToken + this.verificationCodeCacheCount++ - } - - is ServerGroupUploadFileEventPacket -> { - - } - - is ServerMessageEventPacketRaw -> onPacketReceived(packet.analyze()) - - is ServerVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt(this.token00BA)) - is ServerLoginResponseVerificationCodePacketEncrypted -> onPacketReceived(packet.decrypt()) - is ServerLoginResponseResendPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) - is ServerLoginResponseSuccessPacketEncrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) - is ServerSessionKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this._0828_rec_decr_key)) - is ServerTouchResponsePacketEncrypted -> onPacketReceived(packet.decrypt()) - is ServerSKeyResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey)) - is ServerAccountInfoResponsePacketEncrypted -> onPacketReceived(packet.decrypt(this.sessionKey)) - is ServerMessageEventPacketRawEncoded -> onPacketReceived(packet.decrypt(this.sessionKey)) + this.token00BA = packet.token00BA - is ServerSendFriendMessageResponsePacket, - is ServerSendGroupMessageResponsePacket -> { + //todo 看易语言 count 和 sequence 是怎样变化的 - } + if (packet.transmissionCompleted) { + this.verificationCodeCache + TODO("验证码好了") + } else { + sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA)) + } + } - else -> throw IllegalArgumentException(packet.toString()) - } + is ServerLoginResponseSuccessPacket -> { + this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey + sendPacket(ClientSessionRequestPacket(robot.account.qqNumber, socketHandler.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) + } - } + //是ClientPasswordSubmissionPacket之后服务器回复的 + is ServerLoginResponseResendPacket -> { + //if (packet.tokenUnknown != null) { + //this.token00BA = packet.token00BA!! + //println("token00BA changed!!! to " + token00BA.toUByteArray()) + //} + if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) { + this.tgtgtKey = packet.tgtgtKey + sendPacket(ClientLoginResendPacket3104( + robot.account.qqNumber, + robot.account.password, + this.loginTime, + this.loginIP, + this.tgtgtKey!!, + this.token0825, + when (packet.tokenUnknown != null) { + true -> packet.tokenUnknown!! + false -> this.token00BA + }, + packet._0836_tlv0006_encr + )) + } else { + sendPacket(ClientLoginResendPacket3106( + robot.account.qqNumber, + robot.account.password, + this.loginTime, + this.loginIP, + this.tgtgtKey!!, + this.token0825, + when (packet.tokenUnknown != null) { + true -> packet.tokenUnknown!! + false -> this.token00BA + }, + packet._0836_tlv0006_encr + )) + } + } - @ExperimentalUnsignedTypes - fun sendPacket(packet: ClientPacket) { - MiraiThreadPool.getInstance().submit { - try { - packet.encode() - packet.writeHex(Protocol.tail) + is ServerSessionKeyResponsePacket -> { + sessionKey = packet.sessionKey + heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({ + sendPacket(ClientHeartbeatPacket(robot.account.qqNumber, sessionKey)) + }, 90000, 90000, TimeUnit.MILLISECONDS) - val data = packet.toByteArray() - socket.send(DatagramPacket(data, data.size)) - MiraiLogger info "Packet sent: $packet" - } catch (e: Throwable) { - e.printStackTrace() + RobotLoginSucceedEvent(robot).broadcast() + + //登录成功后会收到大量上次的消息, 忽略掉 + MiraiThreadPool.getInstance().schedule({ + (packetHandlers[MessageHandler::class] as MessageHandler).ignoreMessage = false + }, 2, TimeUnit.SECONDS) + + this.tlv0105 = packet.tlv0105 + sendPacket(ClientChangeOnlineStatusPacket(robot.account.qqNumber, sessionKey, ClientLoginStatus.ONLINE)) + } + + is ServerLoginSuccessPacket -> { + socketHandler.loginFuture!!.complete(LoginState.SUCCEED) + sendPacket(ClientSKeyRequestPacket(robot.account.qqNumber, sessionKey)) + } + + is ServerSKeyResponsePacket -> { + val actionHandler = packetHandlers[ActionHandler::class] as ActionHandler + actionHandler.sKey = packet.sKey + actionHandler.cookies = "uin=o" + robot.account.qqNumber + ";skey=" + actionHandler.sKey + ";" + + sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({ + sendPacket(ClientSKeyRefreshmentRequestPacket(robot.account.qqNumber, sessionKey)) + }, 1800000, 1800000, TimeUnit.MILLISECONDS) + + actionHandler.gtk = getGTK(actionHandler.sKey) + sendPacket(ClientAccountInfoRequestPacket(robot.account.qqNumber, sessionKey)) + } + + is ServerEventPacket.Raw -> distributePacket(packet.distribute()) + + is ServerVerificationCodePacket.Encrypted -> distributePacket(packet.decrypt()) + is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> distributePacket(packet.decrypt()) + is ServerLoginResponseResendPacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey!!)) + is ServerLoginResponseSuccessPacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey!!)) + is ServerSessionKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(this.sessionResponseDecryptionKey)) + is ServerTouchResponsePacket.Encrypted -> distributePacket(packet.decrypt()) + is ServerSKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey)) + is ServerAccountInfoResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey)) + is ServerEventPacket.Raw.Encrypted -> distributePacket(packet.decrypt(sessionKey)) + + + is ServerAccountInfoResponsePacket, + is ServerHeartbeatResponsePacket, + is UnknownServerPacket -> { + //ignored + } + else -> { + + } } } + + override fun close() { + this.verificationCodeCache = null + this.tgtgtKey = null + + this.heartbeatFuture?.cancel(true) + this.sKeyRefresherFuture?.cancel(true) + + this.heartbeatFuture = null + this.sKeyRefresherFuture = null + } } -} + + /** + * 处理消息事件, 承担消息发送任务. + */ + inner class MessageHandler : PacketHandler() { + internal var ignoreMessage: Boolean = false + + override fun onPacketReceived(packet: ServerPacket) { + when (packet) { + is ServerGroupUploadFileEventPacket -> { + //todo + } + + is ServerFriendMessageEventPacket -> { + if (ignoreMessage) { + return + } + + FriendMessageEvent(robot, robot.contacts.getQQ(packet.qq), packet.message) + } + + is ServerGroupMessageEventPacket -> { + //todo message chain + //GroupMessageEvent(this.robot, robot.contacts.getGroupByNumber(packet.groupNumber), robot.contacts.getQQ(packet.qq), packet.message) + } + + is UnknownServerEventPacket, + is ServerSendFriendMessageResponsePacket, + is ServerSendGroupMessageResponsePacket -> { + //ignored + } + else -> { + //ignored + } + } + } + + fun sendFriendMessage(qq: QQ, message: Message) { + TODO() + //sendPacket(ClientSendFriendMessagePacket(robot.account.qqNumber, qq.number, sessionKey, message)) + } + + fun sendGroupMessage(group: Group, message: Message): Unit { + TODO() + //sendPacket(ClientSendGroupMessagePacket(group.groupId, robot.account.qqNumber, sessionKey, message)) + } + } + + /** + * 动作: 获取好友列表, 点赞, 踢人等. + * 处理动作事件, 承担动作任务. + */ + inner class ActionHandler : PacketHandler() { + internal lateinit var cookies: String + internal lateinit var sKey: String + internal var gtk: Int = 0 + + override fun onPacketReceived(packet: ServerPacket) { + + } + + override fun close() { + + } + } +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/AccountInfo.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/AccountInfo.kt index 85ffc41dc..73e1e38bc 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/AccountInfo.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/AccountInfo.kt @@ -1,7 +1,7 @@ package net.mamoe.mirai.network.packet import net.mamoe.mirai.network.Protocol -import net.mamoe.mirai.utils.TEACryptor +import net.mamoe.mirai.utils.TEA import java.io.DataInputStream /** @@ -12,13 +12,14 @@ import java.io.DataInputStream @ExperimentalUnsignedTypes @PacketId("00 5C") class ClientAccountInfoRequestPacket( - private val qq: Int, + private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { override fun encode() { this.writeRandom(2)//part of packet id + this.writeQQ(qq) - this.writeHex(Protocol._fixVer) + this.writeHex(Protocol.fixVer2) this.encryptAndWrite(sessionKey) { it.writeByte(0x88) it.writeQQ(qq) @@ -27,6 +28,7 @@ class ClientAccountInfoRequestPacket( } } +@PacketId("00 5C") class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(input) { //等级 //升级剩余活跃天数 @@ -34,16 +36,13 @@ class ServerAccountInfoResponsePacket(input: DataInputStream) : ServerPacket(inp override fun decode() { } -} -class ServerAccountInfoResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { - override fun decode() { - - } - - fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket { - this.input goto 14 - val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } - return ServerAccountInfoResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream()); + @PacketId("00 5C") + class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { + fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket { + 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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt index 29682026d..1f4891942 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt @@ -2,7 +2,6 @@ package net.mamoe.mirai.network.packet import lombok.Getter import net.mamoe.mirai.network.Protocol -import net.mamoe.mirai.util.TestedSuccessfully import net.mamoe.mirai.utils.* import java.io.DataOutputStream import java.io.IOException @@ -15,11 +14,13 @@ import java.security.MessageDigest @ExperimentalUnsignedTypes abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { @Getter - val packageId: String + val idHex: String + + var encoded: Boolean = false init { val annotation = this.javaClass.getAnnotation(PacketId::class.java) - packageId = annotation.value + idHex = annotation.value try { this.writeHex(Protocol.head) @@ -33,7 +34,7 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { @Throws(IOException::class) fun writePacketId() { - this.writeHex(this@ClientPacket.packageId) + this.writeHex(this@ClientPacket.idHex) } /** @@ -43,11 +44,19 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet { * Before sending the packet, a [tail][Protocol.tail] will be added. */ @Throws(IOException::class) - abstract fun encode() + protected abstract fun encode() + + fun encodePacket() { + if (encoded) { + return + } + encode() + writeHex(Protocol.tail) + } @Throws(IOException::class) fun encodeToByteArray(): ByteArray { - encode() + encodePacket() return toByteArray() } @@ -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) { - 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)) } 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() })) } @ExperimentalUnsignedTypes @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 { - it.writeHex("12 12 12 12")//it.writeRandom(4) todo + it.writeRandom(4) it.writeHex("00 02") it.writeQQ(qq) - it.writeHex(Protocol._0825data2) + it.writeHex(Protocol.constantData1) it.writeHex("00 00 01") - val md5_1 = md5(password); - val md5_2 = md5(md5_1 + "00 00 00 00".hexToBytes() + qq.toByteArray()) - println(md5_1.toUByteArray().toUHexString()) - println(md5_2.toUByteArray().toUHexString()) - it.write(md5_1) + val firstMD5 = md5(password) + val secondMD5 = md5(firstMD5 + "00 00 00 00".hexToBytes() + qq.toUInt().toByteArray()) + it.write(firstMD5) it.writeInt(loginTime) - it.writeByte(0); + it.writeByte(0) it.writeZero(4 * 3) it.writeIP(loginIP) it.writeZero(8) it.writeHex("00 10") it.writeHex("15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B") it.write(tgtgtKey) - println() - println(it.toByteArray().toUHexString()) - this.write(TEACryptor.encrypt(it.toByteArray(), md5_2)) + this.write(TEA.encrypt(it.toByteArray(), secondMD5)) } } -fun main() { - -} - /* @ExperimentalUnsignedTypes fun main() { @@ -164,7 +152,7 @@ fun main() { @ExperimentalUnsignedTypes @TestedSuccessfully -fun DataOutputStream.writeCRC32() = writeCRC32(getRandomKey(16)) +fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16)) @ExperimentalUnsignedTypes @@ -193,6 +181,17 @@ fun Int.toByteArray(): ByteArray = byteArrayOf( (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 */ @@ -204,7 +203,7 @@ fun Int.toLByteArray(): ByteArray = byteArrayOf( ) @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()) @@ -225,7 +224,14 @@ fun DataOutputStream.writeRandom(length: Int) { } } +@ExperimentalUnsignedTypes @Throws(IOException::class) -fun DataOutputStream.writeQQ(qq: Int) { - this.writeInt(qq) +fun DataOutputStream.writeQQ(qq: Long) { + this.write(qq.toUInt().toByteArray()) +} + +@ExperimentalUnsignedTypes +@Throws(IOException::class) +fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) { + this.write(groupIdOrGroupNumber.toUInt().toByteArray()) } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt index 8a40a441d..9fcd36304 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt @@ -10,7 +10,7 @@ import java.io.IOException @ExperimentalUnsignedTypes @PacketId("00 58") class ClientHeartbeatPacket( - private val qq: Int, + private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { @Throws(IOException::class) @@ -24,8 +24,4 @@ class ClientHeartbeatPacket( } } -class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input) { - override fun decode() { - - } -} \ No newline at end of file +class ServerHeartbeatResponsePacket(input: DataInputStream) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/MessageEvent.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/MessageEvent.kt deleted file mode 100644 index 2657662ee..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/MessageEvent.kt +++ /dev/null @@ -1,84 +0,0 @@ -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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt index c5a1d4905..4ffb53ca3 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt @@ -1,7 +1,7 @@ package net.mamoe.mirai.network.packet import net.mamoe.mirai.network.Protocol -import net.mamoe.mirai.utils.TEACryptor +import net.mamoe.mirai.utils.TEA import java.io.DataInputStream @@ -11,14 +11,14 @@ import java.io.DataInputStream @ExperimentalUnsignedTypes @PacketId("00 1D") class ClientSKeyRequestPacket( - private val qq: Int, + private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { override fun encode() { this.writeRandom(2)//part of packet id this.writeQQ(qq) - this.writeHex(Protocol._fixVer) + this.writeHex(Protocol.fixVer2) 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") } @@ -31,7 +31,7 @@ class ClientSKeyRequestPacket( @PacketId("00 1D") @ExperimentalUnsignedTypes class ClientSKeyRefreshmentRequestPacket( - private val qq: Int, + private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { override fun encode() { @@ -52,19 +52,13 @@ class ServerSKeyResponsePacket(input: DataInputStream) : ServerPacket(input) { override fun decode() { 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 { - this.input goto 14 - val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } - return ServerSKeyResponsePacket(TEACryptor.decrypt(data, sessionKey).dataInputStream()); + class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { + fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket { + this.input goto 14 + 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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt index 8d73cc493..943e14368 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt @@ -2,28 +2,65 @@ package net.mamoe.mirai.network.packet import net.mamoe.mirai.message.defaults.MessageChain 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 java.io.ByteArrayOutputStream import java.io.DataInputStream import java.util.zip.GZIPInputStream /** + * Packet id: `00 CE` or `00 17` + * * @author Him188moe */ 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) - override fun decode() { + 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) + "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 客户端上线 */ 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) @@ -31,7 +68,7 @@ class ServerAndroidOfflineEventPacket(input: DataInputStream, packetId: ByteArra * 群文件上传 */ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - lateinit var xmlMessage: String + private lateinit var xmlMessage: String override fun decode() { xmlMessage = String(this.input.goto(65).readNBytes(this.input.goto(60).readShort().toInt())) @@ -39,8 +76,8 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr } class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - var groupNumber: Int = 0 - var qq: Int = 0 + var groupNumber: Long = 0 + var qq: Long = 0 lateinit var message: String lateinit var messageType: MessageType @@ -58,9 +95,10 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, OTHER, } + @ExperimentalUnsignedTypes override fun decode() { - groupNumber = this.input.goto(51).readInt() - qq = this.input.goto(56).readInt() + groupNumber = this.input.goto(51).readInt().toLong() + qq = this.input.goto(56).readLong().toUInt().toLong() val fontLength = this.input.goto(108).readShort() //println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00 @@ -76,7 +114,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, 25 -> MessageType.ANONYMOUS else -> { - println("id=$id") + MiraiLogger debug ("ServerGroupMessageEventPacket id=$id") MessageType.OTHER } } @@ -142,10 +180,9 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, } class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - var qq: Int = 0 + var qq: Long = 0 lateinit var message: MessageChain - @ExperimentalUnsignedTypes override fun decode() { //start at Sep1.0:27 @@ -153,7 +190,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray println(input.readAllBytes().toUHexString()) input.goto(0) - qq = input.readIntAt(0) + qq = input.readIntAt(0).toLong() val msgLength = input.readShortAt(22) val fontLength = input.readShortAt(93 + msgLength) val offset = msgLength + fontLength @@ -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 class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) { - var qq: Int = 0 + var qq: Long = 0 lateinit var message: String diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt index c3e0e2e3a..4e54a750b 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt @@ -1,12 +1,9 @@ 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.message.ServerSendFriendMessageResponsePacket -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 net.mamoe.mirai.utils.* import java.io.DataInputStream /** @@ -14,7 +11,9 @@ import java.io.DataInputStream */ abstract class ServerPacket(val input: DataInputStream) : Packet { - abstract fun decode() + open fun decode() { + + } companion object { @@ -28,55 +27,56 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { return when (val idHex = stream.readInt().toHexString(" ")) { - "08 25 31 01" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, stream) - - "08 25 31 02" -> ServerTouchResponsePacketEncrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, 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 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> { 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` 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) { - return ServerLoginResponseSuccessPacketEncrypted(stream) + return ServerLoginResponseSuccessPacket.Encrypted(stream) } return ServerLoginResponseFailedPacket(when (bytes.size) { - 319 -> ServerLoginResponseFailedPacket.State.WRONG_PASSWORD - 135 -> ServerLoginResponseFailedPacket.State.RETYPE_PASSWORD - 279 -> ServerLoginResponseFailedPacket.State.BLOCKED - 263 -> ServerLoginResponseFailedPacket.State.UNKNOWN_QQ_NUMBER - 551, 487 -> ServerLoginResponseFailedPacket.State.DEVICE_LOCK - 359 -> ServerLoginResponseFailedPacket.State.TAKEN_BACK + 319, 135 -> LoginState.WRONG_PASSWORD + //135 -> LoginState.RETYPE_PASSWORD + 279 -> LoginState.BLOCKED + 263 -> LoginState.UNKNOWN_QQ_NUMBER + 551, 487 -> LoginState.DEVICE_LOCK + 359 -> LoginState.TAKEN_BACK + else -> LoginState.UNKNOWN + /* //unknown - 63 -> throw IllegalArgumentException(bytes.size.toString() + " (Already logged in)")//可能是已经完成登录, 服务器拒绝第二次登录 - 351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data)")//包数据有误 + 63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)") + 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) } - "08 28 04 34" -> ServerSessionKeyResponsePacketEncrypted(stream) + "08 28 04 34" -> ServerSessionKeyResponsePacket.Encrypted(stream) else -> when (idHex.substring(0, 5)) { "00 EC" -> ServerLoginSuccessPacket(stream) - "00 1D" -> ServerSKeyResponsePacketEncrypted(stream) - "00 5C" -> ServerAccountInfoResponsePacketEncrypted(stream) + "00 1D" -> ServerSKeyResponsePacket.Encrypted(stream) + "00 5C" -> ServerAccountInfoResponsePacket.Encrypted(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) @@ -89,6 +89,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { } } + @ExperimentalUnsignedTypes override fun toString(): String { return this.javaClass.simpleName + this.getAllDeclaredFields().joinToString(", ", "{", "}") { it.trySetAccessible(); it.name + "=" + it.get(this).let { value -> @@ -100,20 +101,19 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { } } } -} - -fun DataInputStream.readUntil(byte: Byte): ByteArray { - var buff = byteArrayOf() - var b: Byte - b = readByte() - while (b != byte) { - buff += b - b = readByte() + fun decryptBy(key: ByteArray): DataInputStream { + input.goto(14) + return DataInputStream(TEA.decrypt(input.readAllBytes().let { it.copyOfRange(0, it.size - 1) }, key).inputStream()) + } + + @ExperimentalUnsignedTypes + fun decryptBy(keyHex: String): DataInputStream { + return this.decryptBy(keyHex.hexToBytes()) } - return buff } + @ExperimentalUnsignedTypes fun DataInputStream.readIP(): String { var buff = "" @@ -150,6 +150,15 @@ fun <N : Number> DataInputStream.readNBytesAt(position: N, length: Int): ByteArr 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 { this.goto(position) return this.readInt(); @@ -163,4 +172,6 @@ fun <N : Number> DataInputStream.readByteAt(position: N): Byte { fun <N : Number> DataInputStream.readShortAt(position: N): Short { this.goto(position) 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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt index a6eb7bf81..6d69bef64 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt @@ -2,8 +2,8 @@ package net.mamoe.mirai.network.packet import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.utils.ByteArrayDataOutputStream -import net.mamoe.mirai.utils.TEACryptor -import net.mamoe.mirai.utils.getRandomKey +import net.mamoe.mirai.utils.TEA +import net.mamoe.mirai.utils.getRandomByteArray import net.mamoe.mirai.utils.lazyEncode import java.io.DataInputStream import java.net.InetAddress @@ -14,10 +14,8 @@ import java.net.InetAddress @ExperimentalUnsignedTypes @PacketId("08 28 04 34") class ClientSessionRequestPacket( - private val qq: Int, + private val qq: Long, private val serverIp: String, - private val loginIP: String, - private val md5_32: ByteArray, private val token38: ByteArray, private val token88: ByteArray, private val encryptionKey: ByteArray, @@ -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("00 38") this.write(token38) - this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { + this.write(TEA.encrypt(object : ByteArrayDataOutputStream() { override fun toByteArray(): ByteArray { this.writeHex("00 07 00 88") this.write(token88) @@ -36,22 +34,22 @@ class ClientSessionRequestPacket( this.writeIP(serverIp) 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(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(Protocol._0825data0) - this.writeHex(Protocol._0825data2) + this.writeHex(Protocol.constantData0) + this.writeHex(Protocol.constantData1) this.writeQQ(qq) 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.write(tlv0105) 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.write(getRandomKey(1)) + this.write(getRandomByteArray(1)) this.writeHex("10 00 00 00 00 00 00 00 02") //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.write(md5_32) + this.write(getRandomByteArray(32))//md5 32 this.writeHex("68") this.writeHex("00 00 00 00 00 2D 00 06 00 01") @@ -64,8 +62,6 @@ class ClientSessionRequestPacket( } /** - * Dispose_0828 - * * @author Him188moe */ class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val dataLength: Int) : ServerPacket(inputStream) { @@ -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) } -} -class ServerSessionKeyResponsePacketEncrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { - override fun decode() { - - } - - fun decrypt(_0828_rec_decr_key: ByteArray): ServerSessionKeyResponsePacket { - this.input goto 14 - val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } - return ServerSessionKeyResponsePacket(TEACryptor.decrypt(data, _0828_rec_decr_key).dataInputStream(), data.size); + class Encrypted(inputStream: DataInputStream) : ServerPacket(inputStream) { + fun decrypt(sessionResponseDecryptionKey: ByteArray): ServerSessionKeyResponsePacket { + this.input goto 14 + val data = this.input.readAllBytes().let { it.copyOfRange(0, it.size - 1) } + return ServerSessionKeyResponsePacket(TEA.decrypt(data, sessionResponseDecryptionKey).dataInputStream(), data.size) + } } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt index 5654f791c..ca14e3b38 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt @@ -40,7 +40,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp loginTime = input.readInt() loginIP = input.readIP() - tgtgtKey = getRandomKey(16) + tgtgtKey = getRandomByteArray(16) } else -> { @@ -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) { - ServerTouchResponsePacket.Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes() - ServerTouchResponsePacket.Type.TYPE_08_25_31_01 -> Protocol._0825key.hexToBytes() - }).inputStream())); + class Encrypted(private val type: Type, inputStream: DataInputStream) : ServerPacket(inputStream) { + @ExperimentalUnsignedTypes + fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(when (type) { + 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 */ @ExperimentalUnsignedTypes @PacketId("08 25 31 01") -class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() { +class ClientTouchPacket(val qq: Long, val serverIp: String) : ClientPacket() { @ExperimentalUnsignedTypes @Throws(IOException::class) override fun encode() { this.writeQQ(qq) 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) override fun toByteArray(): ByteArray { - this.writeHex(Protocol._0825data0) - this.writeHex(Protocol._0825data2) + this.writeHex(Protocol.constantData0) + this.writeHex(Protocol.constantData1) this.writeQQ(qq) this.writeHex("00 00 00 00 03 09 00 08 00 01") this.writeIP(serverIp); @@ -108,7 +97,7 @@ class ClientTouchPacket(val qq: Int, val serverIp: String) : ClientPacket() { */ @ExperimentalUnsignedTypes @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 override fun encode() { this.writeQQ(qq) @@ -116,11 +105,11 @@ class ClientServerRedirectionPacket(private val serverIP: String, private val qq this.writeHex(Protocol.redirectionKey) - this.write(TEACryptor.encrypt(object : ByteArrayDataOutputStream() { + this.write(TEA.encrypt(object : ByteArrayDataOutputStream() { @Throws(IOException::class) override fun toByteArray(): ByteArray { - this.writeHex(Protocol._0825data0) - this.writeHex(Protocol._0825data2) + this.writeHex(Protocol.constantData0) + this.writeHex(Protocol.constantData1) this.writeQQ(qq) this.writeHex("00 01 00 00 03 09 00 0C 00 01") this.writeIP(serverIP) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt new file mode 100644 index 000000000..3abf7a2f2 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt @@ -0,0 +1,103 @@ +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)) + } + } + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendFriendMessagePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt similarity index 85% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendFriendMessagePacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt index 905d3fd9b..0bea83586 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendFriendMessagePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt @@ -1,4 +1,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.packet.* @@ -11,15 +11,15 @@ import java.io.DataInputStream @PacketId("00 CD") @ExperimentalUnsignedTypes class ClientSendFriendMessagePacket( - val robotQQ: Int, - val targetQQ: Int, - val sessionKey: ByteArray, - val message: String + private val robotQQ: Long, + private val targetQQ: Long, + private val sessionKey: ByteArray, + private val message: String ) : ClientPacket() { override fun encode() { this.writeRandom(2)//part of packet id this.writeQQ(robotQQ) - this.writeHex(Protocol._fixVer) + this.writeHex(Protocol.fixVer2) this.encryptAndWrite(sessionKey) { it.writeQQ(robotQQ) @@ -52,13 +52,10 @@ class ClientSendFriendMessagePacket( it.writeByte(0x01) it.writeShort(bytes.size) it.write(bytes) - }//todo check + } } } } @PacketId("00 CD") -class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) { - override fun decode() { - } -} \ No newline at end of file +class ServerSendFriendMessageResponsePacket(input: DataInputStream) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt similarity index 63% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt index 315814a29..4abd29fed 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/message/ClientSendGroupMessagePacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt @@ -1,8 +1,7 @@ -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.packet.* -import net.mamoe.mirai.utils.lazyEncode import net.mamoe.mirai.utils.toUHexString import java.io.DataInputStream @@ -12,20 +11,20 @@ import java.io.DataInputStream @PacketId("00 02") @ExperimentalUnsignedTypes class ClientSendGroupMessagePacket( - private val groupId: Int,//不是 number - private val qq: Int, + private val groupId: Long,//不是 number + private val robotQQ: Long, private val sessionKey: ByteArray, private val message: String ) : ClientPacket() { override fun encode() { this.writeRandom(2)//part of packet id - this.writeQQ(qq) - this.writeHex(Protocol._fixVer) + this.writeQQ(robotQQ) + this.writeHex(Protocol.fixVer2) this.encryptAndWrite(sessionKey) { val bytes = message.toByteArray() it.writeByte(0x2A) - it.writeInt(groupId) + it.writeGroup(groupId) it.writeShort(56 + bytes.size) it.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00") @@ -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") -class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) { - override fun decode() { - } -} \ No newline at end of file +class ServerSendGroupMessageResponsePacket(input: DataInputStream) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginStatusPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt similarity index 80% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginStatusPacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt index e73b228d5..2007b99bc 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginStatusPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt @@ -5,12 +5,14 @@ import net.mamoe.mirai.network.packet.* import net.mamoe.mirai.utils.ClientLoginStatus /** + * 改变在线状态: "我在线上", "隐身" 等 + * * @author Him188moe */ @ExperimentalUnsignedTypes @PacketId("00 EC") -class ClientLoginStatusPacket( - private val qq: Int, +class ClientChangeOnlineStatusPacket( + private val qq: Long, private val sessionKey: ByteArray, private val loginStatus: ClientLoginStatus @@ -18,7 +20,7 @@ class ClientLoginStatusPacket( override fun encode() { this.writeRandom(2)//part of packet id this.writeQQ(qq) - this.writeHex(Protocol._fixVer) + this.writeHex(Protocol.fixVer2) this.encryptAndWrite(sessionKey) { it.writeHex("01 00") it.writeByte(loginStatus.id) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt similarity index 74% rename from mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginPacket.kt rename to mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt index c58e1c6d4..e28c1a6b7 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt @@ -2,11 +2,7 @@ package net.mamoe.mirai.network.packet.login import net.mamoe.mirai.network.Protocol import net.mamoe.mirai.network.packet.* -import net.mamoe.mirai.util.TestedSuccessfully -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 net.mamoe.mirai.utils.* import java.io.DataOutputStream /** @@ -18,7 +14,7 @@ import java.io.DataOutputStream @ExperimentalUnsignedTypes @TestedSuccessfully class ClientPasswordSubmissionPacket( - private val qq: Int, + private val qq: Long, private val password: String, private val loginTime: Int, private val loginIP: String, @@ -28,10 +24,10 @@ class ClientPasswordSubmissionPacket( @ExperimentalUnsignedTypes override fun encode() { this.writeQQ(qq) - this.writeHex(Protocol._0836_622_fix1) + this.writeHex(Protocol.passwordSubmissionKey1) this.writeHex(Protocol.publicKey) this.writeHex("00 00 00 10") - this.writeHex(Protocol._0836key1) + this.writeHex(Protocol.key0836) this.encryptAndWrite(Protocol.shareKey.hexToBytes()) { it.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825) @@ -43,17 +39,22 @@ class ClientPasswordSubmissionPacket( @PacketId("08 36 31 04") @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) +@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") @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) @ExperimentalUnsignedTypes open class ClientLoginResendPacket internal constructor( - val qq: Int, + val qq: Long, val password: String, val loginTime: Int, val loginIP: String, @@ -64,12 +65,12 @@ open class ClientLoginResendPacket internal constructor( ) : ClientPacket() { override fun encode() { this.writeQQ(qq) - this.writeHex(Protocol._0836_622_fix1) + this.writeHex(Protocol.passwordSubmissionKey1) this.writeHex(Protocol.publicKey) 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 { this.writePart1(qq, password, loginTime, loginIP, tgtgtKey, token0825, tlv_0006_encr) @@ -91,7 +92,7 @@ open class ClientLoginResendPacket internal constructor( * @author Him188moe */ @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.writeHex("01 12")//tag @@ -110,12 +111,12 @@ private fun DataOutputStream.writePart1(qq: Int, password: String, loginTime: In this.writeTLV0006(qq, password, loginTime, loginIP, tgtgtKey) } //fix - this.writeHex(Protocol._0836_622_fix2) + this.writeHex(Protocol.passwordSubmissionKey2) this.writeHex("00 1A")//tag this.writeHex("00 40")//length - this.write(TEACryptor.encrypt(Protocol._0836_622_fix2.hexToBytes(), tgtgtKey)) - this.writeHex(Protocol._0825data0) - this.writeHex(Protocol._0825data2) + this.write(TEA.encrypt(Protocol.passwordSubmissionKey2.hexToBytes(), tgtgtKey)) + this.writeHex(Protocol.constantData0) + this.writeHex(Protocol.constantData1) this.writeQQ(qq) this.writeZero(4) diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginVerificationCodePacket.kt deleted file mode 100644 index 751fa07bf..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLoginVerificationCodePacket.kt +++ /dev/null @@ -1,41 +0,0 @@ -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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt new file mode 100644 index 000000000..482c057a9 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt @@ -0,0 +1,46 @@ +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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseFailedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseFailedPacket.kt index 5276b7f39..6c1915094 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseFailedPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseFailedPacket.kt @@ -6,19 +6,7 @@ import java.io.DataInputStream /** * @author Him188moe */ -class ServerLoginResponseFailedPacket(val state: State, 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, - } - +class ServerLoginResponseFailedPacket(val loginState: LoginState, input: DataInputStream) : ServerPacket(input) { override fun decode() { } } \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt index 033f993d9..b6b8f448c 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt @@ -1,13 +1,9 @@ package net.mamoe.mirai.network.packet.login import net.mamoe.mirai.network.Protocol -import net.mamoe.mirai.network.packet.ServerPacket -import net.mamoe.mirai.network.packet.goto -import net.mamoe.mirai.network.packet.readNBytesAt -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.network.packet.* +import net.mamoe.mirai.utils.TEA +import net.mamoe.mirai.utils.TestedSuccessfully import net.mamoe.mirai.utils.toUHexString import java.io.DataInputStream @@ -15,8 +11,8 @@ import java.io.DataInputStream * @author NaturalHG */ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(input) { - lateinit var _0828_rec_decr_key: ByteArray//16 bytes| - lateinit var nick: String + lateinit var sessionResponseDecryptionKey: ByteArray//16 bytes| + lateinit var nickname: String lateinit var token38: ByteArray lateinit var token88: ByteArray @@ -26,72 +22,6 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in @TestedSuccessfully @ExperimentalUnsignedTypes 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.encryptionKey = this.input.readNBytes(16)//24 @@ -106,32 +36,25 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in 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) 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.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 { - 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) - } -} +} \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt index 7372b1ff8..37a9862d0 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseResendPacket.kt @@ -2,12 +2,8 @@ package net.mamoe.mirai.network.packet.login import net.mamoe.mirai.network.packet.PacketId 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.util.TestedSuccessfully -import net.mamoe.mirai.utils.TEACryptor -import net.mamoe.mirai.utils.hexToUBytes -import net.mamoe.mirai.utils.toUHexString +import net.mamoe.mirai.utils.TestedSuccessfully import java.io.DataInputStream /** @@ -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() { - - } - - @TestedSuccessfully - 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) + class Encrypted(input: DataInputStream, private val flag: Flag) : ServerPacket(input) { + @TestedSuccessfully + fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseResendPacket = ServerLoginResponseResendPacket(decryptBy(tgtgtKey), 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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt new file mode 100644 index 000000000..448d1413c --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt @@ -0,0 +1,70 @@ +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 + */ diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodePacket.kt deleted file mode 100644 index 540b23f5a..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodePacket.kt +++ /dev/null @@ -1,42 +0,0 @@ -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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt index 2aebc04d5..4b5d092d0 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt @@ -4,10 +4,8 @@ import net.mamoe.mirai.network.packet.ServerPacket import java.io.DataInputStream /** + * Congratulations! + * * @author Him188moe */ -class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input) { - override fun decode() { - - } -} \ No newline at end of file +class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt deleted file mode 100644 index 710c1c2a0..000000000 --- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/verification/ServerVerificationCodePacket.kt +++ /dev/null @@ -1,44 +0,0 @@ -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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/plugin/MiraiPluginBase.java b/mirai-core/src/main/java/net/mamoe/mirai/plugin/MiraiPluginBase.java index 085ee1aab..8bec23692 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/plugin/MiraiPluginBase.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/plugin/MiraiPluginBase.java @@ -1,4 +1,14 @@ 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 { } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskExceptionHandler.java b/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskExceptionHandler.java index 1bef14b21..d79df9f53 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskExceptionHandler.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskExceptionHandler.java @@ -4,15 +4,12 @@ package net.mamoe.mirai.task; public interface MiraiTaskExceptionHandler { void onHandle(Throwable e); - static MiraiTaskExceptionHandler byDefault(){ - return byPrint(); - } - - static MiraiTaskExceptionHandler byIgnore(){ - return a -> {}; - } - - static MiraiTaskExceptionHandler byPrint(){ + static MiraiTaskExceptionHandler printing() { return Throwable::printStackTrace; } + + static MiraiTaskExceptionHandler ignoring() { + return a -> { + }; + } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskManager.java b/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskManager.java index cf814fc39..4e950374b 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskManager.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/task/MiraiTaskManager.java @@ -2,7 +2,7 @@ package net.mamoe.mirai.task; 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.Future; @@ -24,7 +24,7 @@ public final class MiraiTaskManager { this.pool = new MiraiThreadPool(); MiraiEventHook - .onEvent(ServerDisableEvent.class) + .onEvent(ServerDisabledEvent.class) .handler(a -> this.pool.close()) .mount(); @@ -35,7 +35,7 @@ public final class MiraiTaskManager { */ public void execute(Runnable runnable) { - this.execute(runnable, MiraiTaskExceptionHandler.byDefault()); + this.execute(runnable, MiraiTaskExceptionHandler.printing()); } public void execute(Runnable runnable, MiraiTaskExceptionHandler handler) { @@ -51,7 +51,7 @@ public final class MiraiTaskManager { 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) { @@ -69,7 +69,7 @@ public final class MiraiTaskManager { * 异步任务 */ 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) { @@ -87,7 +87,7 @@ public final class MiraiTaskManager { */ 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) { @@ -95,7 +95,7 @@ public final class MiraiTaskManager { } 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) { diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ClientLoginStatus.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/ClientLoginStatus.java index 45e1976fd..b2864cae6 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/ClientLoginStatus.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/ClientLoginStatus.java @@ -4,10 +4,14 @@ package net.mamoe.mirai.utils; * @author Him188moe */ public enum ClientLoginStatus { + /** + * 我在线上 + */ ONLINE(0x0A); - // TODO: 2019/8/31 add more - public final int id;//1byte + // TODO: 2019/8/31 add more ClientLoginStatus + + public final int id;//1 ubyte ClientLoginStatus(int id) { this.id = id; diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java index 2729890f2..6cfde9c52 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java @@ -6,5 +6,5 @@ import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap; /** * @author Him188moe */ -public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedListMap<Integer, C> { +public class ContactList<C extends Contact> extends MiraiSynchronizedLinkedListMap<Long, C> { } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/RobotAccount.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/RobotAccount.java new file mode 100644 index 000000000..0f2bea143 --- /dev/null +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/RobotAccount.java @@ -0,0 +1,17 @@ +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; + } +} diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEACryptor.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.java similarity index 84% rename from mirai-core/src/main/java/net/mamoe/mirai/utils/TEACryptor.java rename to mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.java index 9b6269c49..14a05bfdd 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEACryptor.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.java @@ -6,12 +6,13 @@ import java.nio.ByteBuffer; import java.util.Random; /** + * TEA 加密 + * * @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java */ -public class TEACryptor { - public static final TEACryptor CRYPTOR_SHARE_KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol.shareKey)); - public static final TEACryptor CRYPTOR_0825KEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._0825key)); - public static final TEACryptor CRYPTOR_00BAKEY = new TEACryptor(Protocol.Companion.hexToBytes(Protocol._00BaKey)); +public final class TEA { + public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.shareKey)); + public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.key0825)); private static final long UINT32_MASK = 0xffffffffL; private final long[] mKey; @@ -23,26 +24,33 @@ public class TEACryptor { private int mOutPos; private int mPreOutPos; private boolean isFirstBlock; - private boolean isRand; - public TEACryptor(byte[] key) { + public TEA(byte[] key) { mKey = new long[4]; for (int i = 0; i < 4; i++) { mKey[i] = pack(key, i * 4, 4); } - isRand = true; mRandom = new Random(); isFirstBlock = true; } 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) { - 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) { long result = 0; int max_offset = len > 8 ? offset + 8 : offset + len; @@ -53,11 +61,7 @@ public class TEACryptor { } private int rand() { - return isRand ? mRandom.nextInt() : 0xff00ff; - } - - public void enableRandom(boolean rand) { - isRand = rand; + return mRandom.nextInt(); } private byte[] encode(byte[] bytes) { @@ -109,6 +113,7 @@ public class TEACryptor { isFirstBlock = false; } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean decodeOneBlock(byte[] ciphertext, int offset, int len) { for (mIndexPos = 0; mIndexPos < 8; mIndexPos++) { if (mOutPos + mIndexPos < len) { @@ -125,6 +130,7 @@ public class TEACryptor { } + @SuppressWarnings("SameParameterValue") private byte[] encrypt(byte[] plaintext, int offset, int len) { mInBlock = new byte[8]; mIV = new byte[8]; @@ -175,11 +181,12 @@ public class TEACryptor { 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) { throw new IllegalArgumentException("must len % 8 == 0 && len >= 16"); } - mIV = decode(ciphertext, offset); + mIV = decode(cipherText, offset); mIndexPos = mIV[0] & 7; int plen = len - mIndexPos - 10; isFirstBlock = true; @@ -198,7 +205,7 @@ public class TEACryptor { } if (mIndexPos == 8) { isFirstBlock = false; - if (!decodeOneBlock(ciphertext, offset, len)) { + if (!decodeOneBlock(cipherText, offset, len)) { throw new RuntimeException("Unable to decode"); } } @@ -208,20 +215,20 @@ public class TEACryptor { if (mIndexPos < 8) { mOutput[outpos++] = isFirstBlock ? mIV[mIndexPos] : - (byte) (ciphertext[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]); + (byte) (cipherText[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]); ++mIndexPos; } if (mIndexPos == 8) { mPreOutPos = mOutPos - 8; isFirstBlock = false; - if (!decodeOneBlock(ciphertext, offset, len)) { + if (!decodeOneBlock(cipherText, offset, len)) { throw new RuntimeException("Unable to decode"); } } } for (g = 0; g < 7; g++) { if (mIndexPos < 8) { - if ((ciphertext[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]) != 0) { + if ((cipherText[mPreOutPos + offset + mIndexPos] ^ mIV[mIndexPos]) != 0) { throw new RuntimeException(); } else { ++mIndexPos; @@ -230,7 +237,7 @@ public class TEACryptor { if (mIndexPos == 8) { mPreOutPos = mOutPos; - if (!decodeOneBlock(ciphertext, offset, len)) { + if (!decodeOneBlock(cipherText, offset, len)) { throw new RuntimeException("Unable to decode"); } } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt index 6b3e910ee..d2298e467 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt @@ -1,4 +1,4 @@ -package net.mamoe.mirai.util +package net.mamoe.mirai.utils /** * @author Him188moe diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt index 3fac69b21..bcf62f885 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt @@ -8,7 +8,9 @@ import java.lang.reflect.Field import java.util.* import java.util.zip.CRC32 +@JvmSynthetic fun ByteArray.toHexString(): String = toHexString(" ") + fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(separator) { var ret = it.toString(16).toUpperCase() if (ret.length == 1) { @@ -21,18 +23,23 @@ fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(s fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator) @ExperimentalUnsignedTypes +@JvmSynthetic fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString() @ExperimentalUnsignedTypes -fun UByteArray.toUHexString(separator: String = " "): String = this.joinToString(separator) { - var ret = it.toString(16).toUpperCase() - if (ret.length == 1) { - ret = "0$ret" +@JvmSynthetic +fun UByteArray.toUHexString(separator: String = " "): String { + return this.joinToString(separator) { + var ret = it.toString(16).toUpperCase() + if (ret.length == 1) { + ret = "0$ret" + } + return@joinToString ret } - return@joinToString ret } @ExperimentalUnsignedTypes +@JvmSynthetic fun UByteArray.toUHexString(): String = this.toUHexString(" ") @ExperimentalUnsignedTypes @@ -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() } @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 fun String.hexToByte(): Byte = hexToBytes()[0] @@ -65,17 +72,18 @@ open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream()) fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().let { t(it); return it.toByteArray() } @ExperimentalUnsignedTypes -fun getRandomKey(length: Int): ByteArray { +fun getRandomByteArray(length: Int): ByteArray { val bytes = LinkedList<Byte>() repeat(length) { bytes.add((Math.random() * 255).toByte()) } return bytes.toByteArray() } +@JvmSynthetic operator fun File.plus(child: String): File = File(this, child) private const val GTK_BASE_VALUE: Int = 5381 -fun getGTK(sKey: String): Int { +internal fun getGTK(sKey: String): Int { var value = GTK_BASE_VALUE for (c in sKey.toCharArray()) { value += (value shl 5) + c.toInt() @@ -85,7 +93,7 @@ fun getGTK(sKey: String): Int { 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> { 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 diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java index 196ddd59a..7005b3f59 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java @@ -126,10 +126,10 @@ public class MiraiConfigSection<T> extends MiraiSynchronizedLinkedListMap<String 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); if(result == null){ - throw throwableCallable.call(); + throw exceptionSupplier.get(); } return result.toString(); } diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSetting.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSettings.java similarity index 68% rename from mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSetting.java rename to mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSettings.java index 4f93da511..7eaa08cb7 100644 --- a/mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSetting.java +++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/setting/MiraiSettings.java @@ -1,21 +1,22 @@ package net.mamoe.mirai.utils.setting; +import net.mamoe.mirai.plugin.MiraiPluginBase; import org.ini4j.Config; import org.ini4j.Ini; import java.io.File; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * Mirai Config - * Only support {INI} format - * Support MAP and LIST - * Thread safe + * Thread-safe Mirai Config <br> + * Only supports <code>INI</code> format <br> + * Supports {@link Map} and {@link List} */ -public class MiraiSetting { +public class MiraiSettings { private File file; @@ -23,14 +24,21 @@ public class MiraiSetting { 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(".")){ - file = new File(file.getParent() + file.getName() + ".ini"); + file = new File(file.getPath() + ".ini"); } this.file = file; try { if(file.exists()){ - file.createNewFile(); + if (!file.createNewFile()) { + throw new RuntimeException("cannot create config file " + file); + } } Config config = new Config(); config.setMultiSection(true); @@ -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); } - public MiraiSettingMapSection getMapSection(String key){ + public synchronized MiraiSettingMapSection getMapSection(String key) { if(!cacheSection.containsKey(key)) { MiraiSettingMapSection section = new MiraiSettingMapSection(); if(ini.containsKey(key)){ @@ -58,7 +66,7 @@ public class MiraiSetting { return (MiraiSettingMapSection) cacheSection.get(key); } - public MiraiSettingListSection getListSection(String key){ + public synchronized MiraiSettingListSection getListSection(String key) { if(!cacheSection.containsKey(key)) { MiraiSettingListSection section = new MiraiSettingListSection(); if(ini.containsKey(key)){ @@ -85,7 +93,7 @@ public class MiraiSetting { } } - public void clearCache(){ + public synchronized void clearCache() { cacheSection.clear(); } } diff --git a/mirai-core/src/test/java/BadQQFilter.kt b/mirai-core/src/test/java/BadQQFilter.kt new file mode 100644 index 000000000..7cac454e1 --- /dev/null +++ b/mirai-core/src/test/java/BadQQFilter.kt @@ -0,0 +1,129 @@ +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 }) +} + diff --git a/mirai-core/src/test/java/HexComparator.java b/mirai-core/src/test/java/HexComparator.java index cabaeba5d..51f6677e2 100644 --- a/mirai-core/src/test/java/HexComparator.java +++ b/mirai-core/src/test/java/HexComparator.java @@ -3,11 +3,17 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; 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.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Scanner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; @@ -32,15 +38,24 @@ public class HexComparator { 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")); }}; private static class ConstMatcher { 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(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 ConstMatcher(String hex) { @@ -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) { StringBuilder builder = new StringBuilder(); @@ -105,10 +137,17 @@ public class HexComparator { StringBuilder numberLine = new StringBuilder(); + StringBuilder hex1ConstName = new StringBuilder(); StringBuilder hex1b = new StringBuilder(); StringBuilder hex2b = new StringBuilder(); + StringBuilder hex2ConstName = new StringBuilder(); 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) { String h1 = null; String h2 = null; @@ -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(" "); hex2b.append(" ").append(h2).append(" "); if (isDif) { @@ -165,42 +204,46 @@ public class HexComparator { return (builder.append(" ").append(dif).append(" 个不同").append("\n") .append(numberLine).append("\n") + .append(hex1ConstName).append("\n") .append(hex1b).append("\n") - .append(hex2b)) + .append(hex2b).append("\n") + .append(hex2ConstName).append("\n") + ) .toString(); } - private static void doConstReplacement(StringBuilder builder){ + private static void doConstReplacement(StringBuilder builder) { String mirror = builder.toString(); 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()); consts.forEach(a -> { hexs.setTo(i.get()); List<Integer> posToPlaceColor = new LinkedList<>(); AtomicBoolean is = new AtomicBoolean(false); - a.readFully((c,d) -> { - if(c.equals(hexs.readHex())){ + a.readFully((c, d) -> { + if (c.equals(hexs.readHex())) { posToPlaceColor.add(d); - }else{ + } else { is.set(false); } }); - if(is.get()){ + if (is.get()) { AtomicInteger adder = new AtomicInteger(); 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) { return "00" + number; } @@ -210,8 +253,31 @@ public class HexComparator { 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( //mirai @@ -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" ));*/ } - - } -class HexReader{ +class HexReader { private String s; private int pos = 0; private int lastHaxPos = 0; - public HexReader(String s){ + public HexReader(String s) { this.s = s; } - public String readHex(){ + public String readHex() { boolean isStr = false; String next = ""; - for (;pos<s.length()-2;++pos){ + for (; pos < s.length() - 2; ++pos) { char s1 = ' '; - if(pos != 0){ + if (pos != 0) { s1 = this.s.charAt(0); } - char s2 = this.s.charAt(pos+1); - char s3 = this.s.charAt(pos+2); + char s2 = this.s.charAt(pos + 1); + char s3 = this.s.charAt(pos + 2); char s4 = ' '; - if(this.s.length() != (this.pos+3)){ - s4 = this.s.charAt(pos+3); + if (this.s.length() != (this.pos + 3)) { + s4 = this.s.charAt(pos + 3); } - if( + if ( Character.isSpaceChar(s1) && Character.isSpaceChar(s4) - && + && (Character.isDigit(s2) || Character.isAlphabetic(s2)) && (Character.isDigit(s3) || Character.isAlphabetic(s3)) - ){ - this.pos+=2; - this.lastHaxPos = this.pos+1; - return String.valueOf(s2) + String.valueOf(s3); + ) { + this.pos += 2; + this.lastHaxPos = this.pos + 1; + return String.valueOf(s2) + s3; } } return ""; } - public void readFully(BiConsumer<String, Integer> processor){ + public void readFully(BiConsumer<String, Integer> processor) { this.reset(); String nextHax = this.readHex(); - while (!nextHax.equals(" ")){ - processor.accept(nextHax,this.lastHaxPos); + while (!nextHax.equals(" ")) { + processor.accept(nextHax, this.lastHaxPos); nextHax = this.readHex(); } } - public void setTo(int pos){ + public void setTo(int pos) { this.pos = pos; } - public void reset(){ + public void reset() { this.pos = 0; } - } diff --git a/mirai-core/src/test/java/TestKt.kt b/mirai-core/src/test/java/TestKt.kt new file mode 100644 index 000000000..42878b5dc --- /dev/null +++ b/mirai-core/src/test/java/TestKt.kt @@ -0,0 +1,8 @@ +import net.mamoe.mirai.utils.toUHexString + +/** + * @author Him188moe + */ +fun main() { + println("牛逼".toByteArray().toUHexString()) +} \ No newline at end of file