Rearrange QQAndroidBot

This commit is contained in:
Him188 2021-04-16 21:17:50 +08:00
parent 1d053bd1bd
commit 997ad1eb63
2 changed files with 113 additions and 117 deletions

View File

@ -20,13 +20,15 @@ package net.mamoe.mirai.internal
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.ContactList
import net.mamoe.mirai.contact.OtherClient
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.EventPriority.MONITOR
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.internal.network.DefaultServerList
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.handler.NetworkHandler
import net.mamoe.mirai.internal.network.handler.ServerList
import net.mamoe.mirai.supervisorJob
@ -71,11 +73,17 @@ internal abstract class AbstractBot constructor(
// overrides
///////////////////////////////////////////////////////////////////////////
final override val isOnline: Boolean get() = _network.state == NetworkHandler.State.OK
final override val isOnline: Boolean get() = network.state == NetworkHandler.State.OK
final override val eventChannel: EventChannel<BotEvent> =
GlobalEventChannel.filterIsInstance<BotEvent>().filter { it.bot === this@AbstractBot }
override val otherClients: ContactList<OtherClient> = ContactList()
final override val otherClients: ContactList<OtherClient> = ContactList()
final override val friends: ContactList<Friend> = ContactList()
final override val groups: ContactList<Group> = ContactList()
final override val strangers: ContactList<Stranger> = ContactList()
final override val asFriend: Friend by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")) }
final override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) }
///////////////////////////////////////////////////////////////////////////
// sync (// TODO: 2021/4/14 extract sync logic
@ -96,7 +104,6 @@ internal abstract class AbstractBot constructor(
val bot = bot.asQQAndroidBot()
if (
!event.bot.isActive // bot closed
|| !::_network.isInitialized // bot 还未登录就被 close
// || _isConnecting // bot 还在登入 // TODO: 2021/4/14 处理还在登入?
) {
// Close network to avoid endless reconnection while network is ok
@ -137,7 +144,7 @@ internal abstract class AbstractBot constructor(
}
if (event.reconnect) {
if (_network.state != NetworkHandler.State.OK) {
if (network.state != NetworkHandler.State.OK) {
// normally closed
return@subscribeAlways
}
@ -145,15 +152,6 @@ internal abstract class AbstractBot constructor(
val causeMessage = event.castOrNull<BotOfflineEvent.CauseAware>()?.cause?.toString() ?: event.toString()
bot.logger.info { "Connection lost, retrying login ($causeMessage)" }
bot.asQQAndroidBot().client.run {
if (serverList.isEmpty()) {
bot.bdhSyncer.loadServerListFromCache()
if (serverList.isEmpty()) {
serverList.addAll(DefaultServerList)
} else Unit
} else serverList.removeAt(0)
}
bot.launch {
val success: Boolean
val time = measureTime {
@ -173,13 +171,13 @@ internal abstract class AbstractBot constructor(
internal val serverList: MutableList<Pair<String, Int>> = mutableListOf() // TODO: 2021/4/16 remove old
internal val serverListNew = ServerList() // TODO: 2021/4/16 load server list from cache (add a provider)
// bot.bdhSyncer.loadServerListFromCache()
// TODO: 2021/4/14 handle serverList
val network: NetworkHandler get() = _network
@Suppress("PropertyName")
internal lateinit var _network: NetworkHandler
val network: NetworkHandler by lazy {
createNetworkHandler(coroutineContext)
}
/**
@ -188,10 +186,11 @@ internal abstract class AbstractBot constructor(
*/
final override suspend fun login() {
if (!isActive) error("Bot is already closed and cannot relogin. Please create a new Bot instance then do login.")
network
network.resumeConnection()
}
protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler
protected abstract suspend fun sendLogout()
// endregion
@ -211,11 +210,10 @@ internal abstract class AbstractBot constructor(
}
groups.delegate.clear() // job is cancelled, so child jobs are to be cancelled
friends.delegate.clear()
strangers.delegate.clear()
}
}
protected abstract suspend fun sendLogout()
override fun close(cause: Throwable?) {
if (!this.isActive) {
// already cancelled

View File

@ -12,13 +12,11 @@ package net.mamoe.mirai.internal
import kotlinx.coroutines.sync.Mutex
import net.mamoe.mirai.Bot
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.OtherClientInfo
import net.mamoe.mirai.internal.contact.OtherClientImpl
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.handler.*
@ -56,58 +54,72 @@ internal class QQAndroidBot constructor(
internal val account: BotAccount,
configuration: BotConfiguration
) : AbstractBot(configuration, account.id), SsoContext {
val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this)
///////////////////////////////////////////////////////////////////////////
// Account secrets cache
///////////////////////////////////////////////////////////////////////////
// We cannot extract these logics until we rewrite the network framework.
private val cacheDir: File by lazy {
configuration.workingDir.resolve(bot.configuration.cacheDir).apply { mkdirs() }
}
internal val accountSecretsFile: File by lazy {
cacheDir.resolve("account.secrets")
}
private fun saveSecrets(secrets: AccountSecretsImpl) {
if (secrets.wLoginSigInfoField == null) return
accountSecretsFile.writeBytes(
TEA.encrypt(
secrets.toByteArray(AccountSecretsImpl.serializer()),
account.passwordMd5
)
)
network.context.logger.info { "Saved account secrets to local cache for fast login." }
}
init {
if (configuration.loginCacheEnabled) {
eventChannel.parentScope(this).subscribeAlways<WtLogin.Login.LoginPacketResponse> { event ->
if (event is WtLogin.Login.LoginPacketResponse.Success) {
if (client.wLoginSigInfoInitialized) {
saveSecrets(AccountSecretsImpl(client))
}
}
}
}
}
/////////////////////////// accounts secrets end
override lateinit var client: QQAndroidClient
override val bot: QQAndroidBot get() = this
val bdhSyncer: BdhSessionSyncer = BdhSessionSyncer(this)
internal var firstLoginSucceed: Boolean = false
inline val json get() = configuration.json
///////////////////////////////////////////////////////////////////////////
// network
///////////////////////////////////////////////////////////////////////////
override val friends: ContactList<Friend> = ContactList()
// TODO: 2021/4/14 bdhSyncer.loadFromCache() when login
override suspend fun sendLogout() {
network.sendWithoutExpect(StatSvc.Register.offline(client))
}
override fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler {
val context = NetworkHandlerContextImpl(this, this)
return SelectorNetworkHandler(
context,
FactoryKeepAliveNetworkHandlerSelector(NettyNetworkHandlerFactory, serverListNew, context)
) // We can move the factory to configuration but this is not necessary for now.
}
suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect(
timeoutMillis: Long = 5000,
retry: Int = 2
): E = network.run { sendAndExpect(timeoutMillis, retry) }
suspend inline fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E =
network.run { sendAndExpect(timeoutMillis, retry) }
/**
* 获取 获取群公告 所需的 bkn 参数
* */
val bkn: Int
get() = client.wLoginSigInfo.sKey.data
.fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() }
.and(Int.MAX_VALUE)
///////////////////////////////////////////////////////////////////////////
// contacts
///////////////////////////////////////////////////////////////////////////
override lateinit var nick: String
@JvmField
val groupListModifyLock = Mutex()
// internally visible only
fun getGroupByUin(uin: Long): Group {
return getGroupByUinOrNull(uin)
?: throw NoSuchElementException("Group ${Mirai.calculateGroupCodeByGroupUin(uin)} not found")
}
fun getGroupByUinOrNull(uin: Long): Group? {
return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin }
}
///////////////////////////////////////////////////////////////////////////
// contact cache
///////////////////////////////////////////////////////////////////////////
inline val json get() = configuration.json
val friendListCache: FriendListCache? by lazy {
if (!configuration.contactListCache.friendListCacheEnabled) return@lazy null
@ -146,58 +158,44 @@ internal class QQAndroidBot constructor(
}
}
override lateinit var nick: String
///////////////////////////////////////////////////////////////////////////
// Account secrets cache
///////////////////////////////////////////////////////////////////////////
override val asFriend: Friend by lazy {
@OptIn(LowLevelApi::class)
Mirai.newFriend(this, FriendInfoImpl(uin, nick, ""))
// We cannot extract these logics until we rewrite the network framework.
private val cacheDir: File by lazy {
configuration.workingDir.resolve(bot.configuration.cacheDir).apply { mkdirs() }
}
private val accountSecretsFile: File by lazy {
cacheDir.resolve("account.secrets")
}
override val groups: ContactList<Group> = ContactList()
private fun saveSecrets(secrets: AccountSecretsImpl) {
if (secrets.wLoginSigInfoField == null) return
// TODO: 2021/4/14 bdhSyncer.loadFromCache() when login
accountSecretsFile.writeBytes(
TEA.encrypt(
secrets.toByteArray(AccountSecretsImpl.serializer()),
account.passwordMd5
)
)
override suspend fun sendLogout() {
network.sendWithoutExpect(StatSvc.Register.offline(client))
network.context.logger.info { "Saved account secrets to local cache for fast login." }
}
override fun createNetworkHandler(coroutineContext: CoroutineContext): NetworkHandler {
val context = NetworkHandlerContextImpl(this, this)
return SelectorNetworkHandler(
context,
FactoryKeepAliveNetworkHandlerSelector(NettyNetworkHandlerFactory, serverListNew, context)
) // We can move the factory to configuration but this is not necessary for now.
init {
if (configuration.loginCacheEnabled) {
eventChannel.parentScope(this).subscribeAlways<WtLogin.Login.LoginPacketResponse> { event ->
if (event is WtLogin.Login.LoginPacketResponse.Success) {
if (client.wLoginSigInfoInitialized) {
saveSecrets(AccountSecretsImpl(client))
}
}
}
}
}
@JvmField
val groupListModifyLock = Mutex()
/////////////////////////// accounts secrets end
// internally visible only
fun getGroupByUin(uin: Long): Group {
return getGroupByUinOrNull(uin)
?: throw NoSuchElementException("Group ${Mirai.calculateGroupCodeByGroupUin(uin)} not found")
}
fun getGroupByUinOrNull(uin: Long): Group? {
return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin }
}
suspend inline fun <E : Packet> OutgoingPacketWithRespType<E>.sendAndExpect(
timeoutMillis: Long = 5000,
retry: Int = 2
): E = network.run { sendAndExpect(timeoutMillis, retry) }
suspend inline fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 5000, retry: Int = 2): E =
network.run { sendAndExpect(timeoutMillis, retry) }
/**
* 获取 获取群公告 所需的 bkn 参数
* */
val bkn: Int
get() = client.wLoginSigInfo.sKey.data
.fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() }
.and(Int.MAX_VALUE)
override val asStranger: Stranger by lazy { Mirai.newStranger(bot, StrangerInfoImpl(bot.id, bot.nick)) }
override val strangers: ContactList<Stranger> = ContactList()
}