From 388e1a5b7fa40bd9481407271b85287f40281c74 Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Thu, 6 Feb 2020 13:58:57 +0800 Subject: [PATCH] Cleanup --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../network/QQAndroidBotNetworkHandler.kt | 19 +- .../qqandroid/network/http/HttpRequest.kt | 13 +- .../protocol/packet/OutgoingPacketAndroid.kt | 3 +- .../qqandroid/network/protocol/packet/Tlv.kt | 4 +- .../protocol/packet/chat/TroopManagement.kt | 11 +- .../protocol/packet/login/LoginPacket.kt | 31 ++-- .../androidPacketTests/clientToServer.kt | 12 +- .../androidPacketTests/serverToClient.kt | 4 + .../mirai/utils/cryptor/contentToString.kt | 7 +- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 2 - .../kotlin/net.mamoe.mirai/BotHelper.kt | 3 - .../net.mamoe.mirai/utils/BotConfiguration.kt | 1 + .../net.mamoe.mirai/utils/Calculation.kt | 8 - .../net.mamoe.mirai/utils/ExternalImage.kt | 4 +- .../utils/LoginFailedException.kt | 8 - .../net.mamoe.mirai/utils/MiraiLogger.kt | 4 +- .../net.mamoe.mirai/utils/NumberUtils.kt | 6 + .../utils/OverFileSizeMaxException.kt | 6 + .../net.mamoe.mirai/utils/PlatformUtils.kt | 7 +- .../net.mamoe.mirai/utils/SuspendLazy.kt | 4 +- .../kotlin/net.mamoe.mirai/utils/Tested.kt | 9 - .../kotlin/net.mamoe.mirai/utils/Time.kt | 5 + .../utils/assertUnreachable.kt | 7 - .../utils/cryptor/Decrypters.kt | 66 ------- .../net.mamoe.mirai/utils/cryptor/Proto.kt | 15 +- .../net.mamoe.mirai/utils/io/ByteArrayUtil.kt | 18 +- .../net.mamoe.mirai/utils/io/DebugUtil.kt | 23 ++- .../net.mamoe.mirai/utils/io/InputUtils.kt | 170 ++++-------------- .../net.mamoe.mirai/utils/io/OutputUtils.kt | 108 +---------- .../kotlin/net.mamoe.mirai/utils/io/Varint.kt | 8 +- .../kotlin/net.mamoe.mirai/utils/map.kt | 16 +- .../mirai/utils/DefaultCaptchaSolverJvm.kt | 2 +- .../mirai/utils/cryptor/contentToString.kt | 2 + .../net/mamoe/mirai/demo/MiraiService.kt | 3 +- .../net/mamoe/mirai/japt/BlockingBot.java | 3 - 36 files changed, 181 insertions(+), 433 deletions(-) delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46c88fb64..3e9038784 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Thu Oct 03 14:28:43 CST 2019 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists 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 137dce05e..123b3911d 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 @@ -41,7 +41,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job]) override val coroutineContext: CoroutineContext = bot.coroutineContext + CoroutineExceptionHandler { _, throwable -> - throwable.logStacktrace("Exception in NetworkHandler") + bot.logger.error("Exception in NetworkHandler", throwable) } private lateinit var channel: PlatformSocket @@ -106,6 +106,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) // it's slow } + @UseExperimental(MiraiExperimentalAPI::class) override suspend fun init() { this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> { if (this@QQAndroidBotNetworkHandler.bot == this.bot) { @@ -113,6 +114,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } } + MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() + //val msg = MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<MessageSvc.PbGetMsg.Response>() //println(msg.contentToString()) bot.qqs.delegate.clear() @@ -199,16 +202,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } catch (e: Exception) { groupInfo[it.groupCode] = -1 bot.logger.info("群${it.groupCode}的列表拉取失败, 将采用动态加入") - println(e.message) - println(e.logStacktrace()) + bot.logger.error(e) } } } bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个") } catch (e: Exception) { bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表") - println(e.message) - println(e.logStacktrace()) + bot.logger.error(e) } //===log===// @@ -239,8 +240,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } } bot.logger.info("====================Mirai Bot List初始化完毕====================") - return - MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() + + bot.firstLoginSucceed = true } @@ -483,7 +484,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler private val packetReceiveLock: Mutex = Mutex() /** - * 发送一个包, 但不期待任何返回.- + * 发送一个包, 但不期待任何返回. */ suspend fun OutgoingPacket.sendWithoutExpect() { bot.logger.info("Send: ${this.commandName}") @@ -494,6 +495,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler /** * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) + * + * @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry */ suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E { require(timeoutMillis > 0) { "timeoutMillis must > 0" } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt index b74ac1501..3be0d0d90 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt @@ -1,20 +1,17 @@ package net.mamoe.mirai.qqandroid.network.http import io.ktor.client.HttpClient -import io.ktor.client.request.* +import io.ktor.client.request.get +import io.ktor.client.request.headers +import io.ktor.client.request.post import io.ktor.client.response.HttpResponse -import io.ktor.http.ContentType import io.ktor.http.URLProtocol import io.ktor.http.setCookie import io.ktor.http.userAgent -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.io.readRemaining -import kotlinx.coroutines.withContext -import net.mamoe.mirai.qqandroid.QQAndroidBot +import kotlinx.io.core.readBytes import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.currentTimeMillis -import net.mamoe.mirai.utils.io.readRemainingBytes import net.mamoe.mirai.utils.io.toUHexString /** @@ -49,7 +46,7 @@ internal suspend fun HttpClient.getPTLoginCookies( println(res.status) println(res.setCookie()) - println(res.content.readRemaining().readRemainingBytes().toUHexString()) + println(res.content.readRemaining().readBytes().toUHexString()) return "done"; } 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 757956aad..6d93797b2 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,6 @@ import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeIntLVPacket -import net.mamoe.mirai.utils.io.writeQQ internal class OutgoingPacket constructor( name: String?, @@ -250,7 +249,7 @@ internal fun BytePacketBuilder.writeOicqRequestPacket( writeShort(client.protocolVersion) writeShort(commandId.toShort()) writeShort(1) // const?? - writeQQ(client.uin) + writeInt(client.uin.toInt()) writeByte(3) // originally const writeByte(encryptMethod.id.toByte()) writeByte(0) // const8_always_0 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 b63d1365f..74cddb2d3 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 @@ -23,7 +23,7 @@ fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) { writeShort(1) // _ip_ver writeInt(Random.nextInt()) writeInt(uin.toInt()) - writeTime() + writeInt(currentTimeMillis.toInt()) writeFully(ip) writeShort(0) } shouldEqualsTo 20 @@ -100,7 +100,7 @@ fun BytePacketBuilder.t106( writeLong(uin) } - writeTime() + writeInt(currentTimeMillis.toInt()) writeFully(ByteArray(4)) // ip // no need to write actual ip writeByte(n5_always_1.toByte()) writeFully(passwordMd5) 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 16f05b3c3..19d29799d 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 @@ -1,15 +1,15 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat -import kotlinx.io.charsets.Charset -import kotlinx.io.charsets.encode -import kotlinx.io.core.* +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.buildPacket +import kotlinx.io.core.readBytes +import kotlinx.io.core.toByteArray import kotlinx.serialization.toUtf8Bytes import net.mamoe.mirai.contact.Member import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.io.serialization.* import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetTroopListReqV2Simplify import net.mamoe.mirai.qqandroid.network.protocol.data.jce.ModifyGroupCardReq import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.data.jce.stUinInfo @@ -22,7 +22,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.utils.daysToSeconds -import net.mamoe.mirai.utils.io.encodeToGBKString import net.mamoe.mirai.utils.io.encodeToString internal object TroopManagement { @@ -355,7 +354,7 @@ internal object TroopManagement { gender = 0, dwuin = member.id, dwFlag = 31, - sName = newName.toUtf8Bytes().encodeToGBKString(), + sName = newName.toUtf8Bytes().encodeToString(charset = CharsetGBK), sPhone = "", sEmail = "", sRemark = "" 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 f244e509a..923aaae77 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 @@ -22,15 +22,15 @@ import net.mamoe.mirai.utils.md5 /** * OicqRequest */ -@UseExperimental(ExperimentalUnsignedTypes::class) +@Suppress("FunctionName") +@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketResponse>("wtlogin.login") { + private const val subAppId = 537062845L + /** * 提交验证码 */ object SubCommand2 { - private const val appId = 16L - private const val subAppId = 537062845L - fun SubmitSliderCaptcha( client: QQAndroidClient, ticket: String @@ -66,10 +66,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo } object SubCommand20 { - private const val appId = 16L - private const val subAppId = 537062845L - @UseExperimental(MiraiInternalAPI::class) operator fun invoke( client: QQAndroidClient, t402: ByteArray @@ -91,9 +88,6 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo * 提交 SMS */ object SubCommand7 { - private const val appId = 16L - private const val subAppId = 537062845L - @UseExperimental(MiraiInternalAPI::class) operator fun invoke( client: QQAndroidClient ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> @@ -297,7 +291,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo } } - class DeviceLockLogin(val t402: ByteArray, val t403: ByteArray) : LoginPacketResponse() { + class DeviceLockLogin(val t402: ByteArray) : LoginPacketResponse() { override fun toString(): String = "LoginPacket.LoginPacketResponse.DeviceLockLogin" } } @@ -320,7 +314,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo 2 -> onSolveLoginCaptcha(tlvMap, bot) 160 /*-96*/ -> onUnsafeDeviceLogin(tlvMap) 204 /*-52*/ -> onSMSVerifyNeeded(tlvMap, bot) - else -> tlvMap[0x149]?.let { bot.client.analysisTlv149(it) } ?: error("unknown login result type: $type") + else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type") } } @@ -331,11 +325,11 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo ): LoginPacketResponse.DeviceLockLogin { bot.client.t104 = tlvMap.getOrFail(0x104) // println("403: " + tlvMap[0x403]?.toUHexString()) - return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403)) + return LoginPacketResponse.DeviceLockLogin(tlvMap.getOrFail(0x402)) } private fun onUnsafeDeviceLogin(tlvMap: TlvMap): LoginPacketResponse.UnsafeLogin { - return LoginPacketResponse.UnsafeLogin(tlvMap.getOrFail(0x204).toReadPacket().readRemainingBytes().encodeToString()) + return LoginPacketResponse.UnsafeLogin(tlvMap.getOrFail(0x204).toReadPacket().readBytes().encodeToString()) } private fun onErrorMessage(tlvMap: TlvMap): LoginPacketResponse.Error { @@ -369,7 +363,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo imageData.discardExact(2)//image Length val sign = imageData.readBytes(signInfoLength.toInt()) return LoginPacketResponse.Captcha.Picture( - data = imageData.readRemainingBytes().toIoBuffer(), + data = imageData.readBytes().toIoBuffer(), sign = sign ) } else error("UNKNOWN CAPTCHA QUESTION: $question") @@ -652,11 +646,6 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo pwdFlag = readByte().toInt() == 1 } - /** - */ - private fun QQAndroidClient.analysisTlv528(t528: ByteArray) = t528.read { - } - /** * 设置 [QQAndroidClient.uin] */ @@ -696,7 +685,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo /** * 错误消息 */ - private fun QQAndroidClient.analysisTlv149(t149: ByteArray): LoginPacketResponse.Error { + private fun analysisTlv149(t149: ByteArray): LoginPacketResponse.Error { return t149.read { discardExact(2) //type diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt index 58fd3faa4..ef2a81cf0 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt @@ -114,6 +114,16 @@ fun ByteReadPacket.decodeMultiClientToServerPackets() { println() } +fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) = + debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys { + when (keyLength) { + 1 -> it.key.toUByte().contentToString() + 2 -> it.key.toUShort().contentToString() + 4 -> it.key.toUInt().contentToString() + else -> error("Expecting 1, 2 or 4 for keyLength") + } + }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n")) + fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed", { buildPacket { writeInt(it.size + 4); writeFully(it) } }) { val flag1 = readInt() println("flag1=" + flag1.contentToString()) @@ -139,7 +149,7 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed println("uin=" + readString(readInt() - 4)) println("// 解密 body") - val encrypted = readRemainingBytes() + val encrypted = readBytes() val decrypted = encrypted.tryDecryptOrNull() if (decrypted == null) { diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt index 638a7156d..98e12b425 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt @@ -198,6 +198,10 @@ private fun ByteReadPacket.parseOicqResponse(body: ByteReadPacket.() -> Unit) { } } +fun ByteReadPacket.readIoBuffer( + n: Int = remaining.toInt()//not that safe but adequate +): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) } + /** * 解析 SSO 层包装 */ diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt index 12bcdde20..e50f9c0c2 100644 --- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt @@ -1,17 +1,18 @@ package net.mamoe.mirai.utils.cryptor +import net.mamoe.mirai.utils.MiraiDebugAPI import java.lang.reflect.Field import kotlin.reflect.full.allSuperclasses +@MiraiDebugAPI actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String { - val newPrefix = prefix return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") } .distinctBy { it.name } .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" } .joinToStringPrefixed( - prefix = newPrefix + prefix = prefix ) { it.isAccessible = true if (filter != null) { @@ -22,7 +23,7 @@ actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: Strin it.name + "=" + kotlin.runCatching { val value = it.get(this) if (value == this) "<this>" - else value.contentToString(newPrefix) + else value.contentToString(prefix) }.getOrElse { "<!>" } } + "\n$prefix}" } 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 2c369a46b..fb8cb848f 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -134,8 +134,6 @@ abstract class Bot : CoroutineScope { * 不建议调用这个函数. * * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login] - * - * @throws LoginFailedException */ abstract suspend fun login() // endregion diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt index bab878901..a890f562f 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt @@ -4,7 +4,6 @@ package net.mamoe.mirai -import net.mamoe.mirai.utils.LoginFailedException import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -15,8 +14,6 @@ import kotlin.jvm.JvmName //Contacts /** * 登录, 返回 [this] - * - * @throws LoginFailedException */ suspend inline fun <B: Bot> B.alsoLogin(): B = also { login() } 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 913270bed..07e795fab 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 @@ -7,6 +7,7 @@ import kotlin.coroutines.EmptyCoroutineContext import kotlin.jvm.JvmStatic /** + * 验证码, 设备锁解决器 */ abstract class LoginSolver { abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt deleted file mode 100644 index 13f67f445..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt +++ /dev/null @@ -1,8 +0,0 @@ -package net.mamoe.mirai.utils - -import kotlinx.io.core.BytePacketBuilder -import kotlinx.io.core.toByteArray -import kotlinx.io.core.writeFully -import net.mamoe.mirai.utils.io.getRandomByteArray - -fun md5(str: String): ByteArray = md5(str.toByteArray()) \ No newline at end of file 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 171f232b9..011346d81 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 @@ -80,7 +80,7 @@ class ExternalImage( suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) { is Group -> contact.uploadImage(this).sendTo(contact) is QQ -> contact.uploadImage(this).sendTo(contact) - else -> assertUnreachable() + else -> error("unreachable") } /** @@ -92,7 +92,7 @@ suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) { suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) { is Group -> contact.uploadImage(this) is QQ -> contact.uploadImage(this) - else -> assertUnreachable() + else -> error("unreachable") } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt deleted file mode 100644 index 85b2e68d1..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt +++ /dev/null @@ -1,8 +0,0 @@ -package net.mamoe.mirai.utils - -import net.mamoe.mirai.data.LoginResult - -class LoginFailedException( - val result: LoginResult, - message: String = "Login failed with reason $result" -) : RuntimeException(message) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt index eb2d0891d..dfec87bd1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package net.mamoe.mirai.utils import net.mamoe.mirai.Bot @@ -139,7 +141,7 @@ interface MiraiLogger { /** * 当前平台的默认的日志记录器. * 在 _JVM 控制台_ 端的实现为 [println] - * 在 _Android_ 端的实现为 [android.util.Log] + * 在 _Android_ 端的实现为 `android.util.Log` * * 不应该直接构造这个类的实例. 请使用 [DefaultLogger], 或使用默认的顶层日志记录 [MiraiLogger.Companion] */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt index 1db24b31f..061921504 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt @@ -1,5 +1,11 @@ +@file:JvmMultifileClass +@file:JvmName("Utils") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName + /** * 要求 [this] 最小为 [min]. */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt index b6e33e620..c9183a133 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt @@ -1,5 +1,11 @@ +@file:JvmMultifileClass +@file:JvmName("Utils") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName + /** * 图片文件过大 */ diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt index ea91627d7..a1b75697b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt @@ -1,10 +1,11 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE") package net.mamoe.mirai.utils import io.ktor.client.HttpClient import io.ktor.util.date.GMTDate import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.io.core.toByteArray /** * 时间戳 @@ -36,6 +37,8 @@ expect fun ByteArray.unzip(offset: Int = 0, length: Int = this.size - offset): B */ expect fun md5(byteArray: ByteArray): ByteArray +inline fun md5(str: String): ByteArray = md5(str.toByteArray()) + /** * hostname 解析 ipv4 */ @@ -53,7 +56,7 @@ expect val Http: HttpClient expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher -internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int){ +internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) { require(offset >= 0) { "offset shouldn't be negative: $offset" } require(length >= 0) { "length shouldn't be negative: $length" } require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt index d2287fb19..02b730187 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt @@ -1,4 +1,4 @@ -@file:Suppress("unused", "FunctionName") +@file:Suppress("unused", "FunctionName", "NOTHING_TO_INLINE") package net.mamoe.mirai.utils @@ -16,7 +16,7 @@ import kotlin.coroutines.EmptyCoroutineContext * val image: Deferred<Image> by suspendLazy{ /* intializer */ } * ``` */ -fun <R> CoroutineScope.suspendLazy(context: CoroutineContext = EmptyCoroutineContext, initializer: suspend () -> R): Lazy<Deferred<R>> = +inline fun <R> CoroutineScope.suspendLazy(context: CoroutineContext = EmptyCoroutineContext, noinline initializer: suspend () -> R): Lazy<Deferred<R>> = SuspendLazy(this, context, initializer) /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt deleted file mode 100644 index 1f898030c..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt +++ /dev/null @@ -1,9 +0,0 @@ -package net.mamoe.mirai.utils - -/** - * 仅用于测试时标记, 未来会删除 - * - * @author Him188moe - */ -@Suppress("unused") -internal annotation class Tested(val date: String = "") \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt index 2bf5aeb78..09d3380e7 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt @@ -1,5 +1,10 @@ +@file:JvmMultifileClass +@file:JvmName("Utils") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName import kotlin.time.seconds // 临时使用, 待 Kotlin Duration 稳定后使用 Duration. diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt deleted file mode 100644 index 49f3c7601..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt +++ /dev/null @@ -1,7 +0,0 @@ -package net.mamoe.mirai.utils - -/** - * 表示这里是不可到达的位置. - */ -@Suppress("NOTHING_TO_INLINE") -internal inline fun assertUnreachable(): Nothing = error("This clause should not be reached") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt deleted file mode 100644 index 666fd6450..000000000 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt +++ /dev/null @@ -1,66 +0,0 @@ -package net.mamoe.mirai.utils.cryptor - -import kotlinx.io.core.BytePacketBuilder -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.IoBuffer -import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.io.ByteArrayPool -import net.mamoe.mirai.utils.io.encryptAndWrite -import net.mamoe.mirai.utils.io.toReadPacket - - -/** - * [ByteArray] 解密器 - */ -interface DecrypterByteArray : Decrypter { - val value: ByteArray - override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = input.decryptBy(value, offset, length) -} - -/** - * [IoBuffer] 解密器 - */ -interface DecrypterIoBuffer : Decrypter { - val value: IoBuffer - override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = input.decryptBy(value, offset, length) -} - -/** - * 连接在一起的解密器 - */ -inline class LinkedDecrypter(inline val block: (input: ByteReadPacket, offset: Int, length: Int) -> ByteReadPacket) : Decrypter { - override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = block(input, offset, length) -} - -object NoDecrypter : Decrypter, DecrypterType<NoDecrypter> { - override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket { - if (offset == 0 && length == input.remaining.toInt()) { - return input - } - - ByteArrayPool.useInstance { buffer -> - input.readFully(buffer, offset, length) - return buffer.toReadPacket() - } - } -} - -fun Decrypter.decrypt(input: ByteReadPacket): ByteReadPacket = this.decrypt(input, 0, input.remaining.toInt()) - -/** - * 解密器 - */ -interface Decrypter { - // do not write with default args. NoSuchMethodError when inline classes override this function - fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket - /** - * 连接后将会先用 this 解密, 再用 [another] 解密 - */ - operator fun plus(another: Decrypter): Decrypter = - LinkedDecrypter { input: ByteReadPacket, offset: Int, length: Int -> another.decrypt(this.decrypt(input, offset, length)) } -} - -interface DecrypterType<D : Decrypter> - -inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) = - this.encryptAndWrite(key.value, encoder) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt index 931789a6b..21604652a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt @@ -6,6 +6,9 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.readBytes import kotlinx.io.core.readUInt import kotlinx.io.core.readULong +import net.mamoe.mirai.utils.MiraiDebugAPI +import net.mamoe.mirai.utils.MiraiExperimentalAPI +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.* import kotlin.jvm.JvmStatic @@ -24,12 +27,14 @@ import kotlin.jvm.JvmStatic * * https://www.jianshu.com/p/f888907adaeb */ +@MiraiDebugAPI fun ProtoFieldId(serializedId: UInt): ProtoFieldId = ProtoFieldId( protoFieldNumber(serializedId), protoType(serializedId) ) +@MiraiDebugAPI data class ProtoFieldId( val fieldNumber: Int, val type: ProtoType @@ -38,6 +43,7 @@ data class ProtoFieldId( } @Suppress("SpellCheckingInspection") +@MiraiDebugAPI enum class ProtoType(val value: Byte, private val typeName: String) { /** * int32, int64, uint32, uint64, sint32, sint64, bool, enum @@ -82,6 +88,7 @@ enum class ProtoType(val value: Byte, private val typeName: String) { * * serializedId = (fieldNumber << 3) | wireType */ +@MiraiDebugAPI fun protoType(number: UInt): ProtoType = ProtoType.valueOf(number.toInt().shl(29).ushr(29).toByte()) @@ -90,9 +97,10 @@ fun protoType(number: UInt): ProtoType = * * serializedId = (fieldNumber << 3) | wireType */ +@MiraiDebugAPI fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3) - +@MiraiDebugAPI class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, Any> by map { companion object { @JvmStatic @@ -120,6 +128,7 @@ class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, An /** * 将所有元素加入转换为多行的字符串表示. */ +@MiraiDebugAPI fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String { return this.joinToString(prefix = "$prefix${ProtoMap.indent}", separator = "\n$prefix${ProtoMap.indent}", transform = transform) } @@ -135,6 +144,7 @@ fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharS * `data class`: 调用其 [toString] * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [contentToString]. 嵌套结构将会以缩进表示 */ +@MiraiDebugAPI("Extremely slow") fun Any?.contentToString(prefix: String = ""): String = when (this) { is Unit -> "Unit" is UInt -> "0x" + this.toUHexString("") + "($this)" @@ -222,8 +232,11 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) { } } +@MiraiExperimentalAPI("Extremely slow") +@MiraiDebugAPI("Extremely slow") expect fun Any.contentToStringReflectively(prefix: String = "", filter: ((String, Any?) -> Boolean)? = null): String +@MiraiDebugAPI @Suppress("UNCHECKED_CAST") fun ByteReadPacket.readProtoMap(length: Long = this.remaining): ProtoMap { val map = ProtoMap(mutableMapOf()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt index e304fccfd..7e63fa014 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt @@ -1,8 +1,11 @@ -@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("Utils") package net.mamoe.mirai.utils.io 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 @@ -10,6 +13,8 @@ import net.mamoe.mirai.utils.checkOffsetAndLength import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmSynthetic @@ -80,12 +85,9 @@ fun UByteArray.toUHexString(separator: String = " ", offset: Int = 0, length: In } @Suppress("NOTHING_TO_INLINE") -inline fun ByteArray.encodeToString(): String = String(this) +inline fun ByteArray.encodeToString(charset: Charset = Charsets.UTF_8): String = String(this, charset = charset) -fun ByteArray.encodeToGBKString(): String = String(this, 0, this.size, Charset.forName("GBK")) - - -fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset) = +inline fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset) = ByteReadPacket(this, offset = offset, length = length) @UseExperimental(ExperimentalContracts::class) @@ -94,6 +96,4 @@ inline fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R { callsInPlace(t, InvocationKind.EXACTLY_ONCE) } return this.toReadPacket().use(t) -} - -fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length) \ No newline at end of file +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 36a9b56cf..dba0f3820 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -1,3 +1,7 @@ +@file:Suppress("NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("Utils") + package net.mamoe.mirai.utils.io import kotlinx.io.core.* @@ -9,29 +13,34 @@ import net.mamoe.mirai.utils.withSwitch import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.js.JsName +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +@MiraiDebugAPI("Unsatble") object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch() -fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this) +@MiraiDebugAPI("Unstable") +inline fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this) @MiraiDebugAPI("Low efficiency.") -fun debugPrintln(any: Any?) = DebugLogger.debug(any) +inline fun debugPrintln(any: Any?) = DebugLogger.debug(any) @MiraiDebugAPI("Low efficiency.") -fun String.debugPrintThis(name: String): String { +inline fun String.debugPrintThis(name: String): String { DebugLogger.debug("$name=$this") return this } @MiraiDebugAPI("Low efficiency.") -fun ByteArray.debugPrintThis(name: String): ByteArray { +inline fun ByteArray.debugPrintThis(name: String): ByteArray { DebugLogger.debug(name + "=" + this.toUHexString()) return this } @MiraiDebugAPI("Low efficiency.") -fun IoBuffer.debugPrintThis(name: String): IoBuffer { +inline fun IoBuffer.debugPrintThis(name: String): IoBuffer { ByteArrayPool.useInstance { val count = this.readAvailable(it) DebugLogger.debug(name + "=" + it.toUHexString(offset = 0, length = count)) @@ -49,12 +58,12 @@ inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer { } @MiraiDebugAPI("Low efficiency.") -fun Input.debugDiscardExact(n: Number, name: String = "") { +inline fun Input.debugDiscardExact(n: Number, name: String = "") { DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString()) } @MiraiDebugAPI("Low efficiency.") -fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket { +inline fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket { ByteArrayPool.useInstance { val count = this.readAvailable(it) DebugLogger.debug("ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count)) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt index 4cde854c4..4059e3169 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt @@ -1,4 +1,6 @@ @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("Utils") package net.mamoe.mirai.utils.io @@ -7,8 +9,10 @@ import kotlinx.io.charsets.Charset import kotlinx.io.charsets.Charsets import kotlinx.io.core.* import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.assertUnreachable +import net.mamoe.mirai.utils.MiraiDebugAPI +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.contentToString +import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic @@ -34,6 +38,7 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) { } } +@MiraiInternalAPI inline fun <R> ByteReadPacket.useBytes( n: Int = remaining.toInt(),//not that safe but adequate block: (data: ByteArray, length: Int) -> R @@ -42,51 +47,24 @@ inline fun <R> ByteReadPacket.useBytes( block(it, n) } -fun ByteReadPacket.readRemainingBytes( - n: Int = remaining.toInt()//not that safe but adequate -): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) } - -fun ByteReadPacket.readIoBuffer( - n: Int = remaining.toInt()//not that safe but adequate -): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) } - -fun ByteReadPacket.readPacket( +inline fun ByteReadPacket.readPacket( n: Int = remaining.toInt()//not that safe but adequate ): ByteReadPacket = this.readBytes(n).toReadPacket() -fun ByteReadPacket.readIoBuffer(n: Short) = this.readIoBuffer(n.toInt()) +inline fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray()) -fun Input.readIP(): String = buildString(4 + 3) { - repeat(4) { - val byte = readUByte() - this.append(byte.toString()) - if (it != 3) this.append(".") - } -} +inline fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) -fun Input.readQQ(): Long = this.readUInt().toLong() -fun Input.readGroup(): Long = this.readUInt().toLong() -fun Input.readGroupId(): Long = this.readUInt().toLong() -fun Input.readGroupCode(): Long = this.readUInt().toLong() +inline fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt()) -fun Input.readUVarIntLVString(): String = String(this.readUVarIntByteArray()) - -fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray()) - -fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) - -fun Input.readUVarIntByteArray(): ByteArray = this.readBytes(this.readUVarInt().toInt()) - -fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt()) - -fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) +inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) private inline fun <R> inline(block: () -> R): R = block() typealias TlvMap = MutableMap<Int, ByteArray> -fun TlvMap.getOrFail(tag: Int): ByteArray { +inline fun TlvMap.getOrFail(tag: Int): ByteArray { return this[tag] ?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)") } @@ -94,10 +72,12 @@ inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteAr return this[tag] ?: error(lazyMessage(tag)) } -fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize) +@MiraiDebugAPI +inline fun Input.readTLVMap(tagSize: Int = 2, suppressDuplication: Boolean = true): TlvMap = readTLVMap(true, tagSize, suppressDuplication) +@MiraiDebugAPI @Suppress("DuplicatedCode") -fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap { +fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplication: Boolean = true): TlvMap { val map = mutableMapOf<Int, ByteArray>() var key = 0 @@ -119,20 +99,22 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap { }.toUByte() != UByte.MAX_VALUE) { if (map.containsKey(key)) { - DebugLogger.error( - @Suppress("IMPLICIT_CAST_TO_ANY") - """ + if (!suppressDuplication) { + DebugLogger.error( + @Suppress("IMPLICIT_CAST_TO_ANY") + """ Error readTLVMap: duplicated key ${when (tagSize) { - 1 -> key.toByte() - 2 -> key.toShort() - 4 -> key - else -> assertUnreachable() - }.contentToString()} + 1 -> key.toByte() + 2 -> key.toShort() + 4 -> key + else -> error("unreachable") + }.contentToString()} map=${map.contentToString()} duplicating value=${this.readUShortLVByteArray().toUHexString()} """.trimIndent() - ) + ) + } } else { try { map[key] = this.readUShortLVByteArray() @@ -147,98 +129,10 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap { return map } -/** - * 读扁平的 tag-UVarInt map. 重复的 tag 将只保留最后一个 - * - * tag: UByte - * value: UVarint - */ -@Suppress("DuplicatedCode") -fun Input.readFlatTUVarIntMap(expectingEOF: Boolean = false, tagSize: Int = 1): MutableMap<UInt, UInt> { - val map = mutableMapOf<UInt, UInt>() - var type: UShort = 0u - - while (inline { - try { - type = when (tagSize) { - 1 -> readUByte().toUShort() - 2 -> readUShort() - else -> error("Unsupported tag size: $tagSize") - } - } catch (e: EOFException) { - if (expectingEOF) { - return map - } - throw e - } - type - }.toUByte() != UByte.MAX_VALUE) { - - if (map.containsKey(type.toUInt())) { - map[type.toUInt()] = this.readUVarInt() - } else { - map[type.toUInt()] = this.readUVarInt() - } - } - return map -} - -fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) = - debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys { - when (keyLength) { - 1 -> it.key.toUByte().contentToString() - 2 -> it.key.toUShort().contentToString() - 4 -> it.key.toUInt().contentToString() - else -> illegalArgument("Expecting 1, 2 or 4 for keyLength") - } - }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n")) - -internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported") - -internal inline fun illegalArgument(message: String? = null): Nothing = error(message ?: "Illegal argument passed") - -@JvmName("printTLVStringMap") -fun Map<Int, String>.printTLVMap(name: String = "") = - debugPrintln("TLVMap $name= " + this.mapKeys { it.key.toInt().toUShort().toUHexString() }) - -fun Input.readString(length: Int, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length), charset = charset) -fun Input.readString(length: Long, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) -fun Input.readString(length: Short, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) +inline fun Input.readString(length: Int, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length), charset = charset) +inline fun Input.readString(length: Long, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) +inline fun Input.readString(length: Short, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) @JvmSynthetic -fun Input.readString(length: UShort, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) +inline fun Input.readString(length: UShort, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) -fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) - -@JvmSynthetic -fun Input.readStringUntil(stopSignalExclude: UByte, expectingEOF: Boolean = false): String = readStringUntil(stopSignalExclude.toByte(), expectingEOF) - -@JvmName("readStringUntil0") -fun Input.readStringUntil(stopSignalExclude: Byte, expectingEOF: Boolean = false): String { - ByteArrayPool.useInstance { - var count = 0 - - val buffer = byteArrayOf(1) - while (readAvailable(buffer, 1) == 1) { - if (buffer[0] == stopSignalExclude) { - return buffer.encodeToString() - } - it[count++] = buffer[0] - } - if (!expectingEOF) { - throw EOFException("Early EOF") - } - return buffer.encodeToString() - } -} - -private const val TRUE_BYTE_VALUE: Byte = 1 -fun Input.readBoolean(): Boolean = this.readByte() == TRUE_BYTE_VALUE -fun Input.readLVNumber(): Number { - return when (this.readShort().toInt()) { - 1 -> this.readByte() - 2 -> this.readShort() - 4 -> this.readInt() - 8 -> this.readLong() - else -> throw UnsupportedOperationException() - } -} \ No newline at end of file +inline fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt index 6f940b792..24e612c00 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt @@ -1,27 +1,14 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("Utils") package net.mamoe.mirai.utils.io import kotlinx.io.core.* -import kotlinx.io.pool.useInstance -import net.mamoe.mirai.utils.Tested import net.mamoe.mirai.utils.coerceAtMostOrFail import net.mamoe.mirai.utils.cryptor.encryptBy -import net.mamoe.mirai.utils.currentTimeMillis -import net.mamoe.mirai.utils.deviceName -import kotlin.random.Random -import kotlin.random.nextInt - -fun BytePacketBuilder.writeZero(count: Int) { - require(count != 0) { "Trying to write zero with count 0, you made a mistake?" } - require(count > 0) { "writeZero: count must > 0" } - repeat(count) { this.writeByte(0) } -} - -fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) } - -fun BytePacketBuilder.writeQQ(qq: Long) = this.writeUInt(qq.toUInt()) // same bit rep. -fun BytePacketBuilder.writeGroup(groupId: Long) = this.writeUInt(groupId.toUInt()) +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) { if (array.size <= maxLength) { @@ -35,7 +22,7 @@ fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLe } } -fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int { +inline fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int { this.writeShort(byteArray.size.toShort()) this.writeFully(byteArray) return byteArray.size @@ -59,18 +46,7 @@ inline fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset return length.toInt() } -inline fun BytePacketBuilder.writeUVarIntLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long) = {it}, builder: BytePacketBuilder.() -> Unit) = - BytePacketBuilder().apply(builder).build().use { - if (tag != null) writeUByte(tag) - writeUVarInt(lengthOffset.invoke(it.remaining).coerceAtMostOrFail(0xFFFFL)) - writePacket(it) - } - -fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray()) - -fun BytePacketBuilder.writeIP(ip: String) = writeFully(ip.trim().split(".").map { it.toUByte() }.toUByteArray()) - -fun BytePacketBuilder.writeTime() = this.writeInt(currentTimeMillis.toInt()) +inline fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray()) fun BytePacketBuilder.writeHex(uHex: String) { uHex.split(" ").forEach { @@ -79,76 +55,8 @@ fun BytePacketBuilder.writeHex(uHex: String) { } } } - - -fun BytePacketBuilder.writeTLV(tag: UByte, values: UByteArray) { - writeUByte(tag) - writeUVarInt(values.size.toUInt()) - writeFully(values) -} - -fun BytePacketBuilder.writeTLV(tag: UByte, values: ByteArray) { - writeUByte(tag) - writeUVarInt(values.size.toUInt()) - writeFully(values) -} - -fun BytePacketBuilder.writeTHex(tag: UByte, uHex: String) { - this.writeUByte(tag) - this.writeFully(uHex.hexToUBytes()) -} - -fun BytePacketBuilder.writeTV(tagValue: UShort) = writeUShort(tagValue) - -fun BytePacketBuilder.writeTV(tag: UByte, value: UByte) { - writeUByte(tag) - writeUByte(value) -} - -fun BytePacketBuilder.writeTUbyte(tag: UByte, value: UByte) { - this.writeUByte(tag) - this.writeUByte(value) -} - -fun BytePacketBuilder.writeTUVarint(tag: UByte, value: UInt) { - this.writeUByte(tag) - this.writeUVarInt(value) -} - -fun BytePacketBuilder.writeTByteArray(tag: UByte, value: ByteArray) { - this.writeUByte(tag) - this.writeFully(value) -} - -fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) { - this.writeUByte(tag) - this.writeFully(value) -} - /** * 会使用 [ByteArrayPool] 缓存 */ inline fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = - BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) } - -inline fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = ByteArrayPool.useInstance { - key.readFully(it, 0, key.readRemaining) - encryptAndWrite(it, encoder) -} - -inline fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder) - -@Tested -fun BytePacketBuilder.writeDeviceName(random: Boolean) { - val deviceName: String = if (random) { - "DESKTOP-" + String(ByteArray(7) { - (if (Random.nextBoolean()) Random.nextInt('A'.toInt()..'Z'.toInt()) - else Random.nextInt('1'.toInt()..'9'.toInt())).toByte() - }) - } else { - deviceName - } - this.writeShort((deviceName.length + 2).toShort()) - this.writeShort(deviceName.length.toShort()) - this.writeStringUtf8(deviceName) -} \ No newline at end of file + BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt index f3c656ca1..e8c8d850d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt @@ -16,8 +16,8 @@ import kotlin.jvm.JvmSynthetic * * Source project: [Nukkit](http://github.com/nukkit/nukkit) * - * @author MagicDroidX of Nukkit Project - * @author lmlstarqaq of Nukkit Project + * @author MagicDroidX from Nukkit Project + * @author lmlstarqaq from Nukkit Project */ internal fun encodeZigZag32(signedInt: Int): Long { @@ -42,10 +42,6 @@ internal fun decodeZigZag64(signedLong: Long): Long { } -fun Input.readVarInt(): Int { - return decodeZigZag32(this.readUVarInt()) -} - inline class UVarInt( val data: UInt ) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt index 0e9710358..f0506092e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt @@ -1,9 +1,17 @@ +@file:Suppress("NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("Utils") + + package net.mamoe.mirai.utils -fun <K, V> Map<K, V>.firstValue(): V = this.entries.first().value +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName -fun <K, V> Map<K, V>.firstValueOrNull(): V? = this.entries.firstOrNull()?.value +inline fun <K, V> Map<K, V>.firstValue(): V = this.entries.first().value -fun <K, V> Map<K, V>.firstKey(): K = this.entries.first().key +inline fun <K, V> Map<K, V>.firstValueOrNull(): V? = this.entries.firstOrNull()?.value -fun <K, V> Map<K, V>.firstKeyOrNull(): K? = this.entries.firstOrNull()?.key \ No newline at end of file +inline fun <K, V> Map<K, V>.firstKey(): K = this.entries.first().key + +inline fun <K, V> Map<K, V>.firstKeyOrNull(): K? = this.entries.firstOrNull()?.key \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt index 6bcf26598..c17f8f74b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt @@ -28,7 +28,7 @@ import kotlin.coroutines.CoroutineContext actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver() -class DefaultLoginSolver : LoginSolver() { +internal class DefaultLoginSolver : LoginSolver() { override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock { val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } withContext(Dispatchers.IO) { diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt index b6aa1ab05..a59a5b0ca 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt @@ -1,11 +1,13 @@ package net.mamoe.mirai.utils.cryptor +import net.mamoe.mirai.utils.MiraiDebugAPI import java.lang.reflect.Field import kotlin.reflect.full.allSuperclasses val FIELD_TRY_SET_ACCESSIBLE = Field::class.java.declaredMethods.firstOrNull { it.name == "trySetAccessible" } +@MiraiDebugAPI actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String { val newPrefix = prefix + ProtoMap.indent return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" + diff --git a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt index f35414fb6..36992d2f3 100644 --- a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt +++ b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt @@ -16,7 +16,6 @@ import kotlinx.io.core.readBytes import net.mamoe.mirai.Bot import net.mamoe.mirai.event.subscribeMessages import net.mamoe.mirai.qqandroid.QQAndroid -import net.mamoe.mirai.utils.LoginFailedException import net.mamoe.mirai.utils.LoginSolver import java.lang.ref.WeakReference @@ -67,7 +66,7 @@ class MiraiService : Service() { try { login() mCallback?.get()?.onSuccess() - } catch (e: LoginFailedException) { + } catch (e: Exception) { mCallback?.get()?.onFailed() } } diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java index 7deeefd30..49996572a 100644 --- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java +++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java @@ -77,10 +77,7 @@ public interface BlockingBot { * 登录. * <p> * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login] - * - * @throws net.mamoe.mirai.utils.LoginFailedException */ - @SuppressWarnings("JavaDoc") void login(); // endregion