mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-25 15:00:09 +08:00
Cleanup
This commit is contained in:
parent
9aa1d7d395
commit
388e1a5b7f
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||
|
@ -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" }
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 = ""
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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 层包装
|
||||
*/
|
||||
|
@ -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}"
|
||||
}
|
||||
|
@ -134,8 +134,6 @@ abstract class Bot : CoroutineScope {
|
||||
* 不建议调用这个函数.
|
||||
*
|
||||
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
|
||||
*
|
||||
* @throws LoginFailedException
|
||||
*/
|
||||
abstract suspend fun login()
|
||||
// endregion
|
||||
|
@ -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() }
|
||||
|
||||
|
@ -7,6 +7,7 @@ import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* 验证码, 设备锁解决器
|
||||
*/
|
||||
abstract class LoginSolver {
|
||||
abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String?
|
||||
|
@ -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())
|
@ -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")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)
|
@ -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]
|
||||
*/
|
||||
|
@ -1,5 +1,11 @@
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("Utils")
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* 要求 [this] 最小为 [min].
|
||||
*/
|
||||
|
@ -1,5 +1,11 @@
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("Utils")
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
/**
|
||||
* 图片文件过大
|
||||
*/
|
||||
|
@ -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})" }
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -1,9 +0,0 @@
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
/**
|
||||
* 仅用于测试时标记, 未来会删除
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
@Suppress("unused")
|
||||
internal annotation class Tested(val date: String = "")
|
@ -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.
|
||||
|
@ -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")
|
@ -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)
|
@ -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())
|
||||
|
@ -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)
|
||||
}
|
@ -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))
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
inline fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
|
@ -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)
|
||||
}
|
||||
BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) }
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
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
|
@ -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) {
|
||||
|
@ -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" +
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user