mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-11 09:58:03 +08:00
Support server changing, close #52
This commit is contained in:
parent
e6ef03f60d
commit
da801d7b6d
@ -46,7 +46,6 @@ import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.LongMsg
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.*
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.qqandroid.utils.*
|
||||
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
|
||||
import net.mamoe.mirai.qqandroid.utils.encodeToString
|
||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||
@ -204,6 +203,12 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
})
|
||||
}
|
||||
|
||||
override suspend fun relogin(cause: Throwable?) {
|
||||
client.useNextServers { host, port ->
|
||||
network.relogin(host, port, cause)
|
||||
}
|
||||
}
|
||||
|
||||
@LowLevelAPI
|
||||
override fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ {
|
||||
return QQImpl(
|
||||
|
@ -37,6 +37,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.*
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.Heartbeat
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.WtLogin
|
||||
@ -107,7 +108,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}
|
||||
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
override suspend fun relogin(cause: Throwable?) {
|
||||
override suspend fun relogin(host: String, port: Int, cause: Throwable?) {
|
||||
heartbeatJob?.cancel(CancellationException("relogin", cause))
|
||||
heartbeatJob?.join()
|
||||
if (::channel.isInitialized) {
|
||||
@ -121,10 +122,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
}
|
||||
channel = PlatformSocket()
|
||||
// TODO: 2020/2/14 连接多个服务器, #52
|
||||
|
||||
withTimeoutOrNull(3000) {
|
||||
channel.connect("114.221.144.22", 8080)
|
||||
channel.connect(host, port)
|
||||
} ?: error("timeout connecting server")
|
||||
logger.info("Connected to server 114.221.144.22:8080")
|
||||
logger.info("Connected to server $host:$port")
|
||||
startPacketReceiverJobOrKill(CancellationException("relogin", cause))
|
||||
|
||||
var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect()
|
||||
@ -301,6 +303,30 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
launch { reloadGroupList() }
|
||||
}
|
||||
|
||||
this@QQAndroidBotNetworkHandler.launch {
|
||||
logger.info { "Awaiting ConfigPushSvc.PushReq" }
|
||||
val resp =
|
||||
subscribingGetOrNull<ConfigPushSvc.PushReq.PushReqResponse, ConfigPushSvc.PushReq.PushReqResponse>(
|
||||
10_000) { it }
|
||||
|
||||
when (resp) {
|
||||
null -> logger.info { "Missing ConfigPushSvc.PushReq." }
|
||||
is ConfigPushSvc.PushReq.PushReqResponse.Success -> {
|
||||
logger.info { "ConfigPushSvc.PushReq: success" }
|
||||
}
|
||||
is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
|
||||
logger.info { "Server requires reconnect." }
|
||||
logger.info { "ChangeServer.unknown = ${resp.unknown}" }
|
||||
logger.info { "Server list: ${resp.serverList.joinToString()}" }
|
||||
|
||||
resp.serverList.forEach {
|
||||
bot.client.serverList.add(it.host to it.port)
|
||||
}
|
||||
BotOfflineEvent.RequireReconnect(bot).broadcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
withTimeoutOrNull(30000) {
|
||||
launch { subscribingGet<MessageSvc.PbGetMsg.GetMsgSuccess, Unit> { Unit } }
|
||||
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<Packet>()
|
||||
|
@ -15,8 +15,10 @@ import kotlinx.atomicfu.AtomicInt
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.data.OnlineStatus
|
||||
import net.mamoe.mirai.network.NoServerAvailableException
|
||||
import net.mamoe.mirai.qqandroid.BotAccount
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FileStoragePushFSSvcList
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.Tlv
|
||||
@ -42,6 +44,17 @@ private fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray
|
||||
*/
|
||||
internal fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() }
|
||||
|
||||
internal object DefaultServerList : Set<Pair<String, Int>> by setOf(
|
||||
"42.81.169.46" to 8080,
|
||||
"42.81.172.81" to 80,
|
||||
"114.221.148.59" to 14000,
|
||||
"42.81.172.147" to 443,
|
||||
"125.94.60.146" to 80,
|
||||
"114.221.144.215" to 80,
|
||||
"msfwifi.3g.qq.com" to 8080,
|
||||
"42.81.172.22" to 80
|
||||
)
|
||||
|
||||
/*
|
||||
APP ID:
|
||||
GetStViaSMSVerifyLogin = 16
|
||||
@ -64,6 +77,8 @@ internal open class QQAndroidClient(
|
||||
val device: DeviceInfo = SystemDeviceInfo(context),
|
||||
bot: QQAndroidBot
|
||||
) {
|
||||
internal val serverList: MutableList<Pair<String, Int>> = DefaultServerList.toMutableList()
|
||||
|
||||
val keys: Map<String, ByteArray> by lazy {
|
||||
mapOf(
|
||||
"16 zero" to ByteArray(16),
|
||||
@ -102,11 +117,30 @@ internal open class QQAndroidClient(
|
||||
val randomKey: ByteArray = getRandomByteArray(16)
|
||||
|
||||
var miscBitMap: Int = 184024956 // 也可能是 150470524 ?
|
||||
var mainSigMap: Int = 16724722
|
||||
private var mainSigMap: Int = 16724722
|
||||
var subSigMap: Int = 0x10400 //=66,560
|
||||
|
||||
private val _ssoSequenceId: AtomicInt = atomic(85600)
|
||||
|
||||
lateinit var fileStoragePushFSSvcList: FileStoragePushFSSvcList
|
||||
internal suspend inline fun useNextServers(crossinline block: suspend (host: String, port: Int) -> Unit) {
|
||||
@Suppress("UNREACHABLE_CODE", "ThrowableNotThrown") // false positive
|
||||
retryCatching(bot.client.serverList.size) {
|
||||
val pair = bot.client.serverList.random()
|
||||
kotlin.runCatching {
|
||||
block(pair.first, pair.second)
|
||||
return
|
||||
}.getOrElse {
|
||||
bot.client.serverList.remove(pair)
|
||||
bot.logger.warning(it)
|
||||
throw it
|
||||
}
|
||||
}.getOrElse {
|
||||
bot.client.serverList.addAll(DefaultServerList)
|
||||
throw NoServerAvailableException(it)
|
||||
}
|
||||
}
|
||||
|
||||
@MiraiInternalAPI("Do not use directly. Get from the lambda param of buildSsoPacket")
|
||||
internal fun nextSsoSequenceId() = _ssoSequenceId.addAndGet(2)
|
||||
|
||||
|
@ -12,7 +12,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.jce
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.network.Packet
|
||||
import net.mamoe.mirai.qqandroid.utils.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
|
||||
|
||||
@Serializable
|
||||
@ -85,7 +84,7 @@ internal class _340(
|
||||
@JceId(14) val netType: Byte? = 0,
|
||||
@JceId(15) val heThreshold: Int? = 0,
|
||||
@JceId(16) val policyId: String? = ""
|
||||
) : ProtoBuf
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class _339(
|
||||
@ -102,8 +101,8 @@ internal class _339(
|
||||
|
||||
@Serializable
|
||||
internal class FileStoragePushFSSvcList(
|
||||
@JceId(0) val vUpLoadList: List<FileStorageServerListInfo> = listOf(),
|
||||
@JceId(1) val vPicDownLoadList: List<FileStorageServerListInfo> = listOf(),
|
||||
@JceId(0) val vUpLoadList: List<FileStorageServerListInfo>? = listOf(),
|
||||
@JceId(1) val vPicDownLoadList: List<FileStorageServerListInfo>? = listOf(),
|
||||
@JceId(2) val vGPicDownLoadList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(3) val vQzoneProxyServiceList: List<FileStorageServerListInfo>? = null,
|
||||
@JceId(4) val vUrlEncodeServiceList: List<FileStorageServerListInfo>? = null,
|
||||
|
@ -43,6 +43,8 @@ internal sealed class PacketFactory<TPacket : Packet?> {
|
||||
* 筛选从服务器接收到的包时的 commandName
|
||||
*/
|
||||
abstract val receivingCommandName: String
|
||||
|
||||
open val canBeCached: Boolean get() = true
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,7 +218,7 @@ internal object KnownPacketFactories {
|
||||
}?.let {
|
||||
it as IncomingPacket<T>
|
||||
|
||||
if (it.packetFactory is IncomingPacketFactory<T> && bot.network.pendingEnabled) {
|
||||
if (it.packetFactory is IncomingPacketFactory<T> && it.packetFactory.canBeCached && bot.network.pendingEnabled) {
|
||||
bot.network.pendingIncomingPackets?.addLast(it.also {
|
||||
it.consumer = consumer
|
||||
it.flag2 = flag2
|
||||
|
File diff suppressed because one or more lines are too long
@ -83,10 +83,14 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
|
||||
@Suppress("PropertyName")
|
||||
internal lateinit var _network: N
|
||||
|
||||
protected abstract suspend fun relogin(cause: Throwable?)
|
||||
|
||||
@Suppress("unused")
|
||||
private val offlineListener: Listener<BotOfflineEvent> = this.subscribeAlways { event ->
|
||||
when (event) {
|
||||
is BotOfflineEvent.Dropped -> {
|
||||
is BotOfflineEvent.Dropped,
|
||||
is BotOfflineEvent.RequireReconnect
|
||||
-> {
|
||||
if (!_network.isActive) {
|
||||
return@subscribeAlways
|
||||
}
|
||||
@ -96,9 +100,9 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
|
||||
if (tryCount != 0) {
|
||||
delay(configuration.reconnectPeriodMillis)
|
||||
}
|
||||
network.relogin(event.cause)
|
||||
relogin((event as? BotOfflineEvent.Dropped)?.cause)
|
||||
logger.info("Reconnected successfully")
|
||||
BotReloginEvent(bot, event.cause).broadcast()
|
||||
BotReloginEvent(bot, (event as? BotOfflineEvent.Dropped)?.cause).broadcast()
|
||||
return@subscribeAlways
|
||||
}?.let {
|
||||
logger.info("Cannot reconnect")
|
||||
@ -134,7 +138,7 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
|
||||
while (true) {
|
||||
_network = createNetworkHandler(this.coroutineContext)
|
||||
try {
|
||||
_network.relogin()
|
||||
relogin(null)
|
||||
return
|
||||
} catch (e: LoginFailedException) {
|
||||
throw e
|
||||
|
@ -71,6 +71,12 @@ sealed class BotOfflineEvent : BotEvent {
|
||||
* 被服务器断开或因网络问题而掉线
|
||||
*/
|
||||
data class Dropped(override val bot: Bot, val cause: Throwable?) : BotOfflineEvent(), Packet, BotPassiveEvent
|
||||
|
||||
/**
|
||||
* 服务器主动要求更换另一个服务器
|
||||
*/
|
||||
@SinceMirai("0.38.0")
|
||||
data class RequireReconnect(override val bot: Bot) : BotOfflineEvent(), Packet, BotPassiveEvent
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,7 +93,7 @@ inline fun <reified E : Event, R : Any> CoroutineScope.subscribingGetAsync(
|
||||
internal suspend inline fun <reified E : Event, R> subscribingGetOrNullImpl(
|
||||
coroutineScope: CoroutineScope,
|
||||
noinline mapper: suspend E.(E) -> R?
|
||||
): R {
|
||||
): R? {
|
||||
var result: Result<R?> = Result.success(null) // stub
|
||||
var listener: Listener<E>? = null
|
||||
@Suppress("DuplicatedCode") // for better performance
|
||||
@ -114,5 +114,5 @@ internal suspend inline fun <reified E : Event, R> subscribingGetOrNullImpl(
|
||||
}
|
||||
listener.join()
|
||||
|
||||
return result.getOrThrow()!!
|
||||
return result.getOrThrow()
|
||||
}
|
@ -67,7 +67,7 @@ abstract class BotNetworkHandler : CoroutineScope {
|
||||
*/
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@MiraiInternalAPI
|
||||
abstract suspend fun relogin(cause: Throwable? = null)
|
||||
abstract suspend fun relogin(host: String, port: Int, cause: Throwable? = null)
|
||||
|
||||
/**
|
||||
* 初始化获取好友列表等值.
|
||||
|
@ -29,6 +29,11 @@ sealed class LoginFailedException : RuntimeException {
|
||||
*/
|
||||
class WrongPasswordException(message: String?) : LoginFailedException(message)
|
||||
|
||||
/**
|
||||
* 无可用服务器
|
||||
*/
|
||||
class NoServerAvailableException(override val cause: Throwable?) : LoginFailedException("no server available")
|
||||
|
||||
/**
|
||||
* 需要短信验证时抛出. mirai 目前还不支持短信验证.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user