QQAndroidBot.kt 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * Copyright 2019-2021 Mamoe Technologies and contributors.
  3. *
  4. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
  5. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
  6. *
  7. * https://github.com/mamoe/mirai/blob/master/LICENSE
  8. */
  9. @file:Suppress("EXPERIMENTAL_API_USAGE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
  10. package net.mamoe.mirai.internal
  11. import kotlinx.atomicfu.atomic
  12. import kotlinx.coroutines.isActive
  13. import kotlinx.coroutines.runBlocking
  14. import net.mamoe.mirai.Bot
  15. import net.mamoe.mirai.Mirai
  16. import net.mamoe.mirai.contact.Group
  17. import net.mamoe.mirai.event.events.BotOfflineEvent
  18. import net.mamoe.mirai.event.events.BotOnlineEvent
  19. import net.mamoe.mirai.event.events.BotReloginEvent
  20. import net.mamoe.mirai.internal.contact.checkIsGroupImpl
  21. import net.mamoe.mirai.internal.network.component.ComponentStorage
  22. import net.mamoe.mirai.internal.network.component.ComponentStorageDelegate
  23. import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage
  24. import net.mamoe.mirai.internal.network.component.withFallback
  25. import net.mamoe.mirai.internal.network.components.*
  26. import net.mamoe.mirai.internal.network.context.SsoProcessorContext
  27. import net.mamoe.mirai.internal.network.context.SsoProcessorContextImpl
  28. import net.mamoe.mirai.internal.network.handler.NetworkHandler
  29. import net.mamoe.mirai.internal.network.handler.NetworkHandler.State
  30. import net.mamoe.mirai.internal.network.handler.NetworkHandlerContextImpl
  31. import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
  32. import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport.BaseStateImpl
  33. import net.mamoe.mirai.internal.network.handler.selector.KeepAliveNetworkHandlerSelector
  34. import net.mamoe.mirai.internal.network.handler.selector.NetworkException
  35. import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler
  36. import net.mamoe.mirai.internal.network.handler.state.CombinedStateObserver.Companion.plus
  37. import net.mamoe.mirai.internal.network.handler.state.LoggingStateObserver
  38. import net.mamoe.mirai.internal.network.handler.state.StateChangedObserver
  39. import net.mamoe.mirai.internal.network.handler.state.StateObserver
  40. import net.mamoe.mirai.internal.network.handler.state.safe
  41. import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException
  42. import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory
  43. import net.mamoe.mirai.internal.utils.subLogger
  44. import net.mamoe.mirai.utils.BotConfiguration
  45. import net.mamoe.mirai.utils.MiraiLogger
  46. import net.mamoe.mirai.utils.lateinitMutableProperty
  47. import kotlin.contracts.contract
  48. internal fun Bot.asQQAndroidBot(): QQAndroidBot {
  49. contract {
  50. returns() implies (this@asQQAndroidBot is QQAndroidBot)
  51. }
  52. return this as QQAndroidBot
  53. }
  54. @Suppress("INVISIBLE_MEMBER", "BooleanLiteralArgument", "OverridingDeprecatedMember")
  55. internal open class QQAndroidBot constructor(
  56. internal val account: BotAccount,
  57. configuration: BotConfiguration,
  58. ) : AbstractBot(configuration, account.id) {
  59. override val bot: QQAndroidBot get() = this
  60. val client get() = components[SsoProcessor].client
  61. override fun close(cause: Throwable?) {
  62. if (!this.isActive) return
  63. runBlocking {
  64. try { // this may not be very good but
  65. components[SsoProcessor].logout(network)
  66. } catch (ignored: Exception) {
  67. }
  68. }
  69. super.close(cause)
  70. }
  71. ///////////////////////////////////////////////////////////////////////////
  72. // network
  73. ///////////////////////////////////////////////////////////////////////////
  74. // also called by tests.
  75. fun ComponentStorage.stateObserverChain(): StateObserver {
  76. val components = this
  77. val eventDispatcher = this[EventDispatcher]
  78. return StateObserver.chainOfNotNull(
  79. components[BotInitProcessor].asObserver(),
  80. object : StateChangedObserver(State.OK) {
  81. private val shouldBroadcastRelogin = atomic(false)
  82. override fun stateChanged0(
  83. networkHandler: NetworkHandlerSupport,
  84. previous: BaseStateImpl,
  85. new: BaseStateImpl,
  86. ) {
  87. eventDispatcher.broadcastAsync(BotOnlineEvent(bot)).thenBroadcast(eventDispatcher) {
  88. if (!shouldBroadcastRelogin.compareAndSet(false, true)) {
  89. BotReloginEvent(bot, new.getCause())
  90. } else null
  91. }
  92. }
  93. override fun toString(): String = "StateChangedObserver(BotOnlineEventBroadcaster)"
  94. },
  95. StateChangedObserver("BotOfflineEventBroadcaster", State.OK, State.CLOSED) { new ->
  96. // logging performed by BotOfflineEventMonitor
  97. val cause = new.getCause()
  98. when {
  99. cause is ForceOfflineException -> {
  100. eventDispatcher.broadcastAsync(BotOfflineEvent.Force(bot, cause.title, cause.message))
  101. }
  102. cause is NetworkException && cause.recoverable -> {
  103. eventDispatcher.broadcastAsync(BotOfflineEvent.Dropped(bot, cause))
  104. }
  105. cause is BotClosedByEvent -> {
  106. }
  107. else -> {
  108. // any other unexpected exceptions considered as an error
  109. eventDispatcher.broadcastAsync(BotOfflineEvent.Active(bot, cause))
  110. }
  111. }
  112. },
  113. ).safe(logger.subLogger("StateObserver")) + LoggingStateObserver.createLoggingIfEnabled()
  114. }
  115. private val networkLogger: MiraiLogger by lazy { configuration.networkLoggerSupplier(this) }
  116. final override val components: ComponentStorage get() = network.context
  117. private val defaultBotLevelComponents: ComponentStorage by lateinitMutableProperty {
  118. createBotLevelComponents().apply {
  119. set(StateObserver, stateObserverChain())
  120. }.also { components ->
  121. components[BotOfflineEventMonitor].attachJob(bot, this)
  122. }
  123. }
  124. open fun createBotLevelComponents(): ConcurrentComponentStorage = ConcurrentComponentStorage {
  125. val components = ComponentStorageDelegate { this@QQAndroidBot.components }
  126. // There's no need to interrupt a broadcasting event when network handler closed.
  127. set(EventDispatcher, EventDispatcherImpl(bot.coroutineContext, logger.subLogger("EventDispatcher")))
  128. set(SsoProcessorContext, SsoProcessorContextImpl(bot))
  129. set(SsoProcessor, SsoProcessorImpl(get(SsoProcessorContext)))
  130. set(HeartbeatProcessor, HeartbeatProcessorImpl())
  131. set(HeartbeatScheduler, TimeBasedHeartbeatSchedulerImpl(networkLogger.subLogger("HeartbeatScheduler")))
  132. set(KeyRefreshProcessor, KeyRefreshProcessorImpl(networkLogger.subLogger("KeyRefreshProcessor")))
  133. set(ConfigPushProcessor, ConfigPushProcessorImpl(networkLogger.subLogger("ConfigPushProcessor")))
  134. set(BotOfflineEventMonitor, BotOfflineEventMonitorImpl())
  135. set(BotInitProcessor, BotInitProcessorImpl(bot, components, networkLogger.subLogger("BotInitProcessor")))
  136. set(ContactCacheService, ContactCacheServiceImpl(bot, networkLogger.subLogger("ContactCacheService")))
  137. set(ContactUpdater, ContactUpdaterImpl(bot, components, networkLogger.subLogger("ContactUpdater")))
  138. set(
  139. BdhSessionSyncer,
  140. BdhSessionSyncerImpl(configuration, components, networkLogger.subLogger("BotSessionSyncer"))
  141. )
  142. set(
  143. MessageSvcSyncer,
  144. MessageSvcSyncerImpl(bot, bot.coroutineContext, networkLogger.subLogger("MessageSvcSyncer"))
  145. )
  146. set(
  147. EcdhInitialPublicKeyUpdater,
  148. EcdhInitialPublicKeyUpdaterImpl(bot, networkLogger.subLogger("ECDHInitialPublicKeyUpdater"))
  149. )
  150. set(ServerList, ServerListImpl(networkLogger.subLogger("ServerList")))
  151. set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot))
  152. set(
  153. PacketHandler, PacketHandlerChain(
  154. LoggingPacketHandlerAdapter(get(PacketLoggingStrategy), networkLogger),
  155. EventBroadcasterPacketHandler(components),
  156. CallPacketFactoryPacketHandler(bot)
  157. )
  158. )
  159. set(PacketCodec, PacketCodecImpl())
  160. set(
  161. OtherClientUpdater,
  162. OtherClientUpdaterImpl(bot, components, networkLogger.subLogger("OtherClientUpdater"))
  163. )
  164. set(ConfigPushSyncer, ConfigPushSyncerImpl())
  165. set(
  166. AccountSecretsManager,
  167. configuration.createAccountsSecretsManager(bot.logger.subLogger("AccountSecretsManager"))
  168. )
  169. }
  170. /**
  171. * This would overrides those from [createBotLevelComponents]
  172. */
  173. open fun createNetworkLevelComponents(): ComponentStorage {
  174. return ConcurrentComponentStorage {
  175. set(BotClientHolder, BotClientHolderImpl(bot, networkLogger.subLogger("BotClientHolder")))
  176. }.withFallback(defaultBotLevelComponents)
  177. }
  178. override fun createNetworkHandler(): NetworkHandler {
  179. return SelectorNetworkHandler(
  180. KeepAliveNetworkHandlerSelector(
  181. maxAttempts = configuration.reconnectionRetryTimes.coerceIn(1, Int.MAX_VALUE),
  182. logger = networkLogger.subLogger("Selector")
  183. ) {
  184. val context = NetworkHandlerContextImpl(
  185. bot,
  186. networkLogger,
  187. createNetworkLevelComponents()
  188. )
  189. NettyNetworkHandlerFactory.create(
  190. context,
  191. context[ServerList].pollAny().toSocketAddress()
  192. )
  193. }
  194. ) // We can move the factory to configuration but this is not necessary for now.
  195. }
  196. /**
  197. * 获取 获取群公告 所需的 bkn 参数
  198. * */ // TODO: 2021/4/26 extract it after #1141 merged
  199. val bkn: Int
  200. get() = client.wLoginSigInfo.sKey.data
  201. .fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() }
  202. .and(Int.MAX_VALUE)
  203. ///////////////////////////////////////////////////////////////////////////
  204. // contacts
  205. ///////////////////////////////////////////////////////////////////////////
  206. override lateinit var nick: String
  207. // internally visible only
  208. fun getGroupByUin(uin: Long): Group {
  209. return getGroupByUinOrNull(uin)
  210. ?: throw NoSuchElementException("Group ${Mirai.calculateGroupCodeByGroupUin(uin)} not found")
  211. }
  212. fun getGroupByUinOrNull(uin: Long): Group? {
  213. return groups.firstOrNull { it.checkIsGroupImpl(); it.uin == uin }
  214. }
  215. }