From 402e8fbb3275a331b02f7fb17ef0fd045cb3f48e Mon Sep 17 00:00:00 2001
From: Him188moe <Him188>
Date: Thu, 5 Sep 2019 22:10:40 +0800
Subject: [PATCH] Updated robot & network structure

---
 mirai-core/pom.xml                            |   6 +
 .../java/net/mamoe/mirai/MiraiServer.java     |   2 +-
 .../src/main/java/net/mamoe/mirai/Robot.java  |   1 -
 ...MiraiEventManagerKt.kt => EventManager.kt} |   3 +
 .../mamoe/mirai/event/MiraiEventManager.java  |   6 +-
 .../mamoe/mirai/event/events/MiraiEvent.java  |   2 +-
 .../event/events/network/PacketEvent.java     |  19 +++
 .../events/network/PacketReceivedEvent.java   |   7 +
 .../events/network/ServerPacketEvent.java     |  17 +++
 .../network/ServerPacketReceivedEvent.java    |  13 ++
 .../java/net/mamoe/mirai/network/Protocol.kt  |   4 +-
 .../mirai/network/RobotNetworkHandler.kt      | 107 ++++++++++-----
 .../mirai/network/packet/ClientPacket.kt      |   6 +-
 .../net/mamoe/mirai/network/packet/Session.kt |   6 +-
 .../net/mamoe/mirai/network/packet/Touch.kt   |   2 +-
 .../mirai/network/packet/VerificationCode.kt  |   2 +-
 .../mirai/network/packet/login/LoginState.kt  |   5 +-
 .../main/java/net/mamoe/mirai/utils/Utils.kt  |   2 +-
 mirai-core/src/test/java/BadQQFilter.kt       | 127 ++++++++++++++++++
 19 files changed, 281 insertions(+), 56 deletions(-)
 rename mirai-core/src/main/java/net/mamoe/mirai/event/{MiraiEventManagerKt.kt => EventManager.kt} (90%)
 create mode 100644 mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java
 create mode 100644 mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketReceivedEvent.java
 create mode 100644 mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketEvent.java
 create mode 100644 mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java
 create mode 100644 mirai-core/src/test/java/BadQQFilter.kt

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/MiraiServer.java b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
index 9d9074da2..a2a1556c0 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/MiraiServer.java
@@ -120,7 +120,7 @@ public class MiraiServer {
         this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
             try {
                 Robot robot = new Robot(section);
-                robot.network.tryLogin$mirai_core(state -> {
+                robot.network.tryLogin$mirai_core((robot1, state) -> {
                     if (state == LoginState.SUCCEED) {
                         Robot.instances.add(robot);
                     } else {
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 0c3a1c169..7d25a04f0 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/Robot.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/Robot.java
@@ -89,7 +89,6 @@ public final class Robot implements Closeable {
 
     public void close() {
         this.network.close();
-        this.owners.clear();
         this.contacts.groups.values().forEach(Group::close);
         this.contacts.groups.clear();
         this.contacts.qqs.clear();
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt b/mirai-core/src/main/java/net/mamoe/mirai/event/EventManager.kt
similarity index 90%
rename from mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt
rename to mirai-core/src/main/java/net/mamoe/mirai/event/EventManager.kt
index 1ae0337c2..42e6765f0 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/event/MiraiEventManagerKt.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/event/EventManager.kt
@@ -3,6 +3,9 @@ package net.mamoe.mirai.event
 import net.mamoe.mirai.event.events.MiraiEvent
 import kotlin.reflect.KClass
 
+object EventManager : MiraiEventManager()
+typealias MiraiEventManagerKt = EventManager
+typealias EventMgr = EventManager
 
 fun <C : Class<E>, E : MiraiEvent> C.hookAlways(hook: (E) -> Unit) {
     MiraiEventManager.getInstance().hookAlways(MiraiEventHook<E>(this, hook))
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..d93ba6684 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
@@ -10,14 +10,12 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 public class MiraiEventManager {
-    private MiraiEventManager() {
+    MiraiEventManager() {
 
     }
 
-    private static MiraiEventManager instance = new MiraiEventManager();
-
     public static MiraiEventManager getInstance() {
-        return MiraiEventManager.instance;
+        return EventManager.INSTANCE;
     }
 
     private final ReentrantReadWriteLock hooksLock = new ReentrantReadWriteLock();
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
index 0915b523a..a8509e7f3 100644
--- 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
@@ -11,7 +11,7 @@ public abstract class MiraiEvent {
 
     public boolean isCancelled() {
         if (!(this instanceof Cancellable)) {
-            throw new EventException("Event is not Cancellable");
+            return false;
         }
         return this.cancelled;
     }
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..119f87933
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketEvent.java
@@ -0,0 +1,19 @@
+package net.mamoe.mirai.event.events.network;
+
+import net.mamoe.mirai.event.events.MiraiEvent;
+import net.mamoe.mirai.network.packet.Packet;
+
+/**
+ * @author Him188moe
+ */
+public abstract class PacketEvent extends MiraiEvent {
+    private final Packet packet;
+
+    public PacketEvent(Packet packet) {
+        this.packet = packet;
+    }
+
+    public Packet getPacket() {
+        return packet;
+    }
+}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketReceivedEvent.java b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketReceivedEvent.java
new file mode 100644
index 000000000..77b697050
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/PacketReceivedEvent.java
@@ -0,0 +1,7 @@
+package net.mamoe.mirai.event.events.network;
+
+/**
+ * @author Him188moe
+ */
+public class PacketReceivedEvent {
+}
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..77fa219ea
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/event/events/network/ServerPacketReceivedEvent.java
@@ -0,0 +1,13 @@
+package net.mamoe.mirai.event.events.network;
+
+import net.mamoe.mirai.event.events.Cancellable;
+import net.mamoe.mirai.network.packet.ServerPacket;
+
+/**
+ * @author Him188moe
+ */
+public class ServerPacketReceivedEvent extends ServerPacketEvent implements Cancellable {
+    public ServerPacketReceivedEvent(ServerPacket packet) {
+        super(packet);
+    }
+}
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 2e33a4f20..d639dffa7 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
@@ -13,13 +13,13 @@ object Protocol {
             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"
+                    "sz9.tencent.com",
+                    "sz2.tencent.com"
             ).forEach { this.add(InetAddress.getByName(it).hostAddress) }
 
         }
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 e6d784a0a..22081cc11 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
@@ -3,8 +3,10 @@ package net.mamoe.mirai.network
 import net.mamoe.mirai.Robot
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.QQ
+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.packet.*
 import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
@@ -35,7 +37,7 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
     val messageHandler = MessageHandler()
     val actionHandler = ActionHandler()
 
-    private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = mapOf(
+    private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = linkedMapOf(
             DebugHandler::class to debugHandler,
             LoginHandler::class to loginHandler,
             MessageHandler::class to messageHandler,
@@ -63,16 +65,22 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
 
     //private | internal
 
-    internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) {
+    /**
+     * 仅当 [LoginState] 非 [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook].
+     * 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCEED] 调用 [loginHandler]
+     */
+    internal fun tryLogin(loginHook: (Robot.(LoginState) -> Unit)? = null) {
         val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
         fun login(): Boolean {
             val ip = ipQueue.poll()
             return if (ip != null) {
                 this@RobotNetworkHandler.socketHandler.touch(ip) { state ->
-                    if (state == LoginState.UNKNOWN) {
+                    if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
                         login()
                     } else {
-                        loginHook?.invoke(state)
+                        if (loginHook != null) {
+                            robot.loginHook(state)
+                        }
                     }
                 }
                 true
@@ -82,7 +90,12 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
     }
 
     @ExperimentalUnsignedTypes
-    internal fun onPacketReceived(packet: ServerPacket) {
+    internal fun distributePacket(packet: ServerPacket) {
+        packet.decode()
+        if (ServerPacketReceivedEvent(packet).broadcast().isCancelled) {
+            debugHandler.onPacketReceived(packet)
+            return
+        }
         this.packetHandlers.values.forEach {
             it.onPacketReceived(packet)
         }
@@ -90,11 +103,10 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
 
 
     private inner class SocketHandler : Closeable {
-        private lateinit var socket: DatagramSocket
+        private var socket: DatagramSocket? = null
 
         internal var serverIP: String = ""
             set(value) {
-                serverAddress = InetSocketAddress(value, 8000)
                 field = value
 
                 restartSocket()
@@ -104,36 +116,29 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
         internal var loginState: LoginState? = null
             set(value) {
                 field = value
-                if (value != null && value != LoginState.UNKNOWN) {
+                if (value != null) {
                     loginHook?.invoke(value)
                 }
             }
 
-        private lateinit var serverAddress: InetSocketAddress
-
         private fun restartSocket() {
-
-            socket = DatagramSocket((15314 + Math.random() * 100).toInt())
-            socket.close()
-            socket.connect(this.serverAddress)
+            socket?.close()
+            socket = DatagramSocket(0)
+            socket!!.connect(InetSocketAddress(serverIP, 8000))
             Thread {
-                while (socket.isConnected) {
+                while (socket!!.isConnected) {
                     val packet = DatagramPacket(ByteArray(2048), 2048)
-                    kotlin
-                            .runCatching { socket.receive(packet) }
+                    kotlin.runCatching { socket!!.receive(packet) }
                             .onSuccess {
                                 MiraiThreadPool.getInstance().submit {
                                     try {
-                                        onPacketReceived(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
+                                        distributePacket(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
                                     } catch (e: Exception) {
                                         e.printStackTrace()
                                     }
                                 }
                             }.onFailure {
-                                if (it.message == "socket closed") {
-                                    if (!closed) {
-                                        restartSocket()
-                                    }
+                                if (it.message == "Socket closed" || it.message == "socket closed") {
                                     return@Thread
                                 }
                                 it.printStackTrace()
@@ -147,11 +152,16 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
          * Start network and touch the server
          */
         internal fun touch(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
+            MiraiLogger.info("Connecting server: $serverAddress")
             socketHandler.serverIP = serverAddress
             if (loginHook != null) {
                 this.loginHook = loginHook
             }
             sendPacket(ClientTouchPacket(robot.account.qqNumber, socketHandler.serverIP))
+            waitForPacket(ServerTouchResponsePacket::class, 100) {
+                MiraiLogger.error("  Timeout")
+                loginHook?.invoke(LoginState.TIMEOUT)
+            }
         }
 
         /**
@@ -159,20 +169,46 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
          */
         @ExperimentalUnsignedTypes
         internal fun sendPacket(packet: ClientPacket) {
+            checkNotNull(socket) { "socket closed" }
+
             try {
                 packet.encode()
                 packet.writeHex(Protocol.tail)
 
                 val data = packet.toByteArray()
-                socket.send(DatagramPacket(data, data.size))
+                socket!!.send(DatagramPacket(data, data.size))
                 MiraiLogger info "Packet sent: $packet"
             } 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()
+            this.socket?.close()
             this.loginState = null
             this.loginHook = null
         }
@@ -194,7 +230,6 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
      */
     inner class DebugHandler : PacketHandler() {
         override fun onPacketReceived(packet: ServerPacket) {
-            packet.decode()
             MiraiLogger info "Packet received: $packet"
             if (packet is ServerEventPacket) {
                 sendPacket(ClientMessageResponsePacket(robot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity))
@@ -274,6 +309,8 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
                 }
 
                 is ServerVerificationCodeTransmissionPacket -> {
+                    socketHandler.loginState = LoginState.VERIFICATION_CODE
+
                     this.verificationCodeSequence++
                     this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
 
@@ -371,17 +408,17 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
                     sendPacket(ClientAccountInfoRequestPacket(robot.account.qqNumber, sessionKey))
                 }
 
-                is ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
+                is ServerEventPacket.Raw -> distributePacket(packet.distribute())
 
-                is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt())
-                is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt())
-                is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
-                is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
-                is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey))
-                is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt())
-                is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
-                is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
-                is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
+                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,
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 a31ed32e9..e21289fd9 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
@@ -152,10 +152,6 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
     }
 }
 
-fun main() {
-
-}
-
 /*
 @ExperimentalUnsignedTypes
 fun main() {
@@ -164,7 +160,7 @@ fun main() {
 
 @ExperimentalUnsignedTypes
 @TestedSuccessfully
-fun DataOutputStream.writeCRC32() = writeCRC32(getRandomKey(16))
+fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16))
 
 
 @ExperimentalUnsignedTypes
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 ee1dadd85..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
@@ -3,7 +3,7 @@ package net.mamoe.mirai.network.packet
 import net.mamoe.mirai.network.Protocol
 import net.mamoe.mirai.utils.ByteArrayDataOutputStream
 import net.mamoe.mirai.utils.TEA
-import net.mamoe.mirai.utils.getRandomKey
+import net.mamoe.mirai.utils.getRandomByteArray
 import net.mamoe.mirai.utils.lazyEncode
 import java.io.DataInputStream
 import java.net.InetAddress
@@ -44,12 +44,12 @@ class ClientSessionRequestPacket(
                 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(getRandomKey(32))//md5 32
+                this.write(getRandomByteArray(32))//md5 32
                 this.writeHex("68")
 
                 this.writeHex("00 00 00 00 00 2D 00 06 00 01")
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 6e10d37a9..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 -> {
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
index e2e3366a3..5f4b6bdae 100644
--- 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
@@ -82,7 +82,7 @@ class ServerVerificationCodeRepeatPacket(input: DataInputStream) : ServerVerific
     @ExperimentalUnsignedTypes
     override fun decode() {
         token00BA = this.input.readNBytesAt(10, 56)
-        tgtgtKeyUpdate = getRandomKey(16)
+        tgtgtKeyUpdate = getRandomByteArray(16)
     }
 }
 
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
index e03d96855..c2168c03c 100644
--- 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
@@ -13,8 +13,11 @@ enum class LoginState {
     UNKNOWN_QQ_NUMBER,//你输入的帐号不存在
     DEVICE_LOCK,//设备锁
     TAKEN_BACK,//被回收
-    // VERIFICATION_CODE,//需要验证码
+
+
+    VERIFICATION_CODE,//需要验证码
 
 
     UNKNOWN,
+    TIMEOUT,
 }
\ No newline at end of file
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 0d3fd2fca..9f6d6b347 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
@@ -69,7 +69,7 @@ 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()
diff --git a/mirai-core/src/test/java/BadQQFilter.kt b/mirai-core/src/test/java/BadQQFilter.kt
new file mode 100644
index 000000000..22114508e
--- /dev/null
+++ b/mirai-core/src/test/java/BadQQFilter.kt
@@ -0,0 +1,127 @@
+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("----")
+            Robot(RobotAccount(strings[0].toLong(), strings[1].let { password ->
+                if (password.endsWith(".")) {
+                    return@let password.substring(0, password.length - 1)
+                }
+                return@let password
+            }), listOf()).network.tryLogin { state ->
+                if (!(state == LoginState.BLOCKED || state == LoginState.DEVICE_LOCK || state == LoginState.WRONG_PASSWORD)) {
+                    goodRobotList.add(this)
+                }
+            }
+        }
+    }
+
+    Thread.sleep(9 * 3000)
+
+    println(goodRobotList.joinToString("\n") { it.account.qqNumber.toString() + "    " + it.account.password })
+}
+