From defe7ce05470191c529e29e490c408e1203c1618 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 11:19:05 +0800
Subject: [PATCH 1/9] Fix addSuppressed

---
 .../androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt   | 2 +-
 .../src/jvmMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
index fb52beebe..8bf594b5a 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
@@ -6,7 +6,7 @@ private var isAddSuppressedSupported: Boolean = true
 
 @PublishedApi
 internal actual fun Throwable.addSuppressedMirai(e: Throwable) {
-    if (this === e) {
+    if (this == e) {
         return
     }
     if (!isAddSuppressedSupported) {
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
index 797402a7c..ab0da8e54 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/addSuppressed.kt
@@ -4,7 +4,7 @@ private var isAddSuppressedSupported: Boolean = true
 
 @PublishedApi
 internal actual fun Throwable.addSuppressedMirai(e: Throwable) {
-    if (this === e) {
+    if (this == e) {
         return
     }
     if (!isAddSuppressedSupported) {

From 26210e7845d6a3287925882fb27a7228eed0cb1a Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 11:29:04 +0800
Subject: [PATCH 2/9] Generify functions

---
 .../net.mamoe.mirai/utils/ExternalImage.kt    |  3 +-
 .../kotlin/net.mamoe.mirai/utils/channels.kt  | 33 ++++++++++++++
 .../net/mamoe/mirai/message/MessagePacket.kt  | 45 +++++++++++++------
 .../net/mamoe/mirai/message/MessageReceipt.kt | 10 +----
 .../mamoe/mirai/message/SendImageUtilsJvm.kt  | 43 +++++++++---------
 5 files changed, 90 insertions(+), 44 deletions(-)

diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
index 527c76ec5..59a07668c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
@@ -20,6 +20,7 @@ import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Image
+import net.mamoe.mirai.message.data.OfflineImage
 import net.mamoe.mirai.message.data.sendTo
 import net.mamoe.mirai.utils.io.toUHexString
 
@@ -159,7 +160,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
  *
  * @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人
  */
-suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
+suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) {
     is Group -> contact.uploadImage(this)
     is QQ -> contact.uploadImage(this)
     else -> error("unreachable")
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
index cc0bcfef3..e2c480e16 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
@@ -15,6 +15,8 @@ package net.mamoe.mirai.utils
 
 
 import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.ByteWriteChannel
+import io.ktor.utils.io.close
 import io.ktor.utils.io.core.Output
 import io.ktor.utils.io.core.writeFully
 import io.ktor.utils.io.pool.useInstance
@@ -52,6 +54,19 @@ suspend fun ByteReadChannel.copyTo(dst: Output) {
     }
 }
 
+/**
+ * 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst]
+ */
+suspend fun ByteReadChannel.copyTo(dst: ByteWriteChannel) {
+    @UseExperimental(MiraiInternalAPI::class)
+    ByteArrayPool.useInstance { buffer ->
+        var size: Int
+        while (this.readAvailable(buffer).also { size = it } > 0) {
+            dst.writeFully(buffer, 0, size)
+        }
+    }
+}
+
 
 /* // 垃圾 kotlin, Unresolved reference: ByteWriteChannel
 /**
@@ -104,6 +119,24 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
     }
 }
 
+/**
+ * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
+ */
+suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
+    try {
+        @UseExperimental(MiraiInternalAPI::class)
+        ByteArrayPool.useInstance { buffer ->
+            var size: Int
+            while (this.readAvailable(buffer).also { size = it } > 0) {
+                dst.writeFully(buffer, 0, size)
+            }
+        }
+    } finally {
+        @Suppress("DuplicatedCode")
+        dst.close()
+    }
+}
+
 /*// 垃圾 kotlin, Unresolved reference: ByteWriteChannel
 /**
  * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
index 60faf7d90..e36d3bd19 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
@@ -11,7 +11,9 @@
 
 package net.mamoe.mirai.message
 
+import io.ktor.utils.io.ByteWriteChannel
 import io.ktor.utils.io.core.Input
+import io.ktor.utils.io.core.Output
 import io.ktor.utils.io.core.use
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.QQ
@@ -41,17 +43,15 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
     // endregion
 
     // region 发送图片
-    suspend inline fun sendImage(image: BufferedImage) = subject.sendImage(image)
-
-    suspend inline fun sendImage(image: URL) = subject.sendImage(image)
-    suspend inline fun sendImage(image: Input) = subject.sendImage(image)
-    suspend inline fun sendImage(image: InputStream) = subject.sendImage(image)
-    suspend inline fun sendImage(image: File) = subject.sendImage(image)
+    suspend inline fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
+    suspend inline fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
+    suspend inline fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
+    suspend inline fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
+    suspend inline fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
     // endregion
 
     // region 上传图片 (扩展)
     suspend inline fun BufferedImage.upload(): Image = upload(subject)
-
     suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
     suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
     suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
@@ -59,12 +59,11 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
     // endregion 上传图片 (扩展)
 
     // region 发送图片 (扩展)
-    suspend inline fun BufferedImage.send() = sendTo(subject)
-
-    suspend inline fun URL.sendAsImage() = sendAsImageTo(subject)
-    suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
-    suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)
-    suspend inline fun File.sendAsImage() = sendAsImageTo(subject)
+    suspend inline fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
+    suspend inline fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+    suspend inline fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+    suspend inline fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+    suspend inline fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
     // endregion 发送图片 (扩展)
 
     // region 下载图片 (扩展)
@@ -80,6 +79,26 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
      */
     suspend inline fun Image.downloadAndClose(output: OutputStream) = channel().copyAndClose(output)
 
+    /**
+     * 下载图片到 [output] 但不关闭这个 [output]
+     */
+    suspend inline fun Image.downloadTo(output: Output) = channel().copyTo(output)
+
+    /**
+     * 下载图片到 [output] 并关闭这个 [output]
+     */
+    suspend inline fun Image.downloadAndClose(output: Output) = channel().copyAndClose(output)
+
+    /**
+     * 下载图片到 [output] 但不关闭这个 [output]
+     */
+    suspend inline fun Image.downloadTo(output: ByteWriteChannel) = channel().copyTo(output)
+
+    /**
+     * 下载图片到 [output] 并关闭这个 [output]
+     */
+    suspend inline fun Image.downloadAndClose(output: ByteWriteChannel) = channel().copyAndClose(output)
+
     /*
     suspend inline fun Image.downloadAsStream(): InputStream = channel().asInputStream()
     suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { downloadAsStream().toExternalImage() }
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
index 3d708e3ed..0e07a3372 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
@@ -59,15 +59,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
     actual suspend fun recall() {
         @Suppress("BooleanLiteralArgument")
         if (_isRecalled.compareAndSet(false, true)) {
-            when (val contact = target) {
-                is Group -> {
-                    contact.bot.recall(source)
-                }
-                is QQ -> {
-                    TODO()
-                }
-                else -> error("Unknown contact type")
-            }
+            target.bot.recall(source)
         } else error("message is already or planned to be recalled")
     }
 
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
index 4da850fcc..63e246166 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
@@ -16,6 +16,7 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.data.Image
+import net.mamoe.mirai.message.data.OfflineImage
 import net.mamoe.mirai.utils.OverFileSizeMaxException
 import net.mamoe.mirai.utils.sendTo
 import net.mamoe.mirai.utils.toExternalImage
@@ -36,37 +37,37 @@ import java.net.URL
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun BufferedImage.sendTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> BufferedImage.sendTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun InputStream.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun File.sendAsImageTo(contact: Contact) {
+suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
     require(this.exists() && this.canRead())
-    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+    return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 }
 
 // endregion
@@ -78,35 +79,35 @@ suspend fun File.sendAsImageTo(contact: Contact) {
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun BufferedImage.upload(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun BufferedImage.upload(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun URL.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun URL.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun Input.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun Input.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun InputStream.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun File.uploadAsImage(contact: Contact): Image {
+suspend fun File.uploadAsImage(contact: Contact): OfflineImage {
     require(this.exists() && this.canRead())
     return withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 }
@@ -120,35 +121,35 @@ suspend fun File.uploadAsImage(contact: Contact): Image {
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(bufferedImage: BufferedImage) = bufferedImage.sendTo(this)
+suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> = bufferedImage.sendTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageUrl: URL): MessageReceipt<C> = imageUrl.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<C> = imageInput.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> = imageStream.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(file: File): MessageReceipt<C> = file.sendAsImageTo(this)
 
 // endregion
 
@@ -159,34 +160,34 @@ suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): Image = bufferedImage.upload(this)
+suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): OfflineImage = bufferedImage.upload(this)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(imageUrl: URL): Image = imageUrl.uploadAsImage(this)
+suspend inline fun Contact.uploadImage(imageUrl: URL): OfflineImage = imageUrl.uploadAsImage(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(imageInput: Input): Image = imageInput.uploadAsImage(this)
+suspend inline fun Contact.uploadImage(imageInput: Input): OfflineImage = imageInput.uploadAsImage(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(imageStream: InputStream): Image = imageStream.uploadAsImage(this)
+suspend inline fun Contact.uploadImage(imageStream: InputStream): OfflineImage = imageStream.uploadAsImage(this)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片上传, 但不发送
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(file: File): Image = file.uploadAsImage(this)
+suspend inline fun Contact.uploadImage(file: File): OfflineImage = file.uploadAsImage(this)
 
 // endregion
\ No newline at end of file

From b3c4f24e333fe2eea6e050957109e4f8762fe5f0 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 18:48:07 +0800
Subject: [PATCH 3/9] Revert "Use `io.ktor.utils.io` than `kotlinx.io`"

This reverts commit 6a85769e
---
 .../net/mamoe/mirai/qqandroid/ContactImpl.kt  |   2 +-
 .../mirai/qqandroid/io/serialization/Jce.kt   |   4 +-
 .../mirai/qqandroid/io/serialization/utils.kt |   5 +-
 .../mamoe/mirai/qqandroid/message/messages.kt |   5 +-
 .../qqandroid/network/QQAndroidClient.kt      |   4 +-
 .../network/highway/HighwayHelper.kt          |  10 +-
 .../qqandroid/network/highway/highway.kt      |   5 +-
 .../network/protocol/data/proto/Cmd0x857.kt   | 408 ------------------
 .../network/protocol/packet/EncryptMethod.kt  |   5 +-
 .../protocol/packet/OutgoingPacketAndroid.kt  |  19 +-
 .../network/protocol/packet/PacketFactory.kt  |   4 +-
 .../qqandroid/network/protocol/packet/Tlv.kt  |   5 +-
 .../protocol/packet/chat/PbMessageSvc.kt      |   2 +-
 .../protocol/packet/chat/TroopManagement.kt   |   7 +-
 .../protocol/packet/chat/image/ImgStore.kt    |   2 +-
 .../protocol/packet/chat/image/LongConn.kt    |   2 +-
 .../packet/chat/receive/MessageSvc.kt         |   3 +-
 .../protocol/packet/list/FriendList.kt        |   2 +-
 .../protocol/packet/login/ConfigPushSvc.kt    |   2 +-
 .../network/protocol/packet/login/StatSvc.kt  |   2 +-
 .../network/protocol/packet/login/WtLogin.kt  |   9 +-
 .../src/commonTest/kotlin/test/printing.kt    |  10 +-
 .../JceDecoderTest.kt                         |   2 +-
 .../kotlin/test/ProtoBufDataClassGenerator.kt |   2 +-
 .../mamoe/mirai/utils/ExternalImageAndroid.kt |   1 +
 .../net/mamoe/mirai/utils/SystemDeviceInfo.kt |   2 +-
 .../io/PlatformDatagramChannelAndroid.kt      |   8 +-
 .../mamoe/mirai/utils/io/PlatformSocket.kt    |  10 +-
 .../net/mamoe/mirai/utils/platformAndroid.kt  |   2 +-
 .../kotlin/net.mamoe.mirai/BotAccount.kt      |   2 +-
 .../kotlin/net.mamoe.mirai/data/ImageLink.kt  |   4 +-
 .../net.mamoe.mirai/event/events/BotEvents.kt |   1 -
 .../net.mamoe.mirai/utils/ExternalImage.kt    |   4 +-
 .../kotlin/net.mamoe.mirai/utils/channels.kt  |   7 +-
 .../net.mamoe.mirai/utils/cryptor/TEA.kt      |   5 +-
 .../utils/io/PlatformDatagramChannel.kt       |   4 +-
 .../utils/io/PlatformSocket.kt                |   4 +-
 .../net.mamoe.mirai/utils/io/byteArrays.kt    |  10 +-
 .../net.mamoe.mirai/utils/io/chunked.kt       |   8 +-
 .../net.mamoe.mirai/utils/io/conversion.kt    |  12 +
 .../kotlin/net.mamoe.mirai/utils/io/output.kt |   2 +-
 .../kotlin/net.mamoe.mirai/utils/platform.kt  |   2 +-
 .../net/mamoe/mirai/message/MessagePacket.kt  |   4 +-
 .../mamoe/mirai/message/SendImageUtilsJvm.kt  |   2 +-
 .../mamoe/mirai/utils/BotConfigurationJvm.kt  |   2 +-
 .../net/mamoe/mirai/utils/ExternalImageJvm.kt |  10 +-
 .../net/mamoe/mirai/utils/PlatformUtilsJvm.kt |   2 +-
 .../net/mamoe/mirai/utils/SystemDeviceInfo.kt |   2 +-
 .../mamoe/mirai/utils/io/PlatformSocket.kt    |  10 +-
 .../mamoe/mirai/utils/io/PlatformSocketJvm.kt |   8 +-
 50 files changed, 132 insertions(+), 517 deletions(-)
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
index b1ee9c6fd..9580ed3e0 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
@@ -9,9 +9,9 @@
 
 package net.mamoe.mirai.qqandroid
 
-import io.ktor.utils.io.core.Closeable
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withTimeoutOrNull
+import kotlinx.io.core.Closeable
 import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.*
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
index 87ec126c1..bf381f7ea 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
@@ -9,8 +9,8 @@
 
 package net.mamoe.mirai.qqandroid.io.serialization
 
-import io.ktor.utils.io.charsets.Charset
-import io.ktor.utils.io.core.*
+import kotlinx.io.charsets.Charset
+import kotlinx.io.core.*
 import kotlinx.serialization.*
 import kotlinx.serialization.internal.*
 import kotlinx.serialization.modules.EmptyModule
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/utils.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/utils.kt
index 3b3856cb7..fe8e46dd8 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/utils.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/utils.kt
@@ -12,10 +12,7 @@
 
 package net.mamoe.mirai.qqandroid.io.serialization
 
-import io.ktor.utils.io.core.BytePacketBuilder
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.readBytes
-import io.ktor.utils.io.core.writeFully
+import kotlinx.io.core.*
 import kotlinx.serialization.DeserializationStrategy
 import kotlinx.serialization.SerialDescriptor
 import kotlinx.serialization.SerializationStrategy
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
index e23516c3b..bfa2ac150 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
@@ -9,7 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.message
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.discardExact
+import kotlinx.io.core.readBytes
+import kotlinx.io.core.readUInt
 import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.message.data.*
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
index 76df1763d..9a507ab35 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
@@ -11,9 +11,9 @@
 
 package net.mamoe.mirai.qqandroid.network
 
-import io.ktor.utils.io.core.*
 import kotlinx.atomicfu.AtomicInt
 import kotlinx.atomicfu.atomic
+import kotlinx.io.core.*
 import net.mamoe.mirai.BotAccount
 import net.mamoe.mirai.RawAccountIdUse
 import net.mamoe.mirai.data.OnlineStatus
@@ -315,7 +315,7 @@ internal class Pt4Token(data: ByteArray, creationTime: Long, expireTime: Long) :
 internal typealias PSKeyMap = MutableMap<String, PSKey>
 internal typealias Pt4TokenMap = MutableMap<String, Pt4Token>
 
-internal inline fun Input.readUShortLVString(): String = io.ktor.utils.io.core.String(this.readUShortLVByteArray())
+internal inline fun Input.readUShortLVString(): String = kotlinx.io.core.String(this.readUShortLVByteArray())
 
 internal inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
index 61dd755c2..c219a9881 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
@@ -18,14 +18,14 @@ import io.ktor.http.content.OutgoingContent
 import io.ktor.http.userAgent
 import io.ktor.utils.io.ByteReadChannel
 import io.ktor.utils.io.copyAndClose
-import io.ktor.utils.io.core.Input
-import io.ktor.utils.io.core.readAvailable
-import io.ktor.utils.io.core.readInt
-import io.ktor.utils.io.core.use
-import io.ktor.utils.io.pool.useInstance
 import kotlinx.coroutines.InternalCoroutinesApi
 import kotlinx.coroutines.flow.collect
 import kotlinx.io.InputStream
+import kotlinx.io.core.Input
+import kotlinx.io.core.discardExact
+import kotlinx.io.core.readAvailable
+import kotlinx.io.core.use
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
 import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
index cf13c882a..b84834661 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
@@ -12,10 +12,13 @@
 package net.mamoe.mirai.qqandroid.network.highway
 
 import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.core.*
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 import kotlinx.io.InputStream
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.writeFully
 import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
 import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt
deleted file mode 100644
index 2586189c4..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-@file:Suppress("SpellCheckingInspection")
-
-package net.mamoe.mirai.qqandroid.network.protocol.data.proto
-
-import kotlinx.serialization.SerialId
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.protobuf.ProtoNumberType
-import kotlinx.serialization.protobuf.ProtoType
-import net.mamoe.mirai.qqandroid.io.ProtoBuf
-import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
-
-class GroupOpenSysMsg : ProtoBuf {
-    @Serializable
-    class LightApp(
-        @SerialId(1) val app: String = "",
-        @SerialId(2) val view: String = "",
-        @SerialId(3) val desc: String = "",
-        @SerialId(4) val prompt: String = "",
-        @SerialId(5) val ver: String = "",
-        @SerialId(6) val meta: String = "",
-        @SerialId(7) val config: String = "",
-        @SerialId(8) val source: Source? = null
-    ) : ProtoBuf
-
-    @Serializable
-    class RichMsg(
-        @SerialId(1) val title: String = "",
-        @SerialId(2) val desc: String = "",
-        @SerialId(3) val brief: String = "",
-        @SerialId(4) val cover: String = "",
-        @SerialId(5) val url: String = "",
-        @SerialId(6) val source: Source? = null
-    ) : ProtoBuf
-
-    @Serializable
-    class Sender(
-        @SerialId(1) val uin: Long = 0L,
-        @SerialId(2) val nick: String = "",
-        @SerialId(3) val avatar: String = "",
-        @SerialId(4) val url: String = ""
-    ) : ProtoBuf
-
-    @Serializable
-    class Source(
-        @SerialId(1) val name: String = "",
-        @SerialId(2) val icon: String = "",
-        @SerialId(3) val url: String = ""
-    ) : ProtoBuf
-
-    @Serializable
-    class SysMsgBody(
-        @SerialId(1) val groupId: Long = 0L,
-        @SerialId(2) val appid: Long = 0L,
-        @SerialId(3) val sender: Sender? = null,
-        @SerialId(4) val msgType: Int = 0,
-        @SerialId(5) val content: String = "",
-        @SerialId(6) val richMsg: RichMsg? = null,
-        @SerialId(7) val lightApp: LightApp? = null
-    ) : ProtoBuf
-}
-
-@Serializable
-class TroopTips0x857 : ProtoBuf {
-    @Serializable
-    class AIOGrayTipsInfo(
-        @SerialId(1) val optUint32ShowLastest: Int = 0,
-        @SerialId(2) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val optUint32Remind: Int = 0,
-        @SerialId(4) val optBytesBrief: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val receiverUin: Long = 0L,
-        @SerialId(6) val reliaoAdminOpt: Int = 0,
-        @SerialId(7) val robotGroupOpt: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class AIOTopTipsInfo(
-        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val optUint32Icon: Int = 0,
-        @SerialId(3) val optEnumAction: Int /* enum */ = 1,
-        @SerialId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val optBytesData: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val optBytesDataI: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(7) val optBytesDataA: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(8) val optBytesDataP: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class FloatedTipsInfo(
-        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class GeneralGrayTipInfo(
-        @SerialId(1) val busiType: Long = 0L,
-        @SerialId(2) val busiId: Long = 0L,
-        @SerialId(3) val ctrlFlag: Int = 0,
-        @SerialId(4) val c2cType: Int = 0,
-        @SerialId(5) val serviceType: Int = 0,
-        @SerialId(6) val templId: Long = 0L,
-        @SerialId(7) val msgTemplParam: List<TemplParam>? = null,
-        @SerialId(8) val content: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(10) val tipsSeqId: Long = 0L,
-        @SerialId(100) val pbReserv: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class GoldMsgTipsElem(
-        @SerialId(1) val type: Int = 0,
-        @SerialId(2) val billno: String = "",
-        @SerialId(3) val result: Int = 0,
-        @SerialId(4) val amount: Int = 0,
-        @SerialId(5) val total: Int = 0,
-        @SerialId(6) val interval: Int = 0,
-        @SerialId(7) val finish: Int = 0,
-        @SerialId(8) val uin: List<Long>? = null,
-        @SerialId(9) val action: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class GroupInfoChange(
-        @SerialId(1) val groupHonorSwitch: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class GroupNotifyInfo(
-        @SerialId(1) val optUint32AutoPullFlag: Int = 0,
-        @SerialId(2) val optBytesFeedsId: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class InstCtrl(
-        @SerialId(1) val msgSendToInst: List<InstInfo>? = null,
-        @SerialId(2) val msgExcludeInst: List<InstInfo>? = null,
-        @SerialId(3) val msgFromInst: InstInfo? = null
-    ) : ProtoBuf
-
-    @Serializable
-    class InstInfo(
-        @SerialId(1) val apppid: Int = 0,
-        @SerialId(2) val instid: Int = 0,
-        @SerialId(3) val platform: Int = 0,
-        @SerialId(4) val openAppid: Int = 0,
-        @SerialId(5) val productid: Int = 0,
-        @SerialId(6) val ssoBid: Int = 0,
-        @SerialId(7) val guid: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(8) val verMin: Int = 0,
-        @SerialId(9) val verMax: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class LbsShareChangePushInfo(
-        @SerialId(1) val msgType: Int = 0,
-        @SerialId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(4) val groupId: Long = 0L,
-        @SerialId(5) val operUin: Long = 0L,
-        @SerialId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(7) val msgSeq: Long = 0L,
-        @SerialId(8) val joinNums: Int = 0,
-        @SerialId(99) val pushType: Int = 0,
-        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class LuckyBagNotify(
-        @SerialId(1) val msgTips: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class MediaChangePushInfo(
-        @SerialId(1) val msgType: Int = 0,
-        @SerialId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(4) val groupId: Long = 0L,
-        @SerialId(5) val operUin: Long = 0L,
-        @SerialId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(7) val msgSeq: Long = 0L,
-        @SerialId(8) val joinNums: Int = 0,
-        @SerialId(9) val msgPerSetting: PersonalSetting? = null,
-        @SerialId(10) val playMode: Int = 0,
-        @SerialId(99) val mediaType: Int = 0,
-        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf {
-        @Serializable
-        class PersonalSetting(
-            @SerialId(1) val themeId: Int = 0,
-            @SerialId(2) val playerId: Int = 0,
-            @SerialId(3) val fontId: Int = 0
-        ) : ProtoBuf
-    }
-
-    @Serializable
-    class MessageBoxInfo(
-        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val optBytesTitle: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val optBytesButton: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class MessageRecallReminder(
-        @SerialId(1) val uin: Long = 0L,
-        @SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val recalledMsgList: List<MessageMeta> = listOf(),
-        @SerialId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val groupType: Int = 0,
-        @SerialId(7) val opType: Int = 0
-    ) : ProtoBuf {
-        @Serializable
-        class MessageMeta(
-            @SerialId(1) val seq: Int = 0,
-            @SerialId(2) val time: Int = 0,
-            @SerialId(3) val msgRandom: Int = 0,
-            @SerialId(4) val msgType: Int = 0,
-            @SerialId(5) val msgFlag: Int = 0,
-            @SerialId(6) val authorUin: Long = 0L
-        ) : ProtoBuf
-    }
-
-    @Serializable
-    class MiniAppNotify(
-        @SerialId(1) val msg: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class NotifyMsgBody(
-        @SerialId(1) val optEnumType: Int /* enum */ = 1,
-        @SerialId(2) val optUint64MsgTime: Long = 0L,
-        @SerialId(3) val optUint64MsgExpires: Long = 0L,
-        @SerialId(4) val optUint64GroupCode: Long = 0L,
-        @SerialId(5) val optMsgGraytips: AIOGrayTipsInfo? = null,
-        @SerialId(6) val optMsgMessagebox: MessageBoxInfo? = null,
-        @SerialId(7) val optMsgFloatedtips: FloatedTipsInfo? = null,
-        @SerialId(8) val optMsgToptips: AIOTopTipsInfo? = null,
-        @SerialId(9) val optMsgRedtips: RedGrayTipsInfo? = null,
-        @SerialId(10) val optMsgGroupNotify: GroupNotifyInfo? = null,
-        @SerialId(11) val optMsgRecall: MessageRecallReminder? = null,
-        @SerialId(12) val optMsgThemeNotify: ThemeStateNotify? = null,
-        @SerialId(13) val serviceType: Int = 0,
-        @SerialId(14) val optMsgObjmsgUpdate: NotifyObjmsgUpdate? = null,
-        @SerialId(15) val optMsgWerewolfPush: WereWolfPush? = null,
-        // @SerialId(16) val optStcmGameState: ApolloGameStatus.STCMGameMessage? = null,
-        // @SerialId(17) val aplloMsgPush: ApolloPushMsgInfo.STPushMsgElem? = null,
-        @SerialId(18) val optMsgGoldtips: GoldMsgTipsElem? = null,
-        @SerialId(20) val optMsgMiniappNotify: MiniAppNotify? = null,
-        @SerialId(21) val optUint64SenderUin: Long = 0L,
-        @SerialId(22) val optMsgLuckybagNotify: LuckyBagNotify? = null,
-        @SerialId(23) val optMsgTroopformtipsPush: TroopFormGrayTipsInfo? = null,
-        @SerialId(24) val optMsgMediaPush: MediaChangePushInfo? = null,
-        @SerialId(26) val optGeneralGrayTip: GeneralGrayTipInfo? = null,
-        @SerialId(27) val optMsgVideoPush: VideoChangePushInfo? = null,
-        @SerialId(28) val optLbsShareChangePlusInfo: LbsShareChangePushInfo? = null,
-        @SerialId(29) val optMsgSingPush: SingChangePushInfo? = null,
-        @SerialId(30) val optMsgGroupInfoChange: GroupInfoChange? = null
-    ) : ProtoBuf
-
-    @Serializable
-    class NotifyObjmsgUpdate(
-        @SerialId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val updateType: Int = 0,
-        @SerialId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class RedGrayTipsInfo(
-        @SerialId(1) val optUint32ShowLastest: Int = 0,
-        @SerialId(2) val senderUin: Long = 0L,
-        @SerialId(3) val receiverUin: Long = 0L,
-        @SerialId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
-        @ProtoType(ProtoNumberType.SIGNED) @SerialId(7) val sint32Msgtype: Int = 0,
-        @SerialId(8) val luckyFlag: Int = 0,
-        @SerialId(9) val hideFlag: Int = 0,
-        @SerialId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(11) val icon: Int = 0,
-        @SerialId(12) val luckyUin: Long = 0L,
-        @SerialId(13) val time: Int = 0,
-        @SerialId(14) val random: Int = 0,
-        @SerialId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(17) val idiomSeq: Int = 0,
-        @SerialId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class ReqBody(
-        @SerialId(1) val optUint64GroupCode: Long = 0L,
-        @SerialId(2) val uint64Memberuins: List<Long>? = null,
-        @SerialId(3) val optUint32Offline: Int = 0,
-        @SerialId(4) val msgInstCtrl: InstCtrl? = null,
-        @SerialId(5) val optBytesMsg: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val optUint32BusiType: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class RspBody(
-        @SerialId(1) val optUint64GroupCode: Long = 0L
-    ) : ProtoBuf
-
-    @Serializable
-    class SingChangePushInfo(
-        @SerialId(1) val seq: Long = 0L,
-        @SerialId(2) val actionType: Int = 0,
-        @SerialId(3) val groupId: Long = 0L,
-        @SerialId(4) val operUin: Long = 0L,
-        @SerialId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val joinNums: Int = 0
-    ) : ProtoBuf
-
-    @Serializable
-    class TemplParam(
-        @SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val value: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class ThemeStateNotify(
-        @SerialId(1) val state: Int = 0,
-        @SerialId(2) val feedsId: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(3) val themeName: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(4) val actionUin: Long = 0L,
-        @SerialId(5) val createUin: Long = 0L
-    ) : ProtoBuf
-
-    @Serializable
-    class TroopFormGrayTipsInfo(
-        @SerialId(1) val writerUin: Long = 0L,
-        @SerialId(2) val creatorUin: Long = 0L,
-        @SerialId(3) val richContent: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val creatorNick: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class VideoChangePushInfo(
-        @SerialId(1) val seq: Long = 0L,
-        @SerialId(2) val actionType: Int = 0,
-        @SerialId(3) val groupId: Long = 0L,
-        @SerialId(4) val operUin: Long = 0L,
-        @SerialId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val joinNums: Int = 0,
-        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
-    ) : ProtoBuf
-
-    @Serializable
-    class WereWolfPush(
-        @SerialId(1) val pushType: Int = 0,
-        @SerialId(2) val gameRoom: Long = 0L,
-        @SerialId(3) val enumGameState: Int = 0,
-        @SerialId(4) val gameRound: Int = 0,
-        @SerialId(5) val roles: List<Role>? = null,
-        @SerialId(6) val speaker: Long = 0L,
-        @SerialId(7) val judgeUin: Long = 0L,
-        @SerialId(8) val judgeWords: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(9) val enumOperation: Int = 0,
-        @SerialId(10) val srcUser: Long = 0L,
-        @SerialId(11) val dstUser: Long = 0L,
-        @SerialId(12) val deadUsers: List<Long>? = null,
-        @SerialId(13) val gameResult: Int = 0,
-        @SerialId(14) val timeoutSec: Int = 0,
-        @SerialId(15) val killConfirmed: Int = 0,
-        @SerialId(16) val judgeNickname: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(17) val votedTieUsers: List<Long>? = null
-    ) : ProtoBuf {
-        @Serializable
-        class GameRecord(
-            @SerialId(1) val total: Int = 0,
-            @SerialId(2) val win: Int = 0,
-            @SerialId(3) val lose: Int = 0,
-            @SerialId(4) val draw: Int = 0
-        ) : ProtoBuf
-
-        @Serializable
-        class Role(
-            @SerialId(1) val uin: Long = 0L,
-            @SerialId(2) val enumType: Int = 0,
-            @SerialId(3) val enumState: Int = 0,
-            @SerialId(4) val canSpeak: Int = 0,
-            @SerialId(5) val canListen: Int = 0,
-            @SerialId(6) val position: Int = 0,
-            @SerialId(7) val canVote: Int = 0,
-            @SerialId(8) val canVoted: Int = 0,
-            @SerialId(9) val alreadyChecked: Int = 0,
-            @SerialId(10) val alreadySaved: Int = 0,
-            @SerialId(11) val alreadyPoisoned: Int = 0,
-            @SerialId(12) val playerState: Int = 0,
-            @SerialId(13) val enumDeadOp: Int = 0,
-            @SerialId(14) val enumOperation: Int = 0,
-            @SerialId(15) val dstUser: Long = 0L,
-            @SerialId(16) val operationRound: Int = 0,
-            @SerialId(17) val msgGameRecord: GameRecord? = null,
-            @SerialId(18) val isWerewolf: Int = 0,
-            @SerialId(19) val defendedUser: Long = 0L,
-            @SerialId(20) val isSheriff: Int = 0
-        ) : ProtoBuf
-    }
-}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
index 697028560..80d0d6141 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/EncryptMethod.kt
@@ -9,7 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.BytePacketBuilder
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.writeFully
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
 import net.mamoe.mirai.utils.cryptor.ECDH
 import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
index 45485aa7a..f13ec933d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
@@ -10,7 +10,10 @@
 package net.mamoe.mirai.qqandroid.network.protocol.packet
 
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.BytePacketBuilder
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.writeFully
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.io.encryptAndWrite
@@ -43,7 +46,6 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
     body: BytePacketBuilder.(sequenceId: Int) -> Unit
 ): OutgoingPacket {
 
-    @Suppress("DuplicatedCode")
     return OutgoingPacket(name, commandName, sequenceId, buildPacket {
         writeIntLVPacket(lengthOffset = { it + 4 }) {
             writeInt(0x0B)
@@ -52,7 +54,7 @@ internal inline fun OutgoingPacketFactory<*>.buildOutgoingUniPacket(
             writeByte(0)
             client.uin.toString().let {
                 writeInt(it.length + 4)
-                writeText(it)
+                writeStringUtf8(it)
             }
             encryptAndWrite(key) {
                 writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
@@ -75,7 +77,6 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
     sequenceId: Int = client.nextSsoSequenceId(),
     body: BytePacketBuilder.(sequenceId: Int) -> Unit
 ): OutgoingPacket {
-    @Suppress("DuplicatedCode")
     return OutgoingPacket(name, commandName, sequenceId, buildPacket {
         writeIntLVPacket(lengthOffset = { it + 4 }) {
             writeInt(0x0B)
@@ -84,7 +85,7 @@ internal inline fun IncomingPacketFactory<*>.buildResponseUniPacket(
             writeByte(0)
             client.uin.toString().let {
                 writeInt(it.length + 4)
-                writeText(it)
+                writeStringUtf8(it)
             }
             encryptAndWrite(key) {
                 writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) {
@@ -105,7 +106,7 @@ internal inline fun BytePacketBuilder.writeUniPacket(
     writeIntLVPacket(lengthOffset = { it + 4 }) {
         commandName.let {
             writeInt(it.length + 4)
-            writeText(it)
+            writeStringUtf8(it)
         }
 
         writeInt(4 + 4)
@@ -152,7 +153,7 @@ internal inline fun OutgoingPacketFactory<*>.buildLoginOutgoingPacket(
 
             client.uin.toString().let {
                 writeInt(it.length + 4)
-                writeText(it)
+                writeStringUtf8(it)
             }
 
             if (key === NO_ENCRYPT) {
@@ -207,7 +208,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
         }
         commandName.let {
             writeInt(it.length + 4)
-            writeText(it)
+            writeStringUtf8(it)
         }
 
         writeInt(4 + 4)
@@ -215,7 +216,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
 
         client.device.imei.let {
             writeInt(it.length + 4)
-            writeText(it)
+            writeStringUtf8(it)
         }
 
         writeInt(4)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
index 1f62d375b..020a45d2d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
@@ -9,8 +9,8 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet
 
-import io.ktor.utils.io.core.*
-import io.ktor.utils.io.pool.useInstance
+import kotlinx.io.core.*
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.qqandroid.QQAndroidBot
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
index 0e83f4817..7f5d44ac1 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
@@ -9,7 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.BytePacketBuilder
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.toByteArray
+import kotlinx.io.core.writeFully
 import net.mamoe.mirai.qqandroid.network.protocol.LoginType
 import net.mamoe.mirai.qqandroid.utils.NetworkType
 import net.mamoe.mirai.utils.currentTimeMillis
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
index 7cfb9fc93..71e593cac 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/PbMessageSvc.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
index 50faee7e5..b958354d7 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
@@ -9,7 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.readBytes
+import kotlinx.io.core.toByteArray
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.data.Packet
@@ -375,7 +378,7 @@ internal class TroopManagement {
         OutgoingPacketFactory<EditGroupNametag.Response>("friendlist.ModifyGroupCardReq") {
         object Response : Packet
 
-        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
+        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): EditGroupNametag.Response {
             return Response
         }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/ImgStore.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/ImgStore.kt
index 6b46fc0e2..6ac6e0b69 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/ImgStore.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/ImgStore.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/LongConn.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/LongConn.kt
index 743f3e103..b4cf13881 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/LongConn.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/image/LongConn.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
index 38a148d35..0a83fb747 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
@@ -9,9 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
 
-import io.ktor.utils.io.core.ByteReadPacket
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.*
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.discardExact
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.data.MemberInfo
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendList.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendList.kt
index 0c218bf7c..f2ab4eef9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendList.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendList.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.list
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
index 0ea8c4ec5..1341f7f7b 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.JceCharset
 import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
index f46297e34..c513076b4 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
-import io.ktor.utils.io.core.ByteReadPacket
+import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.*
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
index 36d7b5857..6ee16aa42 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
@@ -11,7 +11,7 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
 
 import io.ktor.util.InternalAPI
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.*
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.network.*
@@ -288,7 +288,7 @@ internal class WtLogin {
                 }
 
                 class Picture(
-                    val data: ByteArray,
+                    val data: IoBuffer,
                     val sign: ByteArray
                 ) : Captcha() {
                     override fun toString(): String = "LoginPacketResponse.Captcha.Picture"
@@ -394,8 +394,11 @@ internal class WtLogin {
                 imageData.discardExact(2)//image Length
                 val sign = imageData.readBytes(signInfoLength.toInt())
 
+
+                val buffer = IoBuffer.Pool.borrow()
+                imageData.readAvailable(buffer)
                 return LoginPacketResponse.Captcha.Picture(
-                    data = imageData.readBytes(),
+                    data = buffer,
                     sign = sign
                 )
                 // } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString())
diff --git a/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt b/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
index f2fc92dac..043d67d47 100644
--- a/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
+++ b/mirai-core-qqandroid/src/commonTest/kotlin/test/printing.kt
@@ -11,11 +11,11 @@
 
 package test
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Input
-import io.ktor.utils.io.core.readAvailable
-import io.ktor.utils.io.core.use
-import io.ktor.utils.io.pool.useInstance
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
+import kotlinx.io.core.readAvailable
+import kotlinx.io.core.use
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
index 2f67a7195..a782d38f3 100644
--- a/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
+++ b/mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
@@ -11,7 +11,7 @@ package net.mamoe.mirai.qqandroid.io.serialization
 
 /*
 
-import io.ktor.utils.io.core.readBytes
+import kotlinx.io.core.readBytes
 import kotlinx.serialization.SerialId
 import kotlinx.serialization.Serializable
 import net.mamoe.mirai.qqandroid.io.JceOutput
diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt
index 860aa0b78..b456838cb 100644
--- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt
+++ b/mirai-core-qqandroid/src/jvmTest/kotlin/test/ProtoBufDataClassGenerator.kt
@@ -28,7 +28,7 @@ fun main() {
     println(
         File(
             """
-            E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\oidb\cmd0x857
+            E:\Projects\QQAndroidFF\app\src\main\java\tencent\im\msgrevoke
         """.trimIndent()
         )
             .generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n")
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
index 328af2a50..06eaee34e 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
@@ -53,6 +53,7 @@ fun File.toExternalImage(): ExternalImage {
         md5 = this.inputStream().use { it.md5() },
         imageFormat = this.nameWithoutExtension,
         input = this.inputStream(),
+        inputSize = this.length(),
         filename = this.name
     )
 }
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
index 88b572239..11990fa36 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
@@ -13,7 +13,7 @@ import android.annotation.SuppressLint
 import android.net.wifi.WifiManager
 import android.os.Build
 import android.telephony.TelephonyManager
-import io.ktor.utils.io.core.toByteArray
+import kotlinx.io.core.toByteArray
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.Transient
 import kotlinx.serialization.UnstableDefault
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
index 801a19299..ed02843f3 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformDatagramChannelAndroid.kt
@@ -9,12 +9,12 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
-import io.ktor.utils.io.nio.readPacketAtMost
-import io.ktor.utils.io.nio.writePacket
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
+import kotlinx.io.nio.readPacketAtMost
+import kotlinx.io.nio.writePacket
 import java.net.InetSocketAddress
 import java.nio.channels.DatagramChannel
 import java.nio.channels.ReadableByteChannel
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index 063565c1d..f6c1d2513 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -9,13 +9,13 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
-import io.ktor.utils.io.core.ExperimentalIoApi
-import io.ktor.utils.io.streams.readPacketAtMost
-import io.ktor.utils.io.streams.writePacket
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
+import kotlinx.io.core.ExperimentalIoApi
+import kotlinx.io.streams.readPacketAtMost
+import kotlinx.io.streams.writePacket
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import java.io.BufferedInputStream
 import java.io.BufferedOutputStream
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
index 9adb9aa12..8430ce7e8 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/platformAndroid.kt
@@ -12,7 +12,7 @@ package net.mamoe.mirai.utils
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.cio.CIO
 import io.ktor.util.KtorExperimentalAPI
-import io.ktor.utils.io.pool.useInstance
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.io.ByteArrayPool
 import java.io.ByteArrayOutputStream
 import java.io.DataInput
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt
index b4c84333a..1f0a2ebe6 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotAccount.kt
@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai
 
-import io.ktor.utils.io.core.toByteArray
+import kotlinx.io.core.toByteArray
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.md5
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/ImageLink.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/ImageLink.kt
index 713ff08a1..3cef0c4a3 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/ImageLink.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/data/ImageLink.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.data
 
 import io.ktor.client.request.get
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.readBytes
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.readBytes
 import net.mamoe.mirai.utils.Http
 
 interface ImageLink {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
index 64a6b9e5e..ac2c51b43 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
@@ -21,7 +21,6 @@ import net.mamoe.mirai.event.events.ImageUploadEvent.Failed
 import net.mamoe.mirai.event.events.ImageUploadEvent.Succeed
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.message.data.MessageSource
 import net.mamoe.mirai.utils.ExternalImage
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
index 59a07668c..b26e7163e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
@@ -12,9 +12,9 @@
 package net.mamoe.mirai.utils
 
 import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Input
 import kotlinx.io.InputStream
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.QQ
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
index e2c480e16..c544f09c4 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
@@ -15,13 +15,10 @@ package net.mamoe.mirai.utils
 
 
 import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.ByteWriteChannel
-import io.ktor.utils.io.close
-import io.ktor.utils.io.core.Output
-import io.ktor.utils.io.core.writeFully
-import io.ktor.utils.io.pool.useInstance
 import io.ktor.utils.io.readAvailable
 import kotlinx.io.OutputStream
+import kotlinx.io.core.Output
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.io.ByteArrayPool
 import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
index 4a98d4441..3e43b3fde 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/TEA.kt
@@ -9,9 +9,8 @@
 
 package net.mamoe.mirai.utils.cryptor
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.readFully
-import io.ktor.utils.io.pool.useInstance
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.io.ByteArrayPool
 import net.mamoe.mirai.utils.io.toByteArray
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
index 057670f01..9e4b98f1a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformDatagramChannel.kt
@@ -9,8 +9,8 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
 import net.mamoe.mirai.utils.MiraiInternalAPI
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
index 24a6d7d86..70b411fae 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/PlatformSocket.kt
@@ -9,8 +9,8 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
 import net.mamoe.mirai.utils.MiraiInternalAPI
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt
index 970a71a7d..6a191ce29 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/byteArrays.kt
@@ -13,11 +13,11 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.charsets.Charset
-import io.ktor.utils.io.charsets.Charsets
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.String
-import io.ktor.utils.io.core.use
+import kotlinx.io.charsets.Charset
+import kotlinx.io.charsets.Charsets
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.String
+import kotlinx.io.core.use
 import net.mamoe.mirai.utils.checkOffsetAndLength
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
index e9c3a0dfc..fc0b54515 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
@@ -10,16 +10,14 @@
 package net.mamoe.mirai.utils.io
 
 import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Input
-import io.ktor.utils.io.core.isNotEmpty
-import io.ktor.utils.io.core.readAvailable
-import io.ktor.utils.io.pool.useInstance
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.io.InputStream
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.MiraiInternalAPI
 
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
index dfd11e4df..c47f22772 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/conversion.kt
@@ -11,6 +11,8 @@
 
 package net.mamoe.mirai.utils.io
 
+import kotlinx.io.core.IoBuffer
+import kotlinx.io.pool.ObjectPool
 import kotlin.random.Random
 import kotlin.random.nextInt
 
@@ -200,3 +202,13 @@ fun ByteArray.toInt(): Int =
     (this[0].toInt().and(255) shl 24) + (this[1].toInt().and(255) shl 16) + (this[2].toInt().and(255) shl 8) + (this[3].toInt().and(
         255
     ) shl 0)
+
+/**
+ * 从 [IoBuffer.Pool] [borrow][ObjectPool.borrow] 一个 [IoBuffer] 然后将 [this] 写入.
+ * 注意回收 ([ObjectPool.recycle])
+ */
+fun ByteArray.toIoBuffer(
+    offset: Int = 0,
+    length: Int = this.size - offset,
+    pool: ObjectPool<IoBuffer> = IoBuffer.Pool
+): IoBuffer = pool.borrow().let { it.writeFully(this, offset, length); it }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
index 837541e2a..671e023b3 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/output.kt
@@ -13,7 +13,7 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.*
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.coerceAtMostOrFail
 import net.mamoe.mirai.utils.cryptor.TEA
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt
index cd9188012..c4b730d91 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/platform.kt
@@ -12,7 +12,7 @@
 package net.mamoe.mirai.utils
 
 import io.ktor.client.HttpClient
-import io.ktor.utils.io.core.toByteArray
+import kotlinx.io.core.toByteArray
 
 /**
  * 时间戳
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
index e36d3bd19..9e2b6c493 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
@@ -12,9 +12,9 @@
 package net.mamoe.mirai.message
 
 import io.ktor.utils.io.ByteWriteChannel
-import io.ktor.utils.io.core.Input
+import kotlinx.io.core.Input
 import io.ktor.utils.io.core.Output
-import io.ktor.utils.io.core.use
+import kotlinx.io.core.use
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.message.data.Image
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
index 63e246166..a28d13071 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
@@ -11,9 +11,9 @@
 
 package net.mamoe.mirai.message
 
-import io.ktor.utils.io.core.Input
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.Input
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.OfflineImage
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
index 8f62dc5a8..3b7726efe 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfigurationJvm.kt
@@ -9,7 +9,6 @@
 
 package net.mamoe.mirai.utils
 
-import io.ktor.utils.io.core.use
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.GlobalScope
@@ -21,6 +20,7 @@ import kotlinx.coroutines.io.writeFully
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.use
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.network.BotNetworkHandler
 import java.awt.Image
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
index f6b9f8441..4f500aac4 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
@@ -12,13 +12,13 @@
 package net.mamoe.mirai.utils
 
 import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.core.Input
-import io.ktor.utils.io.core.buildPacket
-import io.ktor.utils.io.core.copyTo
-import io.ktor.utils.io.errors.IOException
-import io.ktor.utils.io.streams.asOutput
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.Input
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.copyTo
+import kotlinx.io.errors.IOException
+import kotlinx.io.streams.asOutput
 import net.mamoe.mirai.utils.io.getRandomString
 import java.awt.image.BufferedImage
 import java.io.File
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
index 49472129a..46d10fe8c 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt
@@ -13,7 +13,7 @@ package net.mamoe.mirai.utils
 
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.cio.CIO
-import io.ktor.utils.io.pool.useInstance
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.utils.io.ByteArrayPool
 import java.io.*
 import java.net.InetAddress
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
index e71cbbfdd..8bcbf6f42 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SystemDeviceInfo.kt
@@ -9,7 +9,7 @@
 
 package net.mamoe.mirai.utils
 
-import io.ktor.utils.io.core.toByteArray
+import kotlinx.io.core.toByteArray
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.Transient
 import kotlinx.serialization.UnstableDefault
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
index 063565c1d..f6c1d2513 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocket.kt
@@ -9,13 +9,13 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
-import io.ktor.utils.io.core.ExperimentalIoApi
-import io.ktor.utils.io.streams.readPacketAtMost
-import io.ktor.utils.io.streams.writePacket
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
+import kotlinx.io.core.ExperimentalIoApi
+import kotlinx.io.streams.readPacketAtMost
+import kotlinx.io.streams.writePacket
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import java.io.BufferedInputStream
 import java.io.BufferedOutputStream
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
index db26ec821..1cf48c36f 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/io/PlatformSocketJvm.kt
@@ -9,12 +9,12 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.core.ByteReadPacket
-import io.ktor.utils.io.core.Closeable
-import io.ktor.utils.io.nio.readPacketAtMost
-import io.ktor.utils.io.nio.writePacket
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Closeable
+import kotlinx.io.nio.readPacketAtMost
+import kotlinx.io.nio.writePacket
 import java.net.InetSocketAddress
 import java.nio.channels.DatagramChannel
 import java.nio.channels.ReadableByteChannel

From 6b54abe73076a12905efc02c311ddfc6edc2f508 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 18:49:59 +0800
Subject: [PATCH 4/9] Revert "Use `ktor.io` than `kotlinx.io`"

This reverts commit ffee2e26
---
 mirai-core-qqandroid/build.gradle.kts         |  2 +
 .../packet/chat/receive/MessageSvc.kt         | 57 +++++++++++++++++++
 mirai-core/build.gradle.kts                   |  5 ++
 .../mamoe/mirai/utils/ExternalImageAndroid.kt | 17 +++---
 .../kotlin/mirai/test/KReflectionTest.kt      | 24 ++++++++
 .../kotlin/mirai/test/UnixColorText.kt        | 16 ++++++
 .../testCaptchaPacket/TestCaptchaPacket.kt    | 30 ++++++++++
 7 files changed, 143 insertions(+), 8 deletions(-)
 create mode 100644 mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt
 create mode 100644 mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt
 create mode 100644 mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt

diff --git a/mirai-core-qqandroid/build.gradle.kts b/mirai-core-qqandroid/build.gradle.kts
index 7f747935b..d081568a8 100644
--- a/mirai-core-qqandroid/build.gradle.kts
+++ b/mirai-core-qqandroid/build.gradle.kts
@@ -11,6 +11,7 @@ plugins {
 val kotlinVersion: String by rootProject.ext
 val atomicFuVersion: String by rootProject.ext
 val coroutinesVersion: String by rootProject.ext
+val kotlinXIoVersion: String by rootProject.ext
 val coroutinesIoVersion: String by rootProject.ext
 
 
@@ -66,6 +67,7 @@ kotlin {
                 api(kotlin("stdlib", kotlinVersion))
 
                 api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
+                api(kotlinx("io", kotlinXIoVersion))
                 api(kotlinx("coroutines-io", coroutinesIoVersion))
                 api(kotlinx("coroutines-core", coroutinesVersion))
             }
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
index 0a83fb747..7e8df470a 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
@@ -9,10 +9,13 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
 
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.*
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.discardExact
+import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.data.MemberInfo
@@ -278,6 +281,60 @@ internal class MessageSvc {
             }
         }
 
+        internal class MessageSourceFromSendFriend(
+            val messageRandom: Int,
+            override val time: Long,
+            override val qqId: Long,
+            override val groupId: Long,
+            val sequenceId: Int
+        ) : MessageSource {
+            @UseExperimental(ExperimentalCoroutinesApi::class)
+            override val id: Long
+                get() = sequenceId.toLong().shl(32) or
+                        messageRandom.toLong().and(0xFFFFFFFF)
+
+            override suspend fun ensureSequenceIdAvailable() {
+                // nothing to do
+            }
+
+            override fun toString(): String {
+                return ""
+            }
+        }
+
+        internal class MessageSourceFromSendGroup(
+            val messageRandom: Int,
+            override val time: Long,
+            override val qqId: Long,
+            override val groupId: Long// ,
+            // override val sourceMessage: MessageChain
+        ) : MessageSource {
+            private lateinit var sequenceIdDeferred: Deferred<Int>
+
+            @UseExperimental(ExperimentalCoroutinesApi::class)
+            override val id: Long
+                get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
+                        messageRandom.toLong().and(0xFFFFFFFF)
+
+            @UseExperimental(MiraiExperimentalAPI::class)
+            fun startWaitingSequenceId(contact: Contact) {
+                sequenceIdDeferred =
+                    contact.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(timeoutMillis = 3000) {
+                        if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
+                            it.sequenceId
+                        } else null
+                    }
+            }
+
+            override suspend fun ensureSequenceIdAvailable() {
+                sequenceIdDeferred.join()
+            }
+
+            override fun toString(): String {
+                return ""
+            }
+        }
+
         inline fun ToFriend(
             client: QQAndroidClient,
             toUin: Long,
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 09dde5fb6..04daba6fa 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -11,6 +11,7 @@ plugins {
 val kotlinVersion: String by rootProject.ext
 val atomicFuVersion: String by rootProject.ext
 val coroutinesVersion: String by rootProject.ext
+val kotlinXIoVersion: String by rootProject.ext
 val coroutinesIoVersion: String by rootProject.ext
 
 
@@ -65,6 +66,7 @@ kotlin {
                 api(kotlin("serialization", kotlinVersion))
 
                 api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
+                api(kotlinx("io", kotlinXIoVersion))
                 api(kotlinx("coroutines-io", coroutinesIoVersion))
                 api(kotlinx("coroutines-core", coroutinesVersion))
             }
@@ -101,6 +103,8 @@ kotlin {
                 dependencies {
                     api(kotlin("reflect", kotlinVersion))
 
+                    api(kotlinx("io", kotlinXIoVersion))
+                    api(kotlinx("io-jvm", kotlinXIoVersion))
                     api(kotlinx("serialization-runtime", serializationVersion))
                     api(kotlinx("coroutines-android", coroutinesVersion))
 
@@ -125,6 +129,7 @@ kotlin {
                 api(kotlin("reflect", kotlinVersion))
 
                 api(ktor("client-core-jvm", ktorVersion))
+                api(kotlinx("io-jvm", kotlinXIoVersion))
                 api(kotlinx("serialization-runtime", serializationVersion))
                 api(kotlinx("coroutines-io", coroutinesIoVersion))
                 api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
index 06eaee34e..fb52b514c 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
@@ -13,13 +13,14 @@ package net.mamoe.mirai.utils
 
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
-import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.core.Input
-import io.ktor.utils.io.core.copyTo
-import io.ktor.utils.io.errors.IOException
-import io.ktor.utils.io.streams.asOutput
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.Input
+import kotlinx.io.core.IoBuffer
+import kotlinx.io.core.copyTo
+import kotlinx.io.errors.IOException
+import kotlinx.io.streams.asInput
+import kotlinx.io.streams.asOutput
 import java.io.File
 import java.io.InputStream
 import java.net.URL
@@ -52,7 +53,7 @@ fun File.toExternalImage(): ExternalImage {
         height = input.height,
         md5 = this.inputStream().use { it.md5() },
         imageFormat = this.nameWithoutExtension,
-        input = this.inputStream(),
+        input = this.inputStream().asInput(IoBuffer.Pool),
         inputSize = this.length(),
         filename = this.name
     )
@@ -69,8 +70,8 @@ suspend inline fun File.suspendToExternalImage(): ExternalImage = withContext(IO
 @Throws(IOException::class)
 fun URL.toExternalImage(): ExternalImage {
     val file = createTempFile().apply { deleteOnExit() }
-    file.outputStream().use { output ->
-        openStream().use { input ->
+    file.outputStream().asOutput().use { output ->
+        openStream().asInput().use { input ->
             input.copyTo(output)
         }
         output.flush()
diff --git a/mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt b/mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt
new file mode 100644
index 000000000..65fa88a5b
--- /dev/null
+++ b/mirai-core/src/jvmTest/kotlin/mirai/test/KReflectionTest.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package mirai.test
+
+import kotlin.reflect.KProperty
+
+
+class A {
+
+    val valProp: Any = Any()
+}
+
+fun main() {
+    A::class.members.filterIsInstance<KProperty<*>>().forEach {
+        println(it.getter.call(A()))
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt b/mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt
new file mode 100644
index 000000000..bd77c6548
--- /dev/null
+++ b/mirai-core/src/jvmTest/kotlin/mirai/test/UnixColorText.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package mirai.test
+
+fun main() {
+    repeat(100) {
+        println("\u001b[1;${it}m" + it)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt b/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
new file mode 100644
index 000000000..a603177bb
--- /dev/null
+++ b/mirai-core/src/jvmTest/kotlin/mirai/test/testCaptchaPacket/TestCaptchaPacket.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package mirai.test.testCaptchaPacket
+
+import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.cryptor.TEA.decrypt
+import net.mamoe.mirai.utils.io.hexToBytes
+import net.mamoe.mirai.utils.io.toUHexString
+
+
+@MiraiInternalAPI
+fun main() {
+    val key = "65 F7 F3 14 E3 94 10 1F DD 95 84 A3 F5 9F AD 94".hexToBytes()
+    val data =
+        decrypt(
+            "8D 4F 6A 70 F8 4A DE 43 AF 75 D1 3F 3A 3F F2 E0 A8 16 1A 46 13 CD B0 51 45 00 29 52 57 75 6D 4A 4C D9 B7 98 8C B0 96 EC 57 4E 67 FB 8D C5 F1 BF 72 38 40 42 19 54 C2 28 F4 72 C8 AE 24 EB 66 B5 D0 45 0B 72 44 81 E2 F6 2B EE C3 85 93 BA CB B7 72 F4 1A 30 F9 5B 3D B0 79 3E F4 0B F2 1A A7 49 60 3B 37 02 60 0C 5D D5 76 76 47 4F B5 B3 F5 CA 58 6C FC D2 41 3E 24 D1 FB 0A 18 53 D8 E5 A5 85 A8 BC 51 54 3B 66 5B 21 C6 7B AF C9 62 F0 AA 9C CF 2E 84 0F CC 15 5B 35 93 49 5C E4 28 49 A7 8A D3 30 A9 6E 36 4E 7A 49 28 69 4D C3 25 39 6E 45 6E 40 F2 86 1E F4 4F 00 A6 9D E6 9B 84 19 69 C1 31 6A 17 BA F0 0D 8A 22 09 86 24 92 F7 22 C3 47 7F F2 BF 94 8A 8A B5 29".hexToBytes(),
+            key
+        )
+    println(data.toUHexString())
+
+    //00 02 00 00 08 04 01 E0 00 00 04 56 00 00 00 01 00 00 15 E3 01 00 38 58 CE A0 12 81 31 5C 5E 36 23 5B E4 0E 05 A6 47 BF 7C 1A 7A 35 37 59 90 17 50 66 0C 07 03 77 E4 48 DB 28 0A CF C3 A9 B7 C0 95 D3 9D 00 AA A5 EB FB D6 85 8D 10 61 5A D0 01 03 00 19 02 CA 53 7E F0 7B 32 82 EC 9F DE CF 51 8B A4 93 26 76 EC 42 1C 02 00 74 58 14 00 05 00 00 00 00 00 04 6C 73 64 61 00 40 CE 99 84 E8 F1 59 31 B0 3F 6C 4D 44 09 E4 82 77 96 67 03 A7 3A EA 8F 36 B9 20 79 7E C9 0F 75 3C 2A C3 E1 E5 C6 00 B3 5E 91 5B 47 63 EF AF 30 C0 48 2F 58 23 96 CF 65 2F 4C 75 95 A6 CA 5A 2C 5C 00 10 E1 50 C9 F4 F6 F4 2F D1 7F E9 8C AB B6 1C 38 7B
+
+}
\ No newline at end of file

From e60a6bcf68443c1c1f5e4cd1e919599dd86bd189 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 22:04:31 +0800
Subject: [PATCH 5/9] Revert "Use `ktor.io` than `kotlinx.io`"

---
 .../net/mamoe/mirai/qqandroid/QQAndroidBot.kt | 175 +++++++-
 .../net/mamoe/mirai/qqandroid/QQAndroidBot.kt |  10 +-
 .../network/QQAndroidBotNetworkHandler.kt     |  14 +-
 .../qqandroid/network/QQAndroidClient.kt      |   4 +-
 .../network/highway/HighwayHelper.kt          |   7 +-
 .../qqandroid/network/highway/highway.kt      |   2 +-
 .../network/protocol/data/proto/Cmd0x857.kt   | 408 ++++++++++++++++++
 .../packet/chat/receive/MessageSvc.kt         |  57 ---
 .../packet/chat/receive/OnlinePush.kt         |   4 +-
 .../network/protocol/packet/login/WtLogin.kt  |   9 +-
 .../net/mamoe/mirai/qqandroid/QQAndroidBot.kt | 174 +++++++-
 mirai-core/build.gradle.kts                   |   3 +-
 .../androidMain/kotlin/net/mamoe/mirai/Bot.kt |   2 +-
 .../mirai/contact/ContactJavaHappyAPI.kt      |   2 +-
 .../mirai/message/SendImageUtilsAndroid.kt    |  28 +-
 .../mamoe/mirai/utils/ExternalImageAndroid.kt |   5 +-
 .../commonMain/kotlin/net.mamoe.mirai/Bot.kt  |   4 +-
 .../net.mamoe.mirai/message/MessagePacket.kt  |   2 +-
 .../net.mamoe.mirai/utils/ExternalImage.kt    |   2 +-
 .../kotlin/net.mamoe.mirai/utils/channels.kt  |  27 +-
 .../net.mamoe.mirai/utils/io/chunked.kt       |   2 +-
 .../src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt |   2 +-
 .../mirai/contact/ContactJavaHappyAPI.kt      |   2 +-
 .../net/mamoe/mirai/message/MessagePacket.kt  |   4 +-
 .../mamoe/mirai/message/SendImageUtilsJvm.kt  |  30 +-
 .../net/mamoe/mirai/utils/ExternalImageJvm.kt |   2 +-
 26 files changed, 856 insertions(+), 125 deletions(-)
 create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt

diff --git a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
index be688325d..5040ed74e 100644
--- a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
+++ b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
@@ -9,10 +9,20 @@
 
 package net.mamoe.mirai.qqandroid
 
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.consumeEachBufferRange
+import io.ktor.utils.io.core.Input
+import io.ktor.utils.io.core.readBytes
+import kotlinx.coroutines.io.*
+import kotlinx.io.core.*
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.BotAccount
 import net.mamoe.mirai.utils.BotConfiguration
 import net.mamoe.mirai.utils.Context
 import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.io.ByteArrayPool
+import net.mamoe.mirai.utils.io.toReadPacket
+import java.nio.ByteBuffer
 
 @UseExperimental(MiraiInternalAPI::class)
 internal actual class QQAndroidBot
@@ -20,4 +30,167 @@ actual constructor(
     context: Context,
     account: BotAccount,
     configuration: BotConfiguration
-) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
+) : QQAndroidBotBase(context, account, configuration)
+
+@UseExperimental(MiraiInternalAPI::class)
+@Suppress("DEPRECATION")
+internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
+    return object : kotlinx.coroutines.io.ByteReadChannel {
+        override val availableForRead: Int
+            get() = this@toKotlinByteReadChannel.availableForRead
+        override val isClosedForRead: Boolean
+            get() = this@toKotlinByteReadChannel.isClosedForRead
+        override val isClosedForWrite: Boolean
+            get() = this@toKotlinByteReadChannel.isClosedForWrite
+
+        @Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
+        override var readByteOrder: ByteOrder
+            get() = when (this@toKotlinByteReadChannel.readByteOrder) {
+                io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
+                io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
+            }
+            set(value) {
+                this@toKotlinByteReadChannel.readByteOrder = when (value) {
+                    ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
+                    ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
+                }
+            }
+
+        @Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
+        override val totalBytesRead: Long
+            get() = this@toKotlinByteReadChannel.totalBytesRead
+
+        override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
+        override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) =
+            this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
+
+        override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
+
+        @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
+        @ExperimentalIoApi
+        override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
+            return this@toKotlinByteReadChannel.lookAhead l@{
+                visitor(object : LookAheadSession {
+                    override fun consumed(n: Int) {
+                        return this@l.consumed(n)
+                    }
+
+                    override fun request(skip: Int, atLeast: Int): ByteBuffer? {
+                        return this@l.request(skip, atLeast)
+                    }
+                })
+            }
+        }
+
+        @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
+        @ExperimentalIoApi
+        override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
+            this@toKotlinByteReadChannel.lookAheadSuspend l@{
+                visitor(object : LookAheadSuspendSession {
+                    override suspend fun awaitAtLeast(n: Int): Boolean {
+                        return this@l.awaitAtLeast(n)
+                    }
+
+                    override fun consumed(n: Int) {
+                        return this@l.consumed(n)
+                    }
+
+                    override fun request(skip: Int, atLeast: Int): ByteBuffer? {
+                        return this@l.request(skip, atLeast)
+                    }
+
+                })
+            }
+
+        override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
+            this@toKotlinByteReadChannel.read(min, consumer)
+
+        override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
+        override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
+            this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
+
+        override suspend fun readAvailable(dst: IoBuffer): Int {
+            ByteArrayPool.useInstance {
+                val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
+                dst.writeFully(it, 0, read)
+                return read
+            }
+        }
+
+        override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
+        override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
+        override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
+        override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
+        override suspend fun readFully(dst: ByteBuffer): Int {
+            TODO("not implemented")
+        }
+
+        override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
+            this@toKotlinByteReadChannel.readFully(dst, offset, length)
+
+        override suspend fun readFully(dst: IoBuffer, n: Int) {
+            ByteArrayPool.useInstance {
+                dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
+            }
+        }
+
+        override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
+        override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
+        override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
+            return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
+        }
+
+        override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
+            return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
+        }
+
+        @UseExperimental(ExperimentalIoApi::class)
+        @ExperimentalIoApi
+        override fun readSession(consumer: ReadSession.() -> Unit) {
+            @Suppress("DEPRECATION")
+            this@toKotlinByteReadChannel.readSession lambda@{
+                consumer(object : ReadSession {
+                    override val availableForRead: Int
+                        get() = this@lambda.availableForRead
+
+                    override fun discard(n: Int): Int = this@lambda.discard(n)
+
+                    override fun request(atLeast: Int): IoBuffer? {
+                        val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
+                        val buffer = IoBuffer.Pool.borrow()
+                        val bytes = (ioBuffer as Input).readBytes()
+                        buffer.writeFully(bytes)
+                        return buffer
+                    }
+                })
+            }
+        }
+
+        override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
+
+        @Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
+        @ExperimentalIoApi
+        override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
+            this@toKotlinByteReadChannel.readSuspendableSession l@{
+                consumer(object : SuspendableReadSession {
+                    override val availableForRead: Int
+                        get() = this@l.availableForRead
+
+                    override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
+                    override fun discard(n: Int): Int = this@l.discard(n)
+                    override fun request(atLeast: Int): IoBuffer? {
+                        @Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
+                            this@l.request(atLeast) ?: return null
+                        val buffer = IoBuffer.Pool.borrow()
+                        val bytes = (ioBuffer as Input).readBytes()
+                        buffer.writeFully(bytes)
+                        return buffer
+                    }
+                })
+            }
+
+        override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
+        override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
+            this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
index b9fe826c0..eea2e4940 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
@@ -11,8 +11,8 @@ package net.mamoe.mirai.qqandroid
 
 import io.ktor.client.request.get
 import io.ktor.client.statement.HttpResponse
-import io.ktor.utils.io.ByteReadChannel
 import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.io.ByteReadChannel
 import net.mamoe.mirai.BotAccount
 import net.mamoe.mirai.BotImpl
 import net.mamoe.mirai.LowLevelAPI
@@ -222,6 +222,10 @@ internal abstract class QQAndroidBotBase constructor(
     }
 
     override suspend fun openChannel(image: Image): ByteReadChannel {
-        return Http.get<HttpResponse>(queryImageUrl(image)).content
+        return Http.get<HttpResponse>(queryImageUrl(image)).content.toKotlinByteReadChannel()
     }
-}
\ No newline at end of file
+}
+
+@Suppress("DEPRECATION")
+@UseExperimental(MiraiInternalAPI::class)
+internal expect fun io.ktor.utils.io.ByteReadChannel.toKotlinByteReadChannel(): ByteReadChannel
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
index f4389c40e..b5a679e19 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
@@ -9,12 +9,15 @@
 
 package net.mamoe.mirai.qqandroid.network
 
-import io.ktor.utils.io.core.*
 import kotlinx.atomicfu.AtomicRef
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.*
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.use
 import net.mamoe.mirai.data.MultiPacket
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.event.*
@@ -67,14 +70,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
         _packetReceiverJob?.join()
 
         return this.launch(CoroutineName("Incoming Packet Receiver")) {
-            while (channel.isOpen) {
+            while (channel.isOpen && isActive) {
                 val rawInput = try {
                     channel.read()
                 } catch (e: CancellationException) {
                     return@launch
                 } catch (e: Throwable) {
                     if (this@QQAndroidBotNetworkHandler.isActive) {
-                        BotOfflineEvent.Dropped(bot, e).broadcast()
+                        bot.launch { BotOfflineEvent.Dropped(bot, e).broadcast() }
                     }
                     return@launch
                 }
@@ -141,10 +144,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
                         continue@mainloop
                     }
                     is WtLogin.Login.LoginPacketResponse.Captcha.Slider -> {
-                        var ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url)
-                        if (ticket == null) {
-                            ticket = ""
-                        }
+                        val ticket = bot.configuration.loginSolver.onSolveSliderCaptcha(bot, response.url).orEmpty()
                         response = WtLogin.Login.SubCommand2.SubmitSliderCaptcha(bot.client, ticket).sendAndExpect()
                         continue@mainloop
                     }
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
index 9a507ab35..c4cfa9bd8 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt
@@ -159,8 +159,8 @@ internal open class QQAndroidClient(
     val uin: Long get() = _uin
 
     @UseExperimental(RawAccountIdUse::class)
-    @Suppress("PropertyName")
-    internal var _uin: Long = bot.uin
+    @Suppress("PropertyName", "DEPRECATION_ERROR")
+    internal var _uin: Long = bot.account.id
 
     var t530: ByteArray? = null
     var t528: ByteArray? = null
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
index c219a9881..e3eb1506a 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
@@ -16,10 +16,10 @@ import io.ktor.http.HttpStatusCode
 import io.ktor.http.URLProtocol
 import io.ktor.http.content.OutgoingContent
 import io.ktor.http.userAgent
-import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.copyAndClose
+import io.ktor.utils.io.ByteWriteChannel
 import kotlinx.coroutines.InternalCoroutinesApi
 import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.io.InputStream
 import kotlinx.io.core.Input
 import kotlinx.io.core.discardExact
@@ -30,6 +30,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
 import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
 import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.copyAndClose
 import net.mamoe.mirai.utils.io.ByteArrayPool
 import net.mamoe.mirai.utils.io.PlatformSocket
 import net.mamoe.mirai.utils.io.withUse
@@ -67,7 +68,7 @@ internal suspend fun HttpClient.postImage(
         override val contentType: ContentType = ContentType.Image.Any
         override val contentLength: Long = inputSize
 
-        override suspend fun writeTo(channel: io.ktor.utils.io.ByteWriteChannel) {
+        override suspend fun writeTo(channel: ByteWriteChannel) {
             ByteArrayPool.useInstance { buffer: ByteArray ->
                 when (imageInput) {
                     is Input -> {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
index b84834661..308d27b82 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
@@ -11,9 +11,9 @@
 
 package net.mamoe.mirai.qqandroid.network.highway
 
-import io.ktor.utils.io.ByteReadChannel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.io.InputStream
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.Input
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt
new file mode 100644
index 000000000..2586189c4
--- /dev/null
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x857.kt
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+@file:Suppress("SpellCheckingInspection")
+
+package net.mamoe.mirai.qqandroid.network.protocol.data.proto
+
+import kotlinx.serialization.SerialId
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.protobuf.ProtoNumberType
+import kotlinx.serialization.protobuf.ProtoType
+import net.mamoe.mirai.qqandroid.io.ProtoBuf
+import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
+
+class GroupOpenSysMsg : ProtoBuf {
+    @Serializable
+    class LightApp(
+        @SerialId(1) val app: String = "",
+        @SerialId(2) val view: String = "",
+        @SerialId(3) val desc: String = "",
+        @SerialId(4) val prompt: String = "",
+        @SerialId(5) val ver: String = "",
+        @SerialId(6) val meta: String = "",
+        @SerialId(7) val config: String = "",
+        @SerialId(8) val source: Source? = null
+    ) : ProtoBuf
+
+    @Serializable
+    class RichMsg(
+        @SerialId(1) val title: String = "",
+        @SerialId(2) val desc: String = "",
+        @SerialId(3) val brief: String = "",
+        @SerialId(4) val cover: String = "",
+        @SerialId(5) val url: String = "",
+        @SerialId(6) val source: Source? = null
+    ) : ProtoBuf
+
+    @Serializable
+    class Sender(
+        @SerialId(1) val uin: Long = 0L,
+        @SerialId(2) val nick: String = "",
+        @SerialId(3) val avatar: String = "",
+        @SerialId(4) val url: String = ""
+    ) : ProtoBuf
+
+    @Serializable
+    class Source(
+        @SerialId(1) val name: String = "",
+        @SerialId(2) val icon: String = "",
+        @SerialId(3) val url: String = ""
+    ) : ProtoBuf
+
+    @Serializable
+    class SysMsgBody(
+        @SerialId(1) val groupId: Long = 0L,
+        @SerialId(2) val appid: Long = 0L,
+        @SerialId(3) val sender: Sender? = null,
+        @SerialId(4) val msgType: Int = 0,
+        @SerialId(5) val content: String = "",
+        @SerialId(6) val richMsg: RichMsg? = null,
+        @SerialId(7) val lightApp: LightApp? = null
+    ) : ProtoBuf
+}
+
+@Serializable
+class TroopTips0x857 : ProtoBuf {
+    @Serializable
+    class AIOGrayTipsInfo(
+        @SerialId(1) val optUint32ShowLastest: Int = 0,
+        @SerialId(2) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val optUint32Remind: Int = 0,
+        @SerialId(4) val optBytesBrief: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(5) val receiverUin: Long = 0L,
+        @SerialId(6) val reliaoAdminOpt: Int = 0,
+        @SerialId(7) val robotGroupOpt: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class AIOTopTipsInfo(
+        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(2) val optUint32Icon: Int = 0,
+        @SerialId(3) val optEnumAction: Int /* enum */ = 1,
+        @SerialId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(5) val optBytesData: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val optBytesDataI: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(7) val optBytesDataA: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(8) val optBytesDataP: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class FloatedTipsInfo(
+        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class GeneralGrayTipInfo(
+        @SerialId(1) val busiType: Long = 0L,
+        @SerialId(2) val busiId: Long = 0L,
+        @SerialId(3) val ctrlFlag: Int = 0,
+        @SerialId(4) val c2cType: Int = 0,
+        @SerialId(5) val serviceType: Int = 0,
+        @SerialId(6) val templId: Long = 0L,
+        @SerialId(7) val msgTemplParam: List<TemplParam>? = null,
+        @SerialId(8) val content: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(10) val tipsSeqId: Long = 0L,
+        @SerialId(100) val pbReserv: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class GoldMsgTipsElem(
+        @SerialId(1) val type: Int = 0,
+        @SerialId(2) val billno: String = "",
+        @SerialId(3) val result: Int = 0,
+        @SerialId(4) val amount: Int = 0,
+        @SerialId(5) val total: Int = 0,
+        @SerialId(6) val interval: Int = 0,
+        @SerialId(7) val finish: Int = 0,
+        @SerialId(8) val uin: List<Long>? = null,
+        @SerialId(9) val action: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class GroupInfoChange(
+        @SerialId(1) val groupHonorSwitch: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class GroupNotifyInfo(
+        @SerialId(1) val optUint32AutoPullFlag: Int = 0,
+        @SerialId(2) val optBytesFeedsId: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class InstCtrl(
+        @SerialId(1) val msgSendToInst: List<InstInfo>? = null,
+        @SerialId(2) val msgExcludeInst: List<InstInfo>? = null,
+        @SerialId(3) val msgFromInst: InstInfo? = null
+    ) : ProtoBuf
+
+    @Serializable
+    class InstInfo(
+        @SerialId(1) val apppid: Int = 0,
+        @SerialId(2) val instid: Int = 0,
+        @SerialId(3) val platform: Int = 0,
+        @SerialId(4) val openAppid: Int = 0,
+        @SerialId(5) val productid: Int = 0,
+        @SerialId(6) val ssoBid: Int = 0,
+        @SerialId(7) val guid: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(8) val verMin: Int = 0,
+        @SerialId(9) val verMax: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class LbsShareChangePushInfo(
+        @SerialId(1) val msgType: Int = 0,
+        @SerialId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(4) val groupId: Long = 0L,
+        @SerialId(5) val operUin: Long = 0L,
+        @SerialId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(7) val msgSeq: Long = 0L,
+        @SerialId(8) val joinNums: Int = 0,
+        @SerialId(99) val pushType: Int = 0,
+        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class LuckyBagNotify(
+        @SerialId(1) val msgTips: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class MediaChangePushInfo(
+        @SerialId(1) val msgType: Int = 0,
+        @SerialId(2) val msgInfo: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val versionCtrl: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(4) val groupId: Long = 0L,
+        @SerialId(5) val operUin: Long = 0L,
+        @SerialId(6) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(7) val msgSeq: Long = 0L,
+        @SerialId(8) val joinNums: Int = 0,
+        @SerialId(9) val msgPerSetting: PersonalSetting? = null,
+        @SerialId(10) val playMode: Int = 0,
+        @SerialId(99) val mediaType: Int = 0,
+        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf {
+        @Serializable
+        class PersonalSetting(
+            @SerialId(1) val themeId: Int = 0,
+            @SerialId(2) val playerId: Int = 0,
+            @SerialId(3) val fontId: Int = 0
+        ) : ProtoBuf
+    }
+
+    @Serializable
+    class MessageBoxInfo(
+        @SerialId(1) val optBytesContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(2) val optBytesTitle: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val optBytesButton: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class MessageRecallReminder(
+        @SerialId(1) val uin: Long = 0L,
+        @SerialId(2) val nickname: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val recalledMsgList: List<MessageMeta> = listOf(),
+        @SerialId(4) val reminderContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(5) val userdef: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val groupType: Int = 0,
+        @SerialId(7) val opType: Int = 0
+    ) : ProtoBuf {
+        @Serializable
+        class MessageMeta(
+            @SerialId(1) val seq: Int = 0,
+            @SerialId(2) val time: Int = 0,
+            @SerialId(3) val msgRandom: Int = 0,
+            @SerialId(4) val msgType: Int = 0,
+            @SerialId(5) val msgFlag: Int = 0,
+            @SerialId(6) val authorUin: Long = 0L
+        ) : ProtoBuf
+    }
+
+    @Serializable
+    class MiniAppNotify(
+        @SerialId(1) val msg: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class NotifyMsgBody(
+        @SerialId(1) val optEnumType: Int /* enum */ = 1,
+        @SerialId(2) val optUint64MsgTime: Long = 0L,
+        @SerialId(3) val optUint64MsgExpires: Long = 0L,
+        @SerialId(4) val optUint64GroupCode: Long = 0L,
+        @SerialId(5) val optMsgGraytips: AIOGrayTipsInfo? = null,
+        @SerialId(6) val optMsgMessagebox: MessageBoxInfo? = null,
+        @SerialId(7) val optMsgFloatedtips: FloatedTipsInfo? = null,
+        @SerialId(8) val optMsgToptips: AIOTopTipsInfo? = null,
+        @SerialId(9) val optMsgRedtips: RedGrayTipsInfo? = null,
+        @SerialId(10) val optMsgGroupNotify: GroupNotifyInfo? = null,
+        @SerialId(11) val optMsgRecall: MessageRecallReminder? = null,
+        @SerialId(12) val optMsgThemeNotify: ThemeStateNotify? = null,
+        @SerialId(13) val serviceType: Int = 0,
+        @SerialId(14) val optMsgObjmsgUpdate: NotifyObjmsgUpdate? = null,
+        @SerialId(15) val optMsgWerewolfPush: WereWolfPush? = null,
+        // @SerialId(16) val optStcmGameState: ApolloGameStatus.STCMGameMessage? = null,
+        // @SerialId(17) val aplloMsgPush: ApolloPushMsgInfo.STPushMsgElem? = null,
+        @SerialId(18) val optMsgGoldtips: GoldMsgTipsElem? = null,
+        @SerialId(20) val optMsgMiniappNotify: MiniAppNotify? = null,
+        @SerialId(21) val optUint64SenderUin: Long = 0L,
+        @SerialId(22) val optMsgLuckybagNotify: LuckyBagNotify? = null,
+        @SerialId(23) val optMsgTroopformtipsPush: TroopFormGrayTipsInfo? = null,
+        @SerialId(24) val optMsgMediaPush: MediaChangePushInfo? = null,
+        @SerialId(26) val optGeneralGrayTip: GeneralGrayTipInfo? = null,
+        @SerialId(27) val optMsgVideoPush: VideoChangePushInfo? = null,
+        @SerialId(28) val optLbsShareChangePlusInfo: LbsShareChangePushInfo? = null,
+        @SerialId(29) val optMsgSingPush: SingChangePushInfo? = null,
+        @SerialId(30) val optMsgGroupInfoChange: GroupInfoChange? = null
+    ) : ProtoBuf
+
+    @Serializable
+    class NotifyObjmsgUpdate(
+        @SerialId(1) val objmsgId: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(2) val updateType: Int = 0,
+        @SerialId(3) val extMsg: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class RedGrayTipsInfo(
+        @SerialId(1) val optUint32ShowLastest: Int = 0,
+        @SerialId(2) val senderUin: Long = 0L,
+        @SerialId(3) val receiverUin: Long = 0L,
+        @SerialId(4) val senderRichContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(5) val receiverRichContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val authkey: ByteArray = EMPTY_BYTE_ARRAY,
+        @ProtoType(ProtoNumberType.SIGNED) @SerialId(7) val sint32Msgtype: Int = 0,
+        @SerialId(8) val luckyFlag: Int = 0,
+        @SerialId(9) val hideFlag: Int = 0,
+        @SerialId(10) val pcBody: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(11) val icon: Int = 0,
+        @SerialId(12) val luckyUin: Long = 0L,
+        @SerialId(13) val time: Int = 0,
+        @SerialId(14) val random: Int = 0,
+        @SerialId(15) val broadcastRichContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(16) val idiom: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(17) val idiomSeq: Int = 0,
+        @SerialId(18) val idiomAlpha: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(19) val jumpurl: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class ReqBody(
+        @SerialId(1) val optUint64GroupCode: Long = 0L,
+        @SerialId(2) val uint64Memberuins: List<Long>? = null,
+        @SerialId(3) val optUint32Offline: Int = 0,
+        @SerialId(4) val msgInstCtrl: InstCtrl? = null,
+        @SerialId(5) val optBytesMsg: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val optUint32BusiType: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class RspBody(
+        @SerialId(1) val optUint64GroupCode: Long = 0L
+    ) : ProtoBuf
+
+    @Serializable
+    class SingChangePushInfo(
+        @SerialId(1) val seq: Long = 0L,
+        @SerialId(2) val actionType: Int = 0,
+        @SerialId(3) val groupId: Long = 0L,
+        @SerialId(4) val operUin: Long = 0L,
+        @SerialId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val joinNums: Int = 0
+    ) : ProtoBuf
+
+    @Serializable
+    class TemplParam(
+        @SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(2) val value: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class ThemeStateNotify(
+        @SerialId(1) val state: Int = 0,
+        @SerialId(2) val feedsId: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(3) val themeName: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(4) val actionUin: Long = 0L,
+        @SerialId(5) val createUin: Long = 0L
+    ) : ProtoBuf
+
+    @Serializable
+    class TroopFormGrayTipsInfo(
+        @SerialId(1) val writerUin: Long = 0L,
+        @SerialId(2) val creatorUin: Long = 0L,
+        @SerialId(3) val richContent: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(4) val optBytesUrl: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(5) val creatorNick: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class VideoChangePushInfo(
+        @SerialId(1) val seq: Long = 0L,
+        @SerialId(2) val actionType: Int = 0,
+        @SerialId(3) val groupId: Long = 0L,
+        @SerialId(4) val operUin: Long = 0L,
+        @SerialId(5) val grayTips: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(6) val joinNums: Int = 0,
+        @SerialId(100) val extInfo: ByteArray = EMPTY_BYTE_ARRAY
+    ) : ProtoBuf
+
+    @Serializable
+    class WereWolfPush(
+        @SerialId(1) val pushType: Int = 0,
+        @SerialId(2) val gameRoom: Long = 0L,
+        @SerialId(3) val enumGameState: Int = 0,
+        @SerialId(4) val gameRound: Int = 0,
+        @SerialId(5) val roles: List<Role>? = null,
+        @SerialId(6) val speaker: Long = 0L,
+        @SerialId(7) val judgeUin: Long = 0L,
+        @SerialId(8) val judgeWords: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(9) val enumOperation: Int = 0,
+        @SerialId(10) val srcUser: Long = 0L,
+        @SerialId(11) val dstUser: Long = 0L,
+        @SerialId(12) val deadUsers: List<Long>? = null,
+        @SerialId(13) val gameResult: Int = 0,
+        @SerialId(14) val timeoutSec: Int = 0,
+        @SerialId(15) val killConfirmed: Int = 0,
+        @SerialId(16) val judgeNickname: ByteArray = EMPTY_BYTE_ARRAY,
+        @SerialId(17) val votedTieUsers: List<Long>? = null
+    ) : ProtoBuf {
+        @Serializable
+        class GameRecord(
+            @SerialId(1) val total: Int = 0,
+            @SerialId(2) val win: Int = 0,
+            @SerialId(3) val lose: Int = 0,
+            @SerialId(4) val draw: Int = 0
+        ) : ProtoBuf
+
+        @Serializable
+        class Role(
+            @SerialId(1) val uin: Long = 0L,
+            @SerialId(2) val enumType: Int = 0,
+            @SerialId(3) val enumState: Int = 0,
+            @SerialId(4) val canSpeak: Int = 0,
+            @SerialId(5) val canListen: Int = 0,
+            @SerialId(6) val position: Int = 0,
+            @SerialId(7) val canVote: Int = 0,
+            @SerialId(8) val canVoted: Int = 0,
+            @SerialId(9) val alreadyChecked: Int = 0,
+            @SerialId(10) val alreadySaved: Int = 0,
+            @SerialId(11) val alreadyPoisoned: Int = 0,
+            @SerialId(12) val playerState: Int = 0,
+            @SerialId(13) val enumDeadOp: Int = 0,
+            @SerialId(14) val enumOperation: Int = 0,
+            @SerialId(15) val dstUser: Long = 0L,
+            @SerialId(16) val operationRound: Int = 0,
+            @SerialId(17) val msgGameRecord: GameRecord? = null,
+            @SerialId(18) val isWerewolf: Int = 0,
+            @SerialId(19) val defendedUser: Long = 0L,
+            @SerialId(20) val isSheriff: Int = 0
+        ) : ProtoBuf
+    }
+}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
index 7e8df470a..0a83fb747 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
@@ -9,13 +9,10 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
 
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.*
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.discardExact
-import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.data.MemberInfo
@@ -281,60 +278,6 @@ internal class MessageSvc {
             }
         }
 
-        internal class MessageSourceFromSendFriend(
-            val messageRandom: Int,
-            override val time: Long,
-            override val qqId: Long,
-            override val groupId: Long,
-            val sequenceId: Int
-        ) : MessageSource {
-            @UseExperimental(ExperimentalCoroutinesApi::class)
-            override val id: Long
-                get() = sequenceId.toLong().shl(32) or
-                        messageRandom.toLong().and(0xFFFFFFFF)
-
-            override suspend fun ensureSequenceIdAvailable() {
-                // nothing to do
-            }
-
-            override fun toString(): String {
-                return ""
-            }
-        }
-
-        internal class MessageSourceFromSendGroup(
-            val messageRandom: Int,
-            override val time: Long,
-            override val qqId: Long,
-            override val groupId: Long// ,
-            // override val sourceMessage: MessageChain
-        ) : MessageSource {
-            private lateinit var sequenceIdDeferred: Deferred<Int>
-
-            @UseExperimental(ExperimentalCoroutinesApi::class)
-            override val id: Long
-                get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
-                        messageRandom.toLong().and(0xFFFFFFFF)
-
-            @UseExperimental(MiraiExperimentalAPI::class)
-            fun startWaitingSequenceId(contact: Contact) {
-                sequenceIdDeferred =
-                    contact.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(timeoutMillis = 3000) {
-                        if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
-                            it.sequenceId
-                        } else null
-                    }
-            }
-
-            override suspend fun ensureSequenceIdAvailable() {
-                sequenceIdDeferred.join()
-            }
-
-            override fun toString(): String {
-                return ""
-            }
-        }
-
         inline fun ToFriend(
             client: QQAndroidClient,
             toUin: Long,
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
index ed6a5e27e..7ee94ebf8 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
 
-import io.ktor.utils.io.core.*
+import kotlinx.io.core.*
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.data.MultiPacketBySequence
 import net.mamoe.mirai.data.NoPacket
@@ -341,7 +341,7 @@ internal class OnlinePush {
                                         }
                                     }
                                     0x11 -> {
-                                        discard(1)
+                                        discardExact(1)
                                         val proto = readProtoBuf(TroopTips0x857.NotifyMsgBody.serializer())
                                         proto.optMsgRecall?.let { recallReminder ->
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
index 6ee16aa42..69d873d2d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/WtLogin.kt
@@ -127,7 +127,7 @@ internal class WtLogin {
             private const val appId = 16L
             private const val subAppId = 537062845L
 
-            @UseExperimental(MiraiInternalAPI::class)
+            @UseExperimental(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
             operator fun invoke(
                 client: QQAndroidClient
             ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
@@ -288,7 +288,7 @@ internal class WtLogin {
                 }
 
                 class Picture(
-                    val data: IoBuffer,
+                    val data: ByteArray,
                     val sign: ByteArray
                 ) : Captcha() {
                     override fun toString(): String = "LoginPacketResponse.Captcha.Picture"
@@ -394,11 +394,8 @@ internal class WtLogin {
                 imageData.discardExact(2)//image Length
                 val sign = imageData.readBytes(signInfoLength.toInt())
 
-
-                val buffer = IoBuffer.Pool.borrow()
-                imageData.readAvailable(buffer)
                 return LoginPacketResponse.Captcha.Picture(
-                    data = buffer,
+                    data = imageData.readBytes(),
                     sign = sign
                 )
                 // } else error("UNKNOWN CAPTCHA QUESTION: ${question.toUHexString()}, tlvMap=" + tlvMap.contentToString())
diff --git a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
index 5ead6b167..62989914b 100644
--- a/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
+++ b/mirai-core-qqandroid/src/jvmMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
@@ -9,19 +9,189 @@
 
 package net.mamoe.mirai.qqandroid
 
+import io.ktor.utils.io.ByteReadChannel
+import io.ktor.utils.io.consumeEachBufferRange
+import io.ktor.utils.io.core.Input
+import io.ktor.utils.io.core.readBytes
+import kotlinx.coroutines.io.*
+import kotlinx.io.core.*
+import kotlinx.io.pool.useInstance
 import net.mamoe.mirai.BotAccount
 import net.mamoe.mirai.utils.BotConfiguration
 import net.mamoe.mirai.utils.Context
 import net.mamoe.mirai.utils.ContextImpl
 import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.io.ByteArrayPool
+import net.mamoe.mirai.utils.io.toReadPacket
+import java.nio.ByteBuffer
 
 @UseExperimental(MiraiInternalAPI::class)
 @Suppress("FunctionName")
-internal fun QQAndroidBot(account: BotAccount, configuration: BotConfiguration): QQAndroidBot = QQAndroidBot(ContextImpl(), account, configuration)
+internal fun QQAndroidBot(account: BotAccount, configuration: BotConfiguration): QQAndroidBot =
+    QQAndroidBot(ContextImpl(), account, configuration)
 
 @UseExperimental(MiraiInternalAPI::class)
 internal actual class QQAndroidBot actual constructor(
     context: Context,
     account: BotAccount,
     configuration: BotConfiguration
-) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
+) : QQAndroidBotBase(context, account, configuration)
+
+@UseExperimental(MiraiInternalAPI::class)
+@Suppress("DEPRECATION")
+internal actual fun ByteReadChannel.toKotlinByteReadChannel(): kotlinx.coroutines.io.ByteReadChannel {
+    return object : kotlinx.coroutines.io.ByteReadChannel {
+        override val availableForRead: Int
+            get() = this@toKotlinByteReadChannel.availableForRead
+        override val isClosedForRead: Boolean
+            get() = this@toKotlinByteReadChannel.isClosedForRead
+        override val isClosedForWrite: Boolean
+            get() = this@toKotlinByteReadChannel.isClosedForWrite
+
+        @Suppress("DEPRECATION_ERROR", "OverridingDeprecatedMember")
+        override var readByteOrder: ByteOrder
+            get() = when (this@toKotlinByteReadChannel.readByteOrder) {
+                io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN -> ByteOrder.BIG_ENDIAN
+                io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN -> ByteOrder.LITTLE_ENDIAN
+            }
+            set(value) {
+                this@toKotlinByteReadChannel.readByteOrder = when (value) {
+                    ByteOrder.BIG_ENDIAN -> io.ktor.utils.io.core.ByteOrder.BIG_ENDIAN
+                    ByteOrder.LITTLE_ENDIAN -> io.ktor.utils.io.core.ByteOrder.LITTLE_ENDIAN
+                }
+            }
+
+        @Suppress("DEPRECATION_ERROR", "DEPRECATION", "OverridingDeprecatedMember")
+        override val totalBytesRead: Long
+            get() = this@toKotlinByteReadChannel.totalBytesRead
+
+        override fun cancel(cause: Throwable?): Boolean = this@toKotlinByteReadChannel.cancel(cause)
+        override suspend fun consumeEachBufferRange(visitor: ConsumeEachBufferVisitor) = this@toKotlinByteReadChannel.consumeEachBufferRange(visitor)
+        override suspend fun discard(max: Long): Long = this@toKotlinByteReadChannel.discard(max)
+        @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
+        @ExperimentalIoApi
+        override fun <R> lookAhead(visitor: LookAheadSession.() -> R): R {
+            return this@toKotlinByteReadChannel.lookAhead l@{
+                visitor(object : LookAheadSession{
+                    override fun consumed(n: Int) {
+                        return this@l.consumed(n)
+                    }
+                    override fun request(skip: Int, atLeast: Int): ByteBuffer? {
+                        return this@l.request(skip, atLeast)
+                    }
+                })
+            }
+        }
+
+        @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
+        @ExperimentalIoApi
+        override suspend fun <R> lookAheadSuspend(visitor: suspend LookAheadSuspendSession.() -> R): R =
+            this@toKotlinByteReadChannel.lookAheadSuspend l@{
+                visitor(object : LookAheadSuspendSession {
+                    override suspend fun awaitAtLeast(n: Int): Boolean {
+                        return this@l.awaitAtLeast(n)
+                    }
+
+                    override fun consumed(n: Int) {
+                        return this@l.consumed(n)
+                    }
+
+                    override fun request(skip: Int, atLeast: Int): ByteBuffer? {
+                        return this@l.request(skip, atLeast)
+                    }
+
+                })
+            }
+
+        override suspend fun read(min: Int, consumer: (ByteBuffer) -> Unit) =
+            this@toKotlinByteReadChannel.read(min, consumer)
+
+        override suspend fun readAvailable(dst: ByteBuffer): Int = this@toKotlinByteReadChannel.readAvailable(dst)
+        override suspend fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int =
+            this@toKotlinByteReadChannel.readAvailable(dst, offset, length)
+
+        override suspend fun readAvailable(dst: IoBuffer): Int {
+            ByteArrayPool.useInstance {
+                val read = this@toKotlinByteReadChannel.readAvailable(it, 0, it.size)
+                dst.writeFully(it, 0, read)
+                return read
+            }
+        }
+
+        override suspend fun readBoolean(): Boolean = this@toKotlinByteReadChannel.readBoolean()
+        override suspend fun readByte(): Byte = this@toKotlinByteReadChannel.readByte()
+        override suspend fun readDouble(): Double = this@toKotlinByteReadChannel.readDouble()
+        override suspend fun readFloat(): Float = this@toKotlinByteReadChannel.readFloat()
+        override suspend fun readFully(dst: ByteBuffer): Int {
+            TODO("not implemented")
+        }
+
+        override suspend fun readFully(dst: ByteArray, offset: Int, length: Int) =
+            this@toKotlinByteReadChannel.readFully(dst, offset, length)
+
+        override suspend fun readFully(dst: IoBuffer, n: Int) {
+            ByteArrayPool.useInstance {
+                dst.writeFully(it, 0, this.readAvailable(it, 0, it.size))
+            }
+        }
+
+        override suspend fun readInt(): Int = this@toKotlinByteReadChannel.readInt()
+        override suspend fun readLong(): Long = this@toKotlinByteReadChannel.readLong()
+        override suspend fun readPacket(size: Int, headerSizeHint: Int): ByteReadPacket {
+            return this@toKotlinByteReadChannel.readPacket(size, headerSizeHint).readBytes().toReadPacket()
+        }
+
+        override suspend fun readRemaining(limit: Long, headerSizeHint: Int): ByteReadPacket {
+            return this@toKotlinByteReadChannel.readRemaining(limit, headerSizeHint).readBytes().toReadPacket()
+        }
+
+        @UseExperimental(ExperimentalIoApi::class)
+        @ExperimentalIoApi
+        override fun readSession(consumer: ReadSession.() -> Unit) {
+            @Suppress("DEPRECATION")
+            this@toKotlinByteReadChannel.readSession lambda@{
+                consumer(object : ReadSession {
+                    override val availableForRead: Int
+                        get() = this@lambda.availableForRead
+
+                    override fun discard(n: Int): Int = this@lambda.discard(n)
+
+                    override fun request(atLeast: Int): IoBuffer? {
+                        val ioBuffer: io.ktor.utils.io.core.IoBuffer = this@lambda.request(atLeast) ?: return null
+                        val buffer = IoBuffer.Pool.borrow()
+                        val bytes = (ioBuffer as Input).readBytes()
+                        buffer.writeFully(bytes)
+                        return buffer
+                    }
+                })
+            }
+        }
+
+        override suspend fun readShort(): Short = this@toKotlinByteReadChannel.readShort()
+
+        @Suppress("EXPERIMENTAL_OVERRIDE", "EXPERIMENTAL_API_USAGE")
+        @ExperimentalIoApi
+        override suspend fun readSuspendableSession(consumer: suspend SuspendableReadSession.() -> Unit) =
+            this@toKotlinByteReadChannel.readSuspendableSession l@{
+                consumer(object : SuspendableReadSession {
+                    override val availableForRead: Int
+                        get() = this@l.availableForRead
+
+                    override suspend fun await(atLeast: Int): Boolean = this@l.await(atLeast)
+                    override fun discard(n: Int): Int = this@l.discard(n)
+                    override fun request(atLeast: Int): IoBuffer? {
+                        @Suppress("DuplicatedCode") val ioBuffer: io.ktor.utils.io.core.IoBuffer =
+                            this@l.request(atLeast) ?: return null
+                        val buffer = IoBuffer.Pool.borrow()
+                        val bytes = (ioBuffer as Input).readBytes()
+                        buffer.writeFully(bytes)
+                        return buffer
+                    }
+                })
+            }
+
+        override suspend fun readUTF8Line(limit: Int): String? = this@toKotlinByteReadChannel.readUTF8Line(limit)
+        override suspend fun <A : Appendable> readUTF8LineTo(out: A, limit: Int): Boolean =
+            this@toKotlinByteReadChannel.readUTF8LineTo(out, limit)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 04daba6fa..3192a15a4 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -107,6 +107,7 @@ kotlin {
                     api(kotlinx("io-jvm", kotlinXIoVersion))
                     api(kotlinx("serialization-runtime", serializationVersion))
                     api(kotlinx("coroutines-android", coroutinesVersion))
+                    api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
 
                     api(ktor("client-android", ktorVersion))
                 }
@@ -131,9 +132,7 @@ kotlin {
                 api(ktor("client-core-jvm", ktorVersion))
                 api(kotlinx("io-jvm", kotlinXIoVersion))
                 api(kotlinx("serialization-runtime", serializationVersion))
-                api(kotlinx("coroutines-io", coroutinesIoVersion))
                 api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
-                api(kotlinx("io-jvm", coroutinesIoVersion))
 
                 api("org.bouncycastle:bcprov-jdk15on:1.64")
                 runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
index de70962a5..4ed5464d9 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
@@ -2,8 +2,8 @@
 
 package net.mamoe.mirai
 
-import io.ktor.utils.io.ByteReadChannel
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.io.ByteReadChannel
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
index 515a87d03..64b463145 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.contact
 
 import android.graphics.Bitmap
-import io.ktor.utils.io.core.Input
 import kotlinx.coroutines.Dispatchers
+import kotlinx.io.core.Input
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.JavaHappyAPI
 import net.mamoe.mirai.event.events.*
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/SendImageUtilsAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/SendImageUtilsAndroid.kt
index a0beee43a..d38892940 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/SendImageUtilsAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/SendImageUtilsAndroid.kt
@@ -12,9 +12,9 @@
 package net.mamoe.mirai.message
 
 import android.graphics.Bitmap
-import io.ktor.utils.io.core.Input
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.Input
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.utils.OverFileSizeMaxException
@@ -36,28 +36,31 @@ import java.net.URL
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun Bitmap.sendTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> Bitmap.sendTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun InputStream.sendAsImageTo(contact: Contact) =
+suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> =
     withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
@@ -65,9 +68,9 @@ suspend fun InputStream.sendAsImageTo(contact: Contact) =
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun File.sendAsImageTo(contact: Contact) {
+suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
     require(this.exists() && this.canRead())
-    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+    return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 }
 
 // endregion
@@ -124,35 +127,36 @@ suspend fun File.uploadAsImage(contact: Contact): Image {
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(bufferedImage: Bitmap) = bufferedImage.sendTo(this)
+suspend inline fun <C : Contact> C.sendImage(bufferedImage: Bitmap): MessageReceipt<C> = bufferedImage.sendTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageUrl: URL): MessageReceipt<C> = imageUrl.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<C> = imageInput.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> =
+    imageStream.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(file: File): MessageReceipt<C> = file.sendAsImageTo(this)
 
 // endregion
 
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
index fb52b514c..0cced476e 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/ExternalImageAndroid.kt
@@ -31,7 +31,7 @@ import java.net.URL
 
 
 /**
- * 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage]
+ * 读取 [Bitmap] 的属性, 然后构造 [ExternalImage]
  */
 @Throws(IOException::class)
 fun Bitmap.toExternalImage(formatName: String = "gif"): Nothing {
@@ -123,6 +123,7 @@ fun Input.toExternalImage(): ExternalImage {
  */
 suspend inline fun Input.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
 
+/*
 /**
  * 保存为临时文件然后调用 [File.toExternalImage].
  */
@@ -134,4 +135,4 @@ suspend fun ByteReadChannel.toExternalImage(): ExternalImage {
     }
 
     return file.suspendToExternalImage()
-}
\ No newline at end of file
+}*/
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
index 54bb1308e..48188f8fb 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -11,10 +11,10 @@
 
 package net.mamoe.mirai
 
-import io.ktor.utils.io.ByteReadChannel
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.coroutines.launch
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
@@ -89,7 +89,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
     // region contacts
 
     /**
-     * [_lowLevelNewQQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
+     * [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
      */
     abstract val selfQQ: QQ
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
index 492c654ac..5fba59979 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.message
 
-import io.ktor.utils.io.ByteReadChannel
+import kotlinx.coroutines.io.ByteReadChannel
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
index b26e7163e..eaa009dcd 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.utils
 
-import io.ktor.utils.io.ByteReadChannel
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.io.InputStream
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.Input
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
index c544f09c4..f823915f7 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt
@@ -14,8 +14,9 @@
 package net.mamoe.mirai.utils
 
 
-import io.ktor.utils.io.ByteReadChannel
-import io.ktor.utils.io.readAvailable
+import kotlinx.coroutines.io.ByteReadChannel
+import kotlinx.coroutines.io.ByteWriteChannel
+import kotlinx.coroutines.io.readAvailable
 import kotlinx.io.OutputStream
 import kotlinx.io.core.Output
 import kotlinx.io.pool.useInstance
@@ -120,6 +121,7 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) {
  * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
  */
 suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
+    @Suppress("DuplicatedCode")
     try {
         @UseExperimental(MiraiInternalAPI::class)
         ByteArrayPool.useInstance { buffer ->
@@ -130,7 +132,26 @@ suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) {
         }
     } finally {
         @Suppress("DuplicatedCode")
-        dst.close()
+        dst.close(null)
+    }
+}
+
+/**
+ * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst]
+ */
+suspend fun ByteReadChannel.copyAndClose(dst: io.ktor.utils.io.ByteWriteChannel) {
+    @Suppress("DuplicatedCode")
+    try {
+        @UseExperimental(MiraiInternalAPI::class)
+        ByteArrayPool.useInstance { buffer ->
+            var size: Int
+            while (this.readAvailable(buffer).also { size = it } > 0) {
+                dst.writeFully(buffer, 0, size)
+            }
+        }
+    } finally {
+        @Suppress("DuplicatedCode")
+        dst.close(null)
     }
 }
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
index fc0b54515..766ab5986 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/chunked.kt
@@ -9,11 +9,11 @@
 
 package net.mamoe.mirai.utils.io
 
-import io.ktor.utils.io.ByteReadChannel
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.io.InputStream
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.Input
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
index 93c0ebd52..bdf62b9d0 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
@@ -2,7 +2,7 @@
 
 package net.mamoe.mirai
 
-import io.ktor.utils.io.ByteReadChannel
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
index e5169ab98..d8cd329c9 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaHappyAPI.kt
@@ -9,8 +9,8 @@
 
 package net.mamoe.mirai.contact
 
-import io.ktor.utils.io.core.Input
 import kotlinx.coroutines.Dispatchers
+import kotlinx.io.core.Input
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.JavaHappyAPI
 import net.mamoe.mirai.event.events.*
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
index 9e2b6c493..2ae09dcef 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
@@ -11,9 +11,9 @@
 
 package net.mamoe.mirai.message
 
-import io.ktor.utils.io.ByteWriteChannel
+import kotlinx.coroutines.io.ByteWriteChannel
 import kotlinx.io.core.Input
-import io.ktor.utils.io.core.Output
+import kotlinx.io.core.Output
 import kotlinx.io.core.use
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.QQ
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
index a28d13071..f045ac98f 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt
@@ -37,28 +37,32 @@ import java.net.URL
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun <C : Contact> BufferedImage.sendTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> BufferedImage.sendTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
+suspend fun <C : Contact> InputStream.sendAsImageTo(contact: C): MessageReceipt<C> =
+    withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
@@ -79,28 +83,32 @@ suspend fun <C : Contact> File.sendAsImageTo(contact: C): MessageReceipt<C> {
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun BufferedImage.upload(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun BufferedImage.upload(contact: Contact): OfflineImage =
+    withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun URL.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun URL.uploadAsImage(contact: Contact): OfflineImage =
+    withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun Input.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun Input.uploadAsImage(contact: Contact): OfflineImage =
+    withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image]
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
+suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage =
+    withContext(Dispatchers.IO) { toExternalImage() }.upload(contact)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image]
@@ -121,7 +129,8 @@ suspend fun File.uploadAsImage(contact: Contact): OfflineImage {
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> = bufferedImage.sendTo(this)
+suspend inline fun <C : Contact> C.sendImage(bufferedImage: BufferedImage): MessageReceipt<C> =
+    bufferedImage.sendTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
@@ -142,7 +151,8 @@ suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<
  * @throws OverFileSizeMaxException
  */
 @Throws(OverFileSizeMaxException::class)
-suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> = imageStream.sendAsImageTo(this)
+suspend inline fun <C : Contact> C.sendImage(imageStream: InputStream): MessageReceipt<C> =
+    imageStream.sendAsImageTo(this)
 
 /**
  * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
index 4f500aac4..20a53c769 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
@@ -11,7 +11,7 @@
 
 package net.mamoe.mirai.utils
 
-import io.ktor.utils.io.ByteReadChannel
+import kotlinx.coroutines.io.ByteReadChannel
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import kotlinx.io.core.Input

From 61ca490b5db7f98faf6af6ce2debbf47a3b4ab6f Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 22:23:10 +0800
Subject: [PATCH 6/9] Rearrange dependencies

---
 build.gradle.kts            | 14 ++++++--------
 mirai-core/build.gradle.kts | 31 +++++++++++--------------------
 2 files changed, 17 insertions(+), 28 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index 41260dc15..583a6598e 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,12 +4,11 @@ import java.util.*
 buildscript {
     repositories {
         mavenLocal()
-        maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
+        maven(url = "https://mirrors.huaweicloud.com/repository/maven")
         jcenter()
-        mavenCentral()
+        // mavenCentral()
         google()
-        maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
-        maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
+        // maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
     }
 
     dependencies {
@@ -41,11 +40,10 @@ allprojects {
 
     repositories {
         mavenLocal()
-        maven { setUrl("https://mirrors.huaweicloud.com/repository/maven") }
+        maven(url = "https://mirrors.huaweicloud.com/repository/maven")
         jcenter()
-        mavenCentral()
+        // mavenCentral()
         google()
-        maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
-        maven { setUrl("https://dl.bintray.com/kotlin/kotlin-dev") }
+        // maven (url="https://dl.bintray.com/kotlin/kotlin-eap")
     }
 }
\ No newline at end of file
diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 3192a15a4..04871a446 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -52,49 +52,40 @@ kotlin {
         )
     }
 
-    jvm("jvm") {
+    targets {
+        jvm()
+        android()
     }
 
     sourceSets {
         all {
             languageSettings.enableLanguageFeature("InlineClasses")
-
             languageSettings.useExperimentalAnnotation("kotlin.Experimental")
+        }
 
+        commonMain {
             dependencies {
                 api(kotlin("stdlib", kotlinVersion))
                 api(kotlin("serialization", kotlinVersion))
+                api(kotlin("reflect", kotlinVersion))
 
-                api("org.jetbrains.kotlinx:atomicfu:$atomicFuVersion")
+                api(kotlinx("coroutines-core-common", coroutinesVersion))
+                api(kotlinx("serialization-runtime-common", serializationVersion))
                 api(kotlinx("io", kotlinXIoVersion))
                 api(kotlinx("coroutines-io", coroutinesIoVersion))
                 api(kotlinx("coroutines-core", coroutinesVersion))
-            }
-        }
-        commonMain {
-            dependencies {
-                api(kotlin("reflect", kotlinVersion))
-                api(kotlin("serialization", kotlinVersion))
-                api(kotlinx("coroutines-core-common", coroutinesVersion))
-                api(kotlinx("serialization-runtime-common", serializationVersion))
 
-                api(ktor("http-cio", ktorVersion))
-                api(ktor("http", ktorVersion))
-                api(ktor("client-core-jvm", ktorVersion))
+                api("org.jetbrains.kotlinx:atomicfu-common:$atomicFuVersion")
+
                 api(ktor("client-cio", ktorVersion))
                 api(ktor("client-core", ktorVersion))
                 api(ktor("network", ktorVersion))
-                //implementation("io.ktor:ktor-io:1.3.0-beta-1")
-
-                //runtimeOnly(files("build/classes/kotlin/metadata/main")) // classpath is not properly set by IDE
             }
         }
         commonTest {
             dependencies {
                 implementation(kotlin("test-annotations-common"))
                 implementation(kotlin("test-common"))
-
-                //runtimeOnly(files("build/classes/kotlin/metadata/test")) // classpath is not properly set by IDE
             }
         }
 
@@ -103,7 +94,6 @@ kotlin {
                 dependencies {
                     api(kotlin("reflect", kotlinVersion))
 
-                    api(kotlinx("io", kotlinXIoVersion))
                     api(kotlinx("io-jvm", kotlinXIoVersion))
                     api(kotlinx("serialization-runtime", serializationVersion))
                     api(kotlinx("coroutines-android", coroutinesVersion))
@@ -133,6 +123,7 @@ kotlin {
                 api(kotlinx("io-jvm", kotlinXIoVersion))
                 api(kotlinx("serialization-runtime", serializationVersion))
                 api(kotlinx("coroutines-io-jvm", coroutinesIoVersion))
+                api(kotlinx("coroutines-core", coroutinesVersion))
 
                 api("org.bouncycastle:bcprov-jdk15on:1.64")
                 runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE

From 3d9b598d576ad09efa633b81dfab07dce219dbb5 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sat, 7 Mar 2020 22:45:12 +0800
Subject: [PATCH 7/9] Improve Heartbeat

---
 .../mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
index b5a679e19..3cfa695b7 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
@@ -338,6 +338,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
     suspend fun doHeartBeat(): Exception? {
         val lastException: Exception?
         try {
+            kotlin.runCatching {
+                Heartbeat.Alive(bot.client)
+                    .sendAndExpect<Heartbeat.Alive.Response>(
+                        timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
+                        retry = 2
+                    )
+                return null
+            }
             Heartbeat.Alive(bot.client)
                 .sendAndExpect<Heartbeat.Alive.Response>(
                     timeoutMillis = bot.configuration.heartbeatTimeoutMillis,

From c334321fe1217d604697cf86db1eba87c1dae886 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sun, 8 Mar 2020 00:02:10 +0800
Subject: [PATCH 8/9] 0.26.0

---
 CHANGELOG.md      | 4 ++++
 gradle.properties | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index af0b5fbf0..f62f4da7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 开发版本. 频繁更新, 不保证高稳定性
 
+## `0.26.0` 2020/3/7
+- 使用 `kotlinx.io` 而不是 `ktor.io`
+- 修复 #111, #108, #116, #112
+
 ## `0.25.0` 2020/3/6
 - 适配 8.2.7 版本(2020 年 3 月)协议
 - 全面的 `Image` 类型: Online/Offline Image, Friend/Group Image
diff --git a/gradle.properties b/gradle.properties
index c24366539..bddc62b33 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
 # style guide
 kotlin.code.style=official
 # config
-miraiVersion=0.25.0
+miraiVersion=0.26.0
 kotlin.incremental.multiplatform=true
 kotlin.parallel.tasks.in.project=true
 # kotlin

From 1b8a87b4cebd4931798534efcf6ccfb3791cd7be Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sun, 8 Mar 2020 00:08:11 +0800
Subject: [PATCH 9/9] Fix build

---
 mirai-core/build.gradle.kts | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts
index 04871a446..decb95aa3 100644
--- a/mirai-core/build.gradle.kts
+++ b/mirai-core/build.gradle.kts
@@ -52,10 +52,7 @@ kotlin {
         )
     }
 
-    targets {
-        jvm()
-        android()
-    }
+    jvm()
 
     sourceSets {
         all {