mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-22 13:46:13 +08:00
Adjust running coroutineContext
This commit is contained in:
parent
ead17724b6
commit
d3270bb36f
@ -14,8 +14,7 @@ import net.mamoe.mirai.withSession
|
||||
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
|
||||
|
||||
/**
|
||||
* 联系人. 虽然叫做联系人, 但它直营
|
||||
* 现支持的联系人只有 [QQ号][QQ] 和 [群][Group].
|
||||
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] 和 [群][Group].
|
||||
*
|
||||
* @param bot 这个联系人所属 [Bot]
|
||||
* @param id 可以是 QQ 号码或者群号码 [GroupId].
|
||||
@ -70,8 +69,7 @@ inline class GroupInternalId(val value: UInt)
|
||||
class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, groupId.value) {
|
||||
val internalId = GroupId(id).toInternalId()
|
||||
val members: ContactList<QQ>
|
||||
//todo members
|
||||
get() = throw UnsupportedOperationException("Not yet supported")
|
||||
get() = TODO("Implementing group members is less important")
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
bot.network[EventPacketHandler].sendGroupMessage(this, message)
|
||||
@ -87,7 +85,7 @@ class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot,
|
||||
inline fun <R> Group.withSession(block: BotSession.() -> R): R = bot.withSession(block)
|
||||
|
||||
/**
|
||||
* QQ 账号.
|
||||
* QQ 对象.
|
||||
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
|
||||
*
|
||||
* A QQ instance helps you to receive event from or sendPacket event to.
|
||||
@ -95,7 +93,7 @@ inline fun <R> Group.withSession(block: BotSession.() -> R): R = bot.withSession
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
class QQ internal constructor(bot: Bot, number: UInt) : Contact(bot, number) {
|
||||
class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) {
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
bot.network[EventPacketHandler].sendFriendMessage(this, message)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.mamoe.mirai.network
|
||||
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler
|
||||
@ -15,7 +16,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket
|
||||
import net.mamoe.mirai.utils.BotNetworkConfiguration
|
||||
import net.mamoe.mirai.utils.io.PlatformDatagramChannel
|
||||
import kotlin.coroutines.ContinuationInterceptor
|
||||
|
||||
/**
|
||||
* Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务.
|
||||
@ -86,7 +86,8 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
|
||||
* 关闭网络接口, 停止所有有关协程和任务
|
||||
*/
|
||||
suspend fun close(cause: Throwable? = null) {
|
||||
//todo check??
|
||||
coroutineContext[ContinuationInterceptor]!!.cancelChildren(CancellationException("handler closed", cause))
|
||||
val job = coroutineContext[Job]
|
||||
checkNotNull(job) { "Job should not be null because there will always be a SupervisorJob. There may be a internal mistake" }
|
||||
job.cancelChildren(CancellationException("handler closed", cause))
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ internal inline fun TIMBotNetworkHandler.BotSession(
|
||||
*/
|
||||
class BotSession(
|
||||
val bot: Bot,
|
||||
val sessionKey: ByteArray,//TODO 协议抽象? 可能并不是所有协议均需要 sessionKey
|
||||
val sessionKey: ByteArray,
|
||||
val socket: DataPacketSocketAdapter,
|
||||
val NetworkScope: CoroutineScope
|
||||
) {
|
||||
@ -61,7 +61,7 @@ class BotSession(
|
||||
|
||||
/**
|
||||
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket][P].
|
||||
* 发送成功后, 该方法会等待收到 [ServerPacket][P] 直到超时.
|
||||
* 这个方法会立即返回.
|
||||
*
|
||||
* 实现方法:
|
||||
* ```kotlin
|
||||
@ -75,13 +75,13 @@ class BotSession(
|
||||
*
|
||||
* @param checkSequence 是否期待 [ServerPacket.sequenceId] 与 [OutgoingPacket.sequenceId] 相同的包.
|
||||
* @param P 期待的包
|
||||
* @param handler 处理期待的包
|
||||
* @param handler 处理期待的包. 将会在调用 [sendAndExpect] 的函数所在 [coroutineContext] 下执行.
|
||||
*
|
||||
* @see Bot.withSession 转换接收器 (receiver, 即 `this` 的指向) 为 [BotSession]
|
||||
*/
|
||||
suspend inline fun <reified P : ServerPacket, R> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, noinline handler: suspend (P) -> R): CompletableDeferred<R> {
|
||||
val deferred: CompletableDeferred<R> = CompletableDeferred(coroutineContext[Job])
|
||||
bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession, checkSequence).also {
|
||||
bot.network.addHandler(TemporaryPacketHandler(P::class, deferred, this@BotSession, checkSequence, coroutineContext + deferred).also {
|
||||
it.toSend(this)
|
||||
it.onExpect(handler)
|
||||
})
|
||||
|
@ -36,7 +36,8 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
BotNetworkHandler<TIMBotNetworkHandler.BotSocketAdapter>, PacketHandlerList() {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) }
|
||||
Dispatchers.Default + CoroutineExceptionHandler { _, e -> bot.logger.log(e) } + SupervisorJob()
|
||||
|
||||
|
||||
override lateinit var socket: BotSocketAdapter
|
||||
private set
|
||||
@ -156,7 +157,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun resendTouch(): LoginResult {
|
||||
internal suspend fun resendTouch(): LoginResult = coroutineScope {
|
||||
if (::loginHandler.isInitialized) loginHandler.close()
|
||||
|
||||
loginHandler = LoginHandler(configuration)
|
||||
@ -169,9 +170,9 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
loginResult.complete(LoginResult.TIMEOUT)
|
||||
}
|
||||
}
|
||||
sendPacket(TouchPacket(bot.qqAccount, this.serverIp))
|
||||
sendPacket(TouchPacket(bot.qqAccount, serverIp))
|
||||
|
||||
return loginResult.await()
|
||||
return@coroutineScope loginResult.await()
|
||||
}
|
||||
|
||||
private suspend inline fun <reified P : ServerPacket> expectPacket(): CompletableDeferred<P> {
|
||||
@ -199,20 +200,19 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
}
|
||||
|
||||
packet.use {
|
||||
val name = packet::class.simpleName
|
||||
if (name != null && !name.endsWith("Encrypted") && !name.endsWith("Raw")) {
|
||||
packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let {
|
||||
bot.logger.logCyan("Packet received: $packet")
|
||||
}
|
||||
|
||||
//Remove first to release the lock
|
||||
// Remove first to release the lock
|
||||
handlersLock.withLock {
|
||||
temporaryPacketHandlers.filter { it.filter(session, packet) }
|
||||
}.forEach {
|
||||
it.doReceive(packet)
|
||||
it.doReceiveWithoutExceptions(packet)
|
||||
}
|
||||
|
||||
if (packet is ServerEventPacket) {
|
||||
//ensure the response packet is sent
|
||||
// Ensure the response packet is sent
|
||||
sendPacket(packet.ResponsePacket(bot.qqAccount, sessionKey))
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
//they should be called in sequence otherwise because packet is lock-free
|
||||
// They should be called in sequence otherwise because packet is lock-free
|
||||
loginHandler.onPacketReceived(packet)
|
||||
this@TIMBotNetworkHandler.forEach {
|
||||
it.instance.onPacketReceived(packet)
|
||||
@ -293,9 +293,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
private lateinit var loginIP: String
|
||||
private var privateKey: ByteArray = getRandomByteArray(16)
|
||||
|
||||
/**
|
||||
* 0828_decr_key
|
||||
*/
|
||||
private lateinit var sessionResponseDecryptionKey: IoBuffer
|
||||
|
||||
private var captchaSectionId: Int = 1
|
||||
@ -406,7 +403,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
||||
bot.qqAccount,
|
||||
token0825,
|
||||
code,
|
||||
packet.verificationToken
|
||||
packet.captchaToken
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.network.protocol.tim.handler
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.mamoe.mirai.network.BotSession
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
@ -23,7 +27,11 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
|
||||
private val expectationClass: KClass<P>,
|
||||
private val deferred: CompletableDeferred<R>,
|
||||
private val fromSession: BotSession,
|
||||
private val checkSequence: Boolean
|
||||
private val checkSequence: Boolean,
|
||||
/**
|
||||
* 调用者的 [CoroutineContext]
|
||||
*/
|
||||
private val callerContext: CoroutineContext
|
||||
) {
|
||||
private lateinit var toSend: OutgoingPacket
|
||||
|
||||
@ -40,21 +48,22 @@ class TemporaryPacketHandler<P : ServerPacket, R>(
|
||||
this.handler = handler
|
||||
}
|
||||
|
||||
suspend fun send(session: BotSession) {
|
||||
internal suspend fun send(session: BotSession) {
|
||||
require(::handler.isInitialized) { "handler is not initialized" }
|
||||
this.session = session
|
||||
session.socket.sendPacket(toSend)
|
||||
}
|
||||
|
||||
@ExperimentalUnsignedTypes
|
||||
fun filter(session: BotSession, packet: ServerPacket): Boolean =
|
||||
internal fun filter(session: BotSession, packet: ServerPacket): Boolean =
|
||||
expectationClass.isInstance(packet) && session === this.fromSession && if (checkSequence) packet.sequenceId == toSend.sequenceId else true
|
||||
|
||||
suspend fun doReceive(packet: ServerPacket) {
|
||||
internal suspend fun doReceiveWithoutExceptions(packet: ServerPacket) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ret = try {
|
||||
handler(packet as P)
|
||||
} catch (e: Exception) {
|
||||
withContext(callerContext) {
|
||||
handler(packet as P)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
deferred.completeExceptionally(e)
|
||||
return
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import net.mamoe.mirai.utils.io.*
|
||||
class RequestCaptchaTransmissionPacket(
|
||||
private val bot: UInt,
|
||||
private val token0825: ByteArray,
|
||||
private val verificationSequence: Int,
|
||||
private val captchaSequence: Int,
|
||||
private val token00BA: ByteArray
|
||||
) : OutgoingPacket() {
|
||||
@Tested
|
||||
@ -31,7 +31,7 @@ class RequestCaptchaTransmissionPacket(
|
||||
writeHex("01 03 00 19")
|
||||
writeHex(TIMProtocol.publicKey)
|
||||
writeHex("13 00 05 00 00 00 00")
|
||||
writeUByte(verificationSequence.toUByte())
|
||||
writeUByte(captchaSequence.toUByte())
|
||||
writeHex("00 28")
|
||||
writeFully(token00BA)
|
||||
writeHex("00 10")
|
||||
@ -48,7 +48,7 @@ class SubmitCaptchaPacket(
|
||||
private val bot: UInt,
|
||||
private val token0825: ByteArray,
|
||||
private val captcha: String,
|
||||
private val verificationToken: IoBuffer
|
||||
private val captchaToken: IoBuffer
|
||||
) : OutgoingPacket() {
|
||||
init {
|
||||
require(captcha.length == 4) { "captcha.length must == 4" }
|
||||
@ -71,7 +71,7 @@ class SubmitCaptchaPacket(
|
||||
writeHex("14 00 05 00 00 00 00 00 04")
|
||||
writeStringUtf8(captcha.toUpperCase())
|
||||
writeHex("00 38")
|
||||
writeFully(verificationToken)
|
||||
writeFully(captchaToken)
|
||||
|
||||
writeShort(16)
|
||||
writeHex(TIMProtocol.key00BAFix)//16
|
||||
@ -113,14 +113,14 @@ class OutgoingCaptchaRefreshPacket(
|
||||
open class CaptchaTransmissionResponsePacket(input: ByteReadPacket) : ServerCaptchaPacket(input) {
|
||||
|
||||
lateinit var captchaSectionN: IoBuffer
|
||||
lateinit var verificationToken: IoBuffer//56bytes
|
||||
lateinit var captchaToken: IoBuffer//56bytes
|
||||
var transmissionCompleted: Boolean = false//验证码是否已经传输完成
|
||||
lateinit var token00BA: ByteArray//40 bytes
|
||||
|
||||
|
||||
override fun decode() = with(input) {
|
||||
input.discardExact(10)//13 00 05 01 00 00 01 23 00 38
|
||||
verificationToken = readIoBuffer(56)
|
||||
captchaToken = readIoBuffer(56)
|
||||
|
||||
val length = readShort()
|
||||
captchaSectionN = readIoBuffer(length)
|
||||
|
@ -5,4 +5,5 @@ package net.mamoe.mirai.utils
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
internal annotation class Tested
|
||||
@Suppress("unused")
|
||||
internal annotation class Tested(val date: String = "")
|
Loading…
Reference in New Issue
Block a user