From 9ff1ff604c19ed30f128aac03a0e3d4deaf037ad Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 24 Jan 2020 17:44:25 +0800 Subject: [PATCH 1/8] QQA Debugging update --- .../protocol/packet/login/LoginPacket.kt | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index 9cbef817c..e451f6938 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -172,6 +172,12 @@ internal object LoginPacket : PacketFactory("wt sealed class LoginPacketResponse : Packet { object Success : LoginPacketResponse() + data class Error( + val title: String, + val message: String, + val errorInfo: String + ) : LoginPacketResponse() + sealed class Captcha : LoginPacketResponse() { class Slider( val data: IoBuffer @@ -197,32 +203,38 @@ internal object LoginPacket : PacketFactory("wt println("subCommand=$subCommand") val type = readByte() println("type=$type") - when (type.toInt()) { - 0 -> { - onLoginSuccess(bot) - } - 1 -> { - throw Exception("Wrong Password") - } - 2 -> { - onSolveLoginCaptcha(bot) - } + return when (type.toInt()) { + 0 -> onLoginSuccess(bot) + 1, 15 -> onErrorMessage() + 2 -> onSolveLoginCaptcha(bot) + else -> error("unknown login result type: $type") } - if (type.toInt() != 0) { - DebugLogger.debug("unknown login result type: $type") - } - return LoginPacketResponse.Success + } + + private fun ByteReadPacket.onErrorMessage(): LoginPacketResponse.Error { + discardExact(2) + val tlvMap = readTLVMap() + tlvMap[0x146]?.toReadPacket()?.run { + readShort() // ver + readShort() // code + + val title = readUShortLVString() + val message = readUShortLVString() + val errorInfo = readUShortLVString() + + return LoginPacketResponse.Error(title, message, errorInfo) + } ?: error("Cannot find error message") } @UseExperimental(MiraiDebugAPI::class) - suspend fun ByteReadPacket.onSolveLoginCaptcha(bot: QQAndroidBot) = this.debugPrint("login验证码解析").run { + suspend fun ByteReadPacket.onSolveLoginCaptcha(bot: QQAndroidBot): LoginPacketResponse.Captcha = this.debugPrint("login验证码解析").run { val client = bot.client debugDiscardExact(2) val tlvMap: Map = this.readTLVMap() - // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } + // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } println() val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN") - when(question[18].toUHexString()){ + when (question[18].toUHexString()) { "36" -> { //图片验证 debugPrint("是一个图片验证码") @@ -236,10 +248,11 @@ internal object LoginPacket : PacketFactory("wt error("UNKNOWN CAPTCHA QUESTION: $question") } } + return TODO() } @UseExperimental(MiraiDebugAPI::class) - fun ByteReadPacket.onLoginSuccess(bot: QQAndroidBot) = this.debugPrint("login成功解析").run { + private fun ByteReadPacket.onLoginSuccess(bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client debugDiscardExact(2) val tlvMap: Map = this.readTLVMap() @@ -446,6 +459,7 @@ internal object LoginPacket : PacketFactory("wt } } + return LoginPacketResponse.Success } From b1451f664caceacd1121cd728dc04ec089365060 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 24 Jan 2020 17:45:32 +0800 Subject: [PATCH 2/8] QQA Debugging update --- .../protocol/packet/login/LoginPacket.kt | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index e451f6938..da18f24df 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -203,18 +203,19 @@ internal object LoginPacket : PacketFactory("wt println("subCommand=$subCommand") val type = readByte() println("type=$type") + + debugDiscardExact(2) + val tlvMap: Map = this.readTLVMap() return when (type.toInt()) { - 0 -> onLoginSuccess(bot) - 1, 15 -> onErrorMessage() - 2 -> onSolveLoginCaptcha(bot) + 0 -> onLoginSuccess(tlvMap, bot) + 1, 15 -> onErrorMessage(tlvMap) + 2 -> onSolveLoginCaptcha(tlvMap, bot) else -> error("unknown login result type: $type") } } - private fun ByteReadPacket.onErrorMessage(): LoginPacketResponse.Error { - discardExact(2) - val tlvMap = readTLVMap() - tlvMap[0x146]?.toReadPacket()?.run { + private fun ByteReadPacket.onErrorMessage(tlvMap: Map): LoginPacketResponse.Error { + return tlvMap[0x146]?.toReadPacket()?.run { readShort() // ver readShort() // code @@ -222,40 +223,37 @@ internal object LoginPacket : PacketFactory("wt val message = readUShortLVString() val errorInfo = readUShortLVString() - return LoginPacketResponse.Error(title, message, errorInfo) + LoginPacketResponse.Error(title, message, errorInfo) } ?: error("Cannot find error message") } @UseExperimental(MiraiDebugAPI::class) - suspend fun ByteReadPacket.onSolveLoginCaptcha(bot: QQAndroidBot): LoginPacketResponse.Captcha = this.debugPrint("login验证码解析").run { - val client = bot.client - debugDiscardExact(2) - val tlvMap: Map = this.readTLVMap() - // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } - println() - val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN") - when (question[18].toUHexString()) { - "36" -> { - //图片验证 - debugPrint("是一个图片验证码") - val imageData = tlvMap[0x165] - bot.configuration.captchaSolver.invoke( - bot, - (tlvMap[0x165] ?: error("Captcha Image Data Not Found")).toIoBuffer() - ) - } - else -> { - error("UNKNOWN CAPTCHA QUESTION: $question") + suspend fun ByteReadPacket.onSolveLoginCaptcha(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Captcha = + this.debugPrint("login验证码解析").run { + val client = bot.client + // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } + println() + val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN") + when (question[18].toUHexString()) { + "36" -> { + //图片验证 + debugPrint("是一个图片验证码") + val imageData = tlvMap[0x165] + bot.configuration.captchaSolver.invoke( + bot, + (tlvMap[0x165] ?: error("Captcha Image Data Not Found")).toIoBuffer() + ) + } + else -> { + error("UNKNOWN CAPTCHA QUESTION: $question") + } } + return TODO() } - return TODO() - } @UseExperimental(MiraiDebugAPI::class) - private fun ByteReadPacket.onLoginSuccess(bot: QQAndroidBot): LoginPacketResponse.Success { + private fun ByteReadPacket.onLoginSuccess(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client - debugDiscardExact(2) - val tlvMap: Map = this.readTLVMap() println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) tlvMap[0x150]?.let { client.analysisTlv150(it) } From 7810127f7f1a4c1948448b735a23f276e3bc8fb2 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 24 Jan 2020 17:57:13 +0800 Subject: [PATCH 3/8] QQA Debugging update --- build.gradle | 1 + .../protocol/packet/login/LoginPacket.kt | 43 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/build.gradle b/build.gradle index e226a72ca..08d1243f2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,7 @@ buildscript { jcenter() mavenCentral() google() + maven { url 'https://dl.bintray.com/kotlin/kotlin-dev/'} } dependencies { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index da18f24df..10ffaaec1 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -214,7 +214,7 @@ internal object LoginPacket : PacketFactory("wt } } - private fun ByteReadPacket.onErrorMessage(tlvMap: Map): LoginPacketResponse.Error { + private fun onErrorMessage(tlvMap: Map): LoginPacketResponse.Error { return tlvMap[0x146]?.toReadPacket()?.run { readShort() // ver readShort() // code @@ -228,31 +228,30 @@ internal object LoginPacket : PacketFactory("wt } @UseExperimental(MiraiDebugAPI::class) - suspend fun ByteReadPacket.onSolveLoginCaptcha(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Captcha = - this.debugPrint("login验证码解析").run { - val client = bot.client - // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } - println() - val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN") - when (question[18].toUHexString()) { - "36" -> { - //图片验证 - debugPrint("是一个图片验证码") - val imageData = tlvMap[0x165] - bot.configuration.captchaSolver.invoke( - bot, - (tlvMap[0x165] ?: error("Captcha Image Data Not Found")).toIoBuffer() - ) - } - else -> { - error("UNKNOWN CAPTCHA QUESTION: $question") - } + suspend fun onSolveLoginCaptcha(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Captcha { + val client = bot.client + // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } + println() + val question = tlvMap[0x165] ?: error("CAPTCHA QUESTION UNKNOWN") + when (question[18].toUHexString()) { + "36" -> { + //图片验证 + DebugLogger.debug("是一个图片验证码") + val imageData = tlvMap[0x165] + bot.configuration.captchaSolver.invoke( + bot, + (tlvMap[0x165] ?: error("Captcha Image Data Not Found")).toIoBuffer() + ) + } + else -> { + error("UNKNOWN CAPTCHA QUESTION: $question") } - return TODO() } + return TODO() + } @UseExperimental(MiraiDebugAPI::class) - private fun ByteReadPacket.onLoginSuccess(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Success { + private fun onLoginSuccess(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Success { val client = bot.client println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) From a1000b141b223f5d2499ffbace56ae685c4436b7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 24 Jan 2020 17:58:11 +0800 Subject: [PATCH 4/8] QQA Debugging update --- .../qqandroid/network/protocol/packet/login/LoginPacket.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index 10ffaaec1..02521d063 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -228,7 +228,7 @@ internal object LoginPacket : PacketFactory("wt } @UseExperimental(MiraiDebugAPI::class) - suspend fun onSolveLoginCaptcha(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Captcha { + private suspend fun onSolveLoginCaptcha(tlvMap: Map, bot: QQAndroidBot): LoginPacketResponse.Captcha { val client = bot.client // val ret = tlvMap[0x104]?.let { println(it.toUHexString()) } println() @@ -240,7 +240,7 @@ internal object LoginPacket : PacketFactory("wt val imageData = tlvMap[0x165] bot.configuration.captchaSolver.invoke( bot, - (tlvMap[0x165] ?: error("Captcha Image Data Not Found")).toIoBuffer() + (tlvMap[0x105] ?: error("Captcha Image Data Not Found")).toIoBuffer() ) } else -> { From 1a0f024081907b36d599f40788b7bf969b266f63 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 24 Jan 2020 18:02:56 +0800 Subject: [PATCH 5/8] LoginError parsing --- .../qqandroid/network/QQAndroidBotNetworkHandler.kt | 12 +++++------- .../network/protocol/packet/login/LoginPacket.kt | 2 +- .../src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt | 2 +- .../kotlin/net.mamoe.mirai/utils/BotConfiguration.kt | 3 +-- 4 files changed, 8 insertions(+), 11 deletions(-) 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 3b99d7e19..2f40aad9e 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 @@ -11,8 +11,7 @@ import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket -import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.Captcha -import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.Success +import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.* import net.mamoe.mirai.qqandroid.network.protocol.packet.login.SvcReqRegisterPacket import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.io.* @@ -31,7 +30,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler launch(CoroutineName("Incoming Packet Receiver")) { processReceive() } bot.logger.info("Trying login") - when (val response = LoginPacket.SubCommand9(bot.client).sendAndExpect()) { + when (val response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect()) { is Captcha -> when (response) { is Captcha.Picture -> { bot.logger.info("需要图片验证码") @@ -41,6 +40,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } } + is Error -> error(response.toString()) + is Success -> { bot.logger.info("Login successful") } @@ -207,10 +208,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler suspend fun OutgoingPacket.sendAndExpect(): E { val handler = PacketListener(commandName = commandName, sequenceId = sequenceId) packetListeners.addLast(handler) - //println(delegate.readBytes().toUHexString()) - println("Sending length=" + delegate.remaining) - channel.send(delegate)//) { packetListeners.remove(handler); "Cannot send packet" } - println("Packet sent") + channel.send(delegate) @Suppress("UNCHECKED_CAST") return handler.await() as E } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt index 02521d063..5a3a5d820 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt @@ -204,7 +204,7 @@ internal object LoginPacket : PacketFactory("wt val type = readByte() println("type=$type") - debugDiscardExact(2) + discardExact(2) val tlvMap: Map = this.readTLVMap() return when (type.toInt()) { 0 -> onLoginSuccess(tlvMap, bot) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt index 7fae74ff2..7f958fd05 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotImpl.kt @@ -28,7 +28,7 @@ abstract class BotImpl constructor( @UseExperimental(MiraiExperimentalAPI::class) final override val uin: Long get() = account.id - final override val logger: MiraiLogger = configuration.logger ?: DefaultLogger("Bot($uin)") + final override val logger: MiraiLogger = configuration.logger ?: DefaultLogger("Bot($uin)").also { configuration.logger = it } init { @Suppress("LeakingThis") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt index 3bc7d89b0..fc18b658b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt @@ -4,7 +4,6 @@ import kotlinx.io.core.IoBuffer import net.mamoe.mirai.Bot import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.coroutines.coroutineContext import kotlin.jvm.JvmStatic /** @@ -26,7 +25,7 @@ class BotConfiguration { /** * 日志记录器 */ - var logger: PlatformLogger? = null + var logger: MiraiLogger? = null /** * 父 [CoroutineContext] From 0ca9cc8ad003b672637225c8177b0f9a7fc20992 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 25 Jan 2020 12:57:44 +0800 Subject: [PATCH 6/8] Update README.md --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01e84bf8b..4c01f9923 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,29 @@ 您的 star 是对我们最大的鼓励(点击项目右上角); +## Features +#### mirai-core +通用 API 模块,请参考此模块调用 Mirai. +#### mirai-core-timpc +TIM PC (2.3.2 版本,2019 年 8 月)协议的实现,相较于 core,仅新增少量 API. 详见 [README.md](mirai-core-timpc/) +支持的功能: +- 消息收发:图片文字复合消息,图片消息 +- 群管功能:群员列表,禁言 +(目前不再更新,请关注安卓协议) + +#### mirai-core-qqandroid +QQ for Android (8.2.0 版本,2019 年 12 月)协议的实现,目前还未完成。 +- 高兼容性:Mirai 协议仅含极少部分为硬编码,其余全部随官方方式动态生成 +- 高安全性:密匙随机,ECDH 动态计算,硬件信息真机模拟(Android 平台获取真机信息) + +开发进度: +- 完成 密码登录 (2020/1/23) +- 进行中 验证码登录 +- 进行中 消息解析 +- 进行中 图片上传下载 + ## Use directly -**直接使用Mirai(终端环境/网页面板).** +**直接使用Mirai(终端环境/网页面板(将来)).** [Mirai-Console](https://github.com/mamoe/mirai/tree/master/mirai-console) 插件支持, 在终端中启动Mirai并获得机器人服务 ## Use as a library From 3728b76d9f0a161c75ba09675e187f4938ecd326 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 25 Jan 2020 13:00:43 +0800 Subject: [PATCH 7/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4c01f9923..4f6fc6512 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ TIM PC (2.3.2 版本,2019 年 8 月)协议的实现,相较于 core,仅 支持的功能: - 消息收发:图片文字复合消息,图片消息 - 群管功能:群员列表,禁言 + (目前不再更新,请关注安卓协议) #### mirai-core-qqandroid From b6ae6ebd781bc78333e1a864bd5407131d95b365 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 25 Jan 2020 13:04:53 +0800 Subject: [PATCH 8/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4f6fc6512..c7c86db60 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ repositories{ **Mirai 目前还处于实验性阶段, 我们无法保证任何稳定性, API 也可能会随时修改.** 现在 Mirai 只支持 TIM PC 协议. QQ Android 协议正在开发中. + **common** ```kotlin implementation("net.mamoe:mirai-core-timpc-common:VERSION")