QQAndroidBot.kt 11 KB

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