mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-20 20:49:54 +08:00
Fast login (#1154)
* wtlogin10 * Fast login Packet Implement (#1125) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Refresh Token when exchanging and solve connection dropping issue (#1128) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update (#1131) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update * Add login lock * Add login lock * Remove key refresh * Remove heartbeat period override * Login: Update tlv and solve constant connection dropping issue (#1150) * Correct group syncing logic again, Fix #1120 * Implement fast login packet, thanks to MiraiGo * Delete duplicated tlv * Schedule token exchanging every 10 minutes, solve connection dropping issue * Refresh Token when exchanging, and correct token expire time * Remove useless params for doFastLogin * Fix missed register and tgt update * Update tlv, add tlv11d and tlv11a decoding * Add stat heartbeat, solve constant connection dropping issue * Update apidump for new configuration * Add comment for statHeartbeatPeriodMillis * Change old naming * Add since version Co-authored-by: Him188 <Him188@mamoe.net> Co-authored-by: sandtechnology <20417547+sandtechnology@users.noreply.github.com>
This commit is contained in:
parent
f6fd4de14b
commit
ea1f43b9c5
@ -5518,6 +5518,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
|
||||
public final fun getProtocol ()Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
|
||||
public final fun getReconnectPeriodMillis ()J
|
||||
public final fun getReconnectionRetryTimes ()I
|
||||
public final fun getStatHeartbeatPeriodMillis ()J
|
||||
public final fun getWorkingDir ()Ljava/io/File;
|
||||
public final synthetic fun inheritCoroutineContext (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun isConvertLineSeparator ()Z
|
||||
@ -5560,6 +5561,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
|
||||
public final fun setProtocol (Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;)V
|
||||
public final fun setReconnectPeriodMillis (J)V
|
||||
public final fun setReconnectionRetryTimes (I)V
|
||||
public final fun setStatHeartbeatPeriodMillis (J)V
|
||||
public final fun setWorkingDir (Ljava/io/File;)V
|
||||
}
|
||||
|
||||
|
@ -5518,6 +5518,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
|
||||
public final fun getProtocol ()Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
|
||||
public final fun getReconnectPeriodMillis ()J
|
||||
public final fun getReconnectionRetryTimes ()I
|
||||
public final fun getStatHeartbeatPeriodMillis ()J
|
||||
public final fun getWorkingDir ()Ljava/io/File;
|
||||
public final synthetic fun inheritCoroutineContext (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public final fun isConvertLineSeparator ()Z
|
||||
@ -5560,6 +5561,7 @@ public class net/mamoe/mirai/utils/BotConfiguration {
|
||||
public final fun setProtocol (Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;)V
|
||||
public final fun setReconnectPeriodMillis (J)V
|
||||
public final fun setReconnectionRetryTimes (I)V
|
||||
public final fun setStatHeartbeatPeriodMillis (J)V
|
||||
public final fun setWorkingDir (Ljava/io/File;)V
|
||||
}
|
||||
|
||||
|
@ -142,9 +142,16 @@ public open class BotConfiguration { // open for Java
|
||||
// Connection
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** 心跳周期. 过长会导致被服务器断开连接. */
|
||||
/** 连接心跳包周期. 过长会导致被服务器断开连接. */
|
||||
public var heartbeatPeriodMillis: Long = 60.secondsToMillis
|
||||
|
||||
/**
|
||||
* 状态心跳包周期. 过长会导致掉线.
|
||||
* 该值会在登录时根据服务器下发的配置自动进行更新.
|
||||
* @since 2.6
|
||||
*/
|
||||
public var statHeartbeatPeriodMillis: Long = 300.secondsToMillis
|
||||
|
||||
/**
|
||||
* 每次心跳时等待结果的时间.
|
||||
* 一旦心跳超时, 整个网络服务将会重启 (将消耗约 1s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响.
|
||||
|
@ -35,10 +35,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin15
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin2
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin20
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.WtLogin9
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin.*
|
||||
import net.mamoe.mirai.internal.utils.*
|
||||
import net.mamoe.mirai.network.*
|
||||
import net.mamoe.mirai.utils.*
|
||||
@ -63,6 +60,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
|
||||
private var _packetReceiverJob: Job? = null
|
||||
private var heartbeatJob: Job? = null
|
||||
private var statHeartbeatJob: Job? = null
|
||||
|
||||
private val packetReceiveLock: Mutex = Mutex()
|
||||
|
||||
@ -96,6 +94,25 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
}.also { _packetReceiverJob = it }
|
||||
}
|
||||
|
||||
private fun startStatHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||
statHeartbeatJob?.cancel(cancelCause)
|
||||
|
||||
return this@QQAndroidBotNetworkHandler.launch(CoroutineName("statHeartbeatJob")) statHeartbeatJob@{
|
||||
while (this.isActive) {
|
||||
delay(bot.configuration.statHeartbeatPeriodMillis)
|
||||
val failException = doStatHeartbeat()
|
||||
if (failException != null) {
|
||||
delay(bot.configuration.firstReconnectDelayMillis)
|
||||
|
||||
bot.launch {
|
||||
BotOfflineEvent.Dropped(bot, failException).broadcast()
|
||||
}
|
||||
return@statHeartbeatJob
|
||||
}
|
||||
}
|
||||
}.also { statHeartbeatJob = it }
|
||||
}
|
||||
|
||||
private fun startHeartbeatJobOrKill(cancelCause: CancellationException? = null): Job {
|
||||
heartbeatJob?.cancel(cancelCause)
|
||||
|
||||
@ -121,6 +138,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
override suspend fun closeEverythingAndRelogin(host: String, port: Int, cause: Throwable?, step: Int) {
|
||||
heartbeatJob?.cancel(CancellationException("relogin", cause))
|
||||
heartbeatJob?.join()
|
||||
statHeartbeatJob?.cancel(CancellationException("relogin", cause))
|
||||
statHeartbeatJob?.join()
|
||||
_packetReceiverJob?.cancel(CancellationException("relogin", cause))
|
||||
_packetReceiverJob?.join()
|
||||
if (::channel.isInitialized) {
|
||||
@ -134,7 +153,6 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
}
|
||||
|
||||
channel = PlatformSocket()
|
||||
bot.initClient()
|
||||
|
||||
while (isActive) {
|
||||
try {
|
||||
@ -156,9 +174,81 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info { "Connected to server $host:$port" }
|
||||
if (bot.client.wLoginSigInfoInitialized) {
|
||||
// do fast login
|
||||
} else {
|
||||
bot.initClient()
|
||||
}
|
||||
|
||||
startPacketReceiverJobOrKill(CancellationException("relogin", cause))
|
||||
|
||||
if (bot.client.wLoginSigInfoInitialized) {
|
||||
// do fast login
|
||||
kotlin.runCatching {
|
||||
doFastLogin()
|
||||
}.onFailure {
|
||||
bot.initClient()
|
||||
doSlowLogin(host, port, cause, step)
|
||||
}
|
||||
} else {
|
||||
doSlowLogin(host, port, cause, step)
|
||||
}
|
||||
|
||||
|
||||
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
|
||||
registerClientOnline()
|
||||
startStatHeartbeatJobOrKill()
|
||||
startHeartbeatJobOrKill()
|
||||
bot.eventChannel.subscribeOnce<BotOnlineEvent>(this.coroutineContext) {
|
||||
val bot = (bot as QQAndroidBot)
|
||||
if (bot.firstLoginSucceed && bot.client.wLoginSigInfoInitialized) {
|
||||
launch {
|
||||
while (isActive) {
|
||||
bot.client.wLoginSigInfo.vKey.run {
|
||||
//由过期时间最短的且不会被skey更换更新的vkey计算重新登录的时间
|
||||
val delay = (expireTime - creationTime).seconds - 5.minutes
|
||||
logger.info { "Scheduled refresh login session in ${delay.toHumanReadableString()}." }
|
||||
delay(delay)
|
||||
}
|
||||
runCatching {
|
||||
doFastLogin()
|
||||
registerClientOnline()
|
||||
}.onFailure {
|
||||
logger.warning("Failed to refresh login session.", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
while (isActive) {
|
||||
bot.client.wLoginSigInfo.sKey.run {
|
||||
val delay = (expireTime - creationTime).seconds - 5.minutes
|
||||
logger.info { "Scheduled key refresh in ${delay.toHumanReadableString()}." }
|
||||
delay(delay)
|
||||
}
|
||||
runCatching {
|
||||
refreshKeys()
|
||||
}.onFailure {
|
||||
logger.error("Failed to refresh key.", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val fastLoginOrSendPacketLock = Mutex()
|
||||
|
||||
private suspend fun doFastLogin(): Boolean {
|
||||
fastLoginOrSendPacketLock.withLock {
|
||||
val login10 = WtLogin10(bot.client).sendAndExpect(ignoreLock = true)
|
||||
return login10 is WtLogin.Login.LoginPacketResponse.Success
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun doSlowLogin(host: String, port: Int, cause: Throwable?, step: Int) {
|
||||
|
||||
fun LoginSolver?.notnull(): LoginSolver {
|
||||
checkNotNull(this) {
|
||||
"No LoginSolver found. Please provide by BotConfiguration.loginSolver. " +
|
||||
@ -263,24 +353,6 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
}
|
||||
}
|
||||
|
||||
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
|
||||
registerClientOnline()
|
||||
startHeartbeatJobOrKill()
|
||||
|
||||
launch {
|
||||
while (isActive) {
|
||||
bot.client.wLoginSigInfo.sKey.run {
|
||||
val delay = (expireTime - creationTime).seconds - 5.minutes
|
||||
logger.info { "Scheduled key refresh in ${delay.toHumanReadableString()}." }
|
||||
delay(delay)
|
||||
}
|
||||
runCatching {
|
||||
refreshKeys()
|
||||
}.onFailure {
|
||||
logger.error("Failed to refresh key.", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun refreshKeys() {
|
||||
@ -429,6 +501,17 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
logger.info { "Syncing friend message history: Success." }
|
||||
}
|
||||
|
||||
private suspend fun doStatHeartbeat(): Throwable? {
|
||||
return retryCatching(2) {
|
||||
StatSvc.SimpleGet(bot.client)
|
||||
.sendAndExpect<StatSvc.SimpleGet.Response>(
|
||||
timeoutMillis = bot.configuration.heartbeatTimeoutMillis,
|
||||
retry = 2
|
||||
)
|
||||
return null
|
||||
}.exceptionOrNull()
|
||||
}
|
||||
|
||||
private suspend fun doHeartBeat(): Throwable? {
|
||||
return retryCatching(2) {
|
||||
Heartbeat.Alive(bot.client)
|
||||
@ -675,16 +758,27 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
||||
|
||||
suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect(
|
||||
timeoutMillis: Long = 5000,
|
||||
retry: Int = 2
|
||||
retry: Int = 2,
|
||||
ignoreLock: Boolean = false,
|
||||
): E {
|
||||
return (this as OutgoingPacket).sendAndExpect(timeoutMillis, retry)
|
||||
return (this as OutgoingPacket).sendAndExpect(timeoutMillis, retry, ignoreLock)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一个包, 挂起协程直到接收到指定的返回包或超时
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E {
|
||||
suspend fun <E : Packet> OutgoingPacket.sendAndExpect(
|
||||
timeoutMillis: Long = 5000,
|
||||
retry: Int = 2,
|
||||
ignoreLock: Boolean = false
|
||||
): E {
|
||||
return if (!ignoreLock) fastLoginOrSendPacketLock.withLock {
|
||||
sendAndExpectImpl(timeoutMillis, retry)
|
||||
} else sendAndExpectImpl(timeoutMillis, retry)
|
||||
}
|
||||
|
||||
private suspend fun <E : Packet> OutgoingPacket.sendAndExpectImpl(timeoutMillis: Long, retry: Int): E {
|
||||
require(timeoutMillis > 100) { "timeoutMillis must > 100" }
|
||||
require(retry in 0..10) { "retry must in 0..10" }
|
||||
|
||||
|
@ -13,6 +13,7 @@ import kotlinx.io.core.ByteReadPacket
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.io.core.readUShort
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.internal.network.getRandomByteArray
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.PacketLogger
|
||||
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||
@ -111,8 +112,19 @@ internal class WLoginSigInfo(
|
||||
// val pt4Token: ByteArray,
|
||||
var wtSessionTicket: WtSessionTicket,
|
||||
var wtSessionTicketKey: ByteArray,
|
||||
var deviceToken: ByteArray
|
||||
var deviceToken: ByteArray,
|
||||
var encryptedDownloadSession: EncryptedDownloadSession? = null
|
||||
) {
|
||||
|
||||
//图片加密下载
|
||||
//是否加密从bigdatachannel处得知
|
||||
@Serializable
|
||||
internal class EncryptedDownloadSession(
|
||||
val appId: Long,//1600000226L
|
||||
val stKey: ByteArray,
|
||||
val stSig: ByteArray
|
||||
)
|
||||
|
||||
override fun toString(): String {
|
||||
return "WLoginSigInfo(uin=$uin, encryptA1=${encryptA1?.toUHexString()}, noPicSig=${noPicSig?.toUHexString()}, simpleInfo=$simpleInfo, appPri=$appPri, a2ExpiryTime=$a2ExpiryTime, loginBitmap=$loginBitmap, tgt=${tgt.toUHexString()}, a2CreationTime=$a2CreationTime, tgtKey=${tgtKey.toUHexString()}, userStSig=$userStSig, userStKey=${userStKey.toUHexString()}, userStWebSig=$userStWebSig, userA5=$userA5, userA8=$userA8, lsKey=$lsKey, sKey=$sKey, userSig64=$userSig64, openId=${openId.toUHexString()}, openKey=$openKey, vKey=$vKey, accessToken=$accessToken, d2=$d2, d2Key=${d2Key.toUHexString()}, sid=$sid, aqSig=$aqSig, psKey=$psKeyMap, superKey=${superKey.toUHexString()}, payToken=${payToken.toUHexString()}, pf=${pf.toUHexString()}, pfKey=${pfKey.toUHexString()}, da2=${da2.toUHexString()}, wtSessionTicket=$wtSessionTicket, wtSessionTicketKey=${wtSessionTicketKey.toUHexString()}, deviceToken=${deviceToken.toUHexString()})"
|
||||
}
|
||||
|
@ -127,6 +127,7 @@ internal object KnownPacketFactories {
|
||||
WtLogin.ExchangeEmp,
|
||||
StatSvc.Register,
|
||||
StatSvc.GetOnlineStatus,
|
||||
StatSvc.SimpleGet,
|
||||
StatSvc.GetDevLoginInfo,
|
||||
MessageSvcPbGetMsg,
|
||||
MessageSvcPushForceOffline,
|
||||
|
@ -222,6 +222,16 @@ internal fun BytePacketBuilder.t100(
|
||||
} shouldEqualsTo 22
|
||||
}
|
||||
|
||||
internal fun BytePacketBuilder.t10a(
|
||||
tgt: ByteArray,
|
||||
) {
|
||||
writeShort(0x10a)
|
||||
writeShortLVPacket {
|
||||
writeFully(tgt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun BytePacketBuilder.t107(
|
||||
picType: Int,
|
||||
capType: Int = 0,
|
||||
@ -326,6 +336,15 @@ internal fun BytePacketBuilder.t142(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BytePacketBuilder.t143(
|
||||
d2: ByteArray
|
||||
) {
|
||||
writeShort(0x143)
|
||||
writeShortLVPacket {
|
||||
writeFully(d2)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BytePacketBuilder.t112(
|
||||
nonNumberUin: ByteArray
|
||||
) {
|
||||
|
@ -143,7 +143,7 @@ internal class ConfigPushSvc {
|
||||
serverListPush.mobileSSOServerList
|
||||
}
|
||||
|
||||
bot.logger.info { "Server list: ${pushServerList.joinToString()}." }
|
||||
bot.network.logger.info { "Server list: ${pushServerList.joinToString()}." }
|
||||
|
||||
if (pushServerList.isNotEmpty()) {
|
||||
bot.serverList.clear()
|
||||
|
@ -95,6 +95,29 @@ internal class StatSvc {
|
||||
}
|
||||
}
|
||||
|
||||
internal object SimpleGet : OutgoingPacketFactory<SimpleGet.Response>("StatSvc.SimpleGet") {
|
||||
internal object Response : Packet {
|
||||
override fun toString(): String = "Response(SimpleGet.Response)"
|
||||
}
|
||||
|
||||
operator fun invoke(
|
||||
client: QQAndroidClient
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(
|
||||
client,
|
||||
bodyType = 1,
|
||||
extraData = client.wLoginSigInfo.d2.data,
|
||||
key = client.wLoginSigInfo.d2Key
|
||||
) {
|
||||
writeSsoPacket(client, client.subAppId, commandName, sequenceId = it) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
|
||||
return Response
|
||||
}
|
||||
}
|
||||
|
||||
internal object Register : OutgoingPacketFactory<Register.Response>("StatSvc.register") {
|
||||
|
||||
internal class Response(
|
||||
@ -106,7 +129,7 @@ internal class StatSvc {
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
|
||||
val packet = readUniPacket(SvcRespRegister.serializer())
|
||||
packet.iHelloInterval.let {
|
||||
bot.configuration.heartbeatPeriodMillis = it.times(1000).toLong()
|
||||
bot.configuration.statHeartbeatPeriodMillis = it.times(1000).toLong()
|
||||
}
|
||||
|
||||
return Response(packet)
|
||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
|
||||
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.*
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||
import net.mamoe.mirai.internal.utils.GuidSource
|
||||
import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag
|
||||
import net.mamoe.mirai.internal.utils.guidFlag
|
||||
import net.mamoe.mirai.utils.generateDeviceInfoData
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import net.mamoe.mirai.utils.toReadPacket
|
||||
|
||||
internal object WtLogin10 : WtLoginExt {
|
||||
|
||||
const val appId: Long = 16L
|
||||
operator fun invoke(
|
||||
client: QQAndroidClient,
|
||||
) = WtLogin.ExchangeEmp.buildLoginOutgoingPacket(client, bodyType = 2, key = ByteArray(16)) { sequenceId ->
|
||||
writeSsoPacket(
|
||||
client,
|
||||
client.subAppId,
|
||||
WtLogin.ExchangeEmp.commandName,
|
||||
extraData = client.wLoginSigInfo.tgt.toReadPacket(),
|
||||
sequenceId = sequenceId
|
||||
) {
|
||||
writeOicqRequestPacket(
|
||||
client,
|
||||
EncryptMethodECDH(client.ecdh),
|
||||
0x0810
|
||||
) {
|
||||
writeShort(11) // subCommand
|
||||
writeShort(17)
|
||||
t100(appId, 100, client.appClientVersion, client.ssoVersion, client.mainSigMap)
|
||||
t10a(client.wLoginSigInfo.tgt)
|
||||
t116(client.miscBitMap, client.subSigMap)
|
||||
t108(client.ksid)
|
||||
t144(
|
||||
androidId = client.device.androidId,
|
||||
androidDevInfo = client.device.generateDeviceInfoData(),
|
||||
osType = client.device.osType,
|
||||
osVersion = client.device.version.release,
|
||||
networkType = client.networkType,
|
||||
simInfo = client.device.simInfo,
|
||||
unknown = byteArrayOf(),
|
||||
apn = client.device.apn,
|
||||
isGuidFromFileNull = false,
|
||||
isGuidAvailable = true,
|
||||
isGuidChanged = false,
|
||||
guidFlag = guidFlag(GuidSource.FROM_STORAGE, MacOrAndroidIdChangeFlag(0)),
|
||||
buildModel = client.device.model,
|
||||
guid = client.device.guid,
|
||||
buildBrand = client.device.brand,
|
||||
tgtgtKey = client.wLoginSigInfo.d2Key.md5()
|
||||
)
|
||||
//t112(client.account.phoneNumber.encodeToByteArray())
|
||||
t143(client.wLoginSigInfo.d2.data)
|
||||
t142(client.apkId)
|
||||
t154(sequenceId)
|
||||
t18(appId, uin = client.uin)
|
||||
t141(client.device.simInfo, client.networkType, client.device.apn)
|
||||
t8(2052)
|
||||
//t511()
|
||||
t147(appId, client.apkVersionName, client.apkSignatureMd5)
|
||||
t177(client.buildTime, client.sdkVersion)
|
||||
t187(client.device.macAddress)
|
||||
t188(client.device.androidId)
|
||||
t194(client.device.imsiMd5)
|
||||
t511(
|
||||
listOf(
|
||||
"tenpay.com", "openmobile.qq.com", "docs.qq.com", "connect.qq.com",
|
||||
"qzone.qq.com", "vip.qq.com", "qun.qq.com", "game.qq.com", "qqweb.qq.com",
|
||||
"office.qq.com", "ti.qq.com", "mail.qq.com", "qzone.com", "mma.qq.com"
|
||||
)
|
||||
)
|
||||
//t544()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.internal.network.LoginExtraData
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.WLoginSigInfo
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.Tlv
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||
@ -40,14 +41,14 @@ internal inline fun WtLoginExt.analysisTlv0x531(
|
||||
|
||||
internal interface WtLoginExt { // so as not to register to global extension
|
||||
|
||||
fun onErrorMessage(tlvMap: TlvMap): WtLogin.Login.LoginPacketResponse.Error? {
|
||||
fun onErrorMessage(type: Int, tlvMap: TlvMap): WtLogin.Login.LoginPacketResponse.Error? {
|
||||
return tlvMap[0x149]?.read {
|
||||
discardExact(2) //type
|
||||
val title: String = readUShortLVString()
|
||||
val content: String = readUShortLVString()
|
||||
val otherInfo: String = readUShortLVString()
|
||||
|
||||
WtLogin.Login.LoginPacketResponse.Error(title, content, otherInfo)
|
||||
WtLogin.Login.LoginPacketResponse.Error(type, title, content, otherInfo)
|
||||
} ?: tlvMap[0x146]?.read {
|
||||
discardExact(2) // ver
|
||||
discardExact(2) // code
|
||||
@ -56,7 +57,7 @@ internal interface WtLoginExt { // so as not to register to global extension
|
||||
val message = readUShortLVString()
|
||||
val errorInfo = readUShortLVString()
|
||||
|
||||
WtLogin.Login.LoginPacketResponse.Error(title, message, errorInfo)
|
||||
WtLogin.Login.LoginPacketResponse.Error(type, title, message, errorInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,6 +117,23 @@ internal interface WtLoginExt { // so as not to register to global extension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt sig and key for pic downloading
|
||||
*/
|
||||
fun QQAndroidClient.analysisTlv11d(t11d: ByteArray): WLoginSigInfo.EncryptedDownloadSession = t11d.read {
|
||||
val appid = readInt().toLong().and(4294967295L)
|
||||
val stKey = ByteArray(16)
|
||||
readAvailable(stKey)
|
||||
val stSigLength = readUShort().toInt()
|
||||
val stSig = ByteArray(stSigLength)
|
||||
readAvailable(stSig)
|
||||
WLoginSigInfo.EncryptedDownloadSession(
|
||||
appid,
|
||||
stKey,
|
||||
stSig
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* pwd flag
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user