Commit bd638829 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
parents be8ab11f df56c86f
...@@ -5,6 +5,7 @@ import android.net.wifi.WifiManager ...@@ -5,6 +5,7 @@ import android.net.wifi.WifiManager
import android.os.Build import android.os.Build
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import kotlinx.io.core.toByteArray import kotlinx.io.core.toByteArray
import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.localIpAddress import net.mamoe.mirai.utils.localIpAddress
import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.md5
import java.io.File import java.io.File
......
...@@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network ...@@ -3,29 +3,29 @@ package net.mamoe.mirai.qqandroid.network
import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.Cancellable
import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.QQImpl
import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent
import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent
import net.mamoe.mirai.qqandroid.network.protocol.packet.* import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
...@@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -41,14 +41,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private lateinit var channel: PlatformSocket private lateinit var channel: PlatformSocket
override suspend fun login() { override suspend fun login() {
if (::channel.isInitialized) { if (::channel.isInitialized) {
channel.close() channel.close()
} }
channel = PlatformSocket() channel = PlatformSocket()
channel.connect("113.96.13.208", 8080) channel.connect("113.96.13.208", 8080)
launch(CoroutineName("Incoming Packet Receiver")) { processReceive() } this.launch(CoroutineName("Incoming Packet Receiver")) { processReceive() }
// bot.logger.info("Trying login") // bot.logger.info("Trying login")
var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect() var response: LoginPacket.LoginPacketResponse = LoginPacket.SubCommand9(bot.client).sendAndExpect()
...@@ -104,10 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -104,10 +103,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
override suspend fun init() { override suspend fun init() {
//start updating friend/group list bot.logger.info("开始加载好友信息")
bot.logger.info("Start updating friend/group list")
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
close()
}
}
/*
* 开始加载Contact表
* */
var currentFriendCount = 0 var currentFriendCount = 0
var totalFriendCount: Short = 0 var totalFriendCount: Short
while (true) { while (true) {
val data = FriendList.GetFriendGroupList( val data = FriendList.GetFriendGroupList(
bot.client, bot.client,
...@@ -117,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -117,14 +124,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
0 0
).sendAndExpect<FriendList.GetFriendGroupList.Response>() ).sendAndExpect<FriendList.GetFriendGroupList.Response>()
totalFriendCount = data.totalFriendCount totalFriendCount = data.totalFriendCount
bot.qqs.delegate.addAll( data.friendList.forEach {
data.friendList.map { // atomic add
QQImpl(this@QQAndroidBotNetworkHandler.bot, EmptyCoroutineContext, it.friendUin!!).also { bot.qqs.delegate.addLast(QQImpl(bot, EmptyCoroutineContext, it.friendUin).also {
currentFriendCount++ currentFriendCount++
})
} }
} bot.logger.verbose("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
)
bot.logger.info("正在加载好友信息 ${currentFriendCount}/${totalFriendCount}")
if (currentFriendCount >= totalFriendCount) { if (currentFriendCount >= totalFriendCount) {
break break
} }
...@@ -138,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -138,22 +144,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
).sendAndExpect<FriendList.GetTroopList.Response>(100000) ).sendAndExpect<FriendList.GetTroopList.Response>(100000)
println(data.contentToString()) println(data.contentToString())
*/ */
} }
/**
* 单线程处理包的接收, 分割和连接.
*/
@Suppress("PrivatePropertyName")
private val PacketReceiveDispatcher = newCoroutineDispatcher(1)
/**
* 单线程处理包的解析 (协程挂起效率够)
*/
@Suppress("PrivatePropertyName")
private val PacketProcessDispatcher = newCoroutineDispatcher(1)
/** /**
* 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理 * 缓存超时处理的 [Job]. 超时后将清空缓存, 以免阻碍后续包的处理
*/ */
...@@ -168,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -168,12 +160,12 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private var expectingRemainingLength: Long = 0 private var expectingRemainingLength: Long = 0
/** /**
* 在 [PacketProcessDispatcher] 调度器中解析包内容. * 解析包内容.
* *
* @param input 一个完整的包的内容, 去掉开头的 int 包长度 * @param input 一个完整的包的内容, 去掉开头的 int 包长度
*/ */
fun parsePacketAsync(input: Input): Job { fun parsePacketAsync(input: Input): Job {
return this.launch(PacketProcessDispatcher) { return this.launch {
input.use { parsePacket(it) } input.use { parsePacket(it) }
} }
} }
...@@ -320,27 +312,28 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -320,27 +312,28 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val rawInput = try { val rawInput = try {
channel.read() channel.read()
} catch (e: ClosedChannelException) { } catch (e: ClosedChannelException) {
dispose()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} catch (e: ReadPacketInternalException) { } catch (e: ReadPacketInternalException) {
bot.logger.error("Socket channel read failed: ${e.message}") bot.logger.error("Socket channel read failed: ${e.message}")
dispose()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} catch (e: CancellationException) { } catch (e: CancellationException) {
return return
} catch (e: Throwable) { } catch (e: Throwable) {
bot.logger.error("Caught unexpected exceptions", e) bot.logger.error("Caught unexpected exceptions", e)
dispose()
bot.tryReinitializeNetworkHandler(e) bot.tryReinitializeNetworkHandler(e)
return return
} }
launch(context = PacketReceiveDispatcher + CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) { launch(CoroutineName("Incoming Packet handler"), start = CoroutineStart.ATOMIC) {
packetReceiveLock.withLock {
processPacket(rawInput) processPacket(rawInput)
} }
} }
} }
}
private val packetReceiveLock: Mutex = Mutex()
/** /**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
...@@ -364,7 +357,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -364,7 +357,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E { private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
withContext(this@QQAndroidBotNetworkHandler.coroutineContext + CoroutineName("Packet sender")) {
channel.send(delegate) channel.send(delegate)
}
bot.logger.info("Send: ${this.commandName}") bot.logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) { return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
...@@ -386,11 +381,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -386,11 +381,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId fun filter(commandName: String, sequenceId: Int) = this.commandName == commandName && this.sequenceId == sequenceId
} }
override fun dispose(cause: Throwable?) { override fun close(cause: Throwable?) {
if (::channel.isInitialized) { if (::channel.isInitialized) {
channel.close() channel.close()
} }
super.dispose(cause) super.close(cause)
} }
override suspend fun awaitDisconnection() = supervisor.join() override suspend fun awaitDisconnection() = supervisor.join()
......
...@@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode( ...@@ -67,7 +67,7 @@ internal class FriendListSubSrvRspCode(
@Serializable @Serializable
internal class FriendInfo( internal class FriendInfo(
@SerialId(0) val friendUin: Long? = 0, @SerialId(0) val friendUin: Long,
@SerialId(1) val groupId: Byte, @SerialId(1) val groupId: Byte,
@SerialId(2) val faceId: Short, @SerialId(2) val faceId: Short,
@SerialId(3) val remark: String = "", @SerialId(3) val remark: String = "",
......
...@@ -11,6 +11,6 @@ class SyncCookie( ...@@ -11,6 +11,6 @@ class SyncCookie(
@SerialId(4) val unknown2: Long = 3497826378, @SerialId(4) val unknown2: Long = 3497826378,
@SerialId(5) val const1: Long = 1680172298, @SerialId(5) val const1: Long = 1680172298,
@SerialId(6) val const2: Long = 2424173273, @SerialId(6) val const2: Long = 2424173273,
@SerialId(7) val unknown3: Long = 83, @SerialId(7) val unknown3: Long = 0,
@SerialId(8) val unknown4: Long = 0 @SerialId(8) val unknown4: Long = 0
) : ProtoBuf ) : ProtoBuf
\ No newline at end of file
...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ ...@@ -16,7 +16,6 @@ import net.mamoe.mirai.utils.io.writeQQ
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket]. * 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket].
* 只有最终的包才会被包装为 [OutgoingPacket]. * 只有最终的包才会被包装为 [OutgoingPacket].
*/ */
@UseExperimental(ExperimentalUnsignedTypes::class)
internal class OutgoingPacket constructor( internal class OutgoingPacket constructor(
name: String?, name: String?,
val commandName: String, val commandName: String,
......
...@@ -25,8 +25,8 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket ...@@ -25,8 +25,8 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.utils.toMessageChain import net.mamoe.mirai.qqandroid.utils.toMessageChain
import net.mamoe.mirai.qqandroid.utils.toRichTextElems import net.mamoe.mirai.qqandroid.utils.toRichTextElems
import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.currentTimeSeconds
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.random.Random import kotlin.random.Random
...@@ -61,8 +61,8 @@ internal class MessageSvc { ...@@ -61,8 +61,8 @@ internal class MessageSvc {
client: QQAndroidClient, client: QQAndroidClient,
msgTime: Long //PbPushMsg.msg.msgHead.msgTime msgTime: Long //PbPushMsg.msg.msgHead.msgTime
): OutgoingPacket = buildOutgoingUniPacket( ): OutgoingPacket = buildOutgoingUniPacket(
client, client//,
extraData = EXTRA_DATA.toReadPacket() // extraData = EXTRA_DATA.toReadPacket()
) { ) {
writeProtoBuf( writeProtoBuf(
MsgSvc.PbGetMsgReq.serializer(), MsgSvc.PbGetMsgReq.serializer(),
...@@ -163,8 +163,9 @@ internal class MessageSvc { ...@@ -163,8 +163,9 @@ internal class MessageSvc {
) )
), ),
msgSeq = client.atomicNextMessageSequenceId(), msgSeq = client.atomicNextMessageSequenceId(),
msgRand = Random.nextInt().absoluteValue msgRand = Random.nextInt().absoluteValue,
// syncCookie = client.c2cMessageSync.syncCookie.takeIf { it.isNotEmpty() } ?: "08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00".hexToBytes(), syncCookie = client.c2cMessageSync.syncCookie?.takeIf { it.isNotEmpty() }
?: SyncCookie(currentTimeSeconds).toByteArray(SyncCookie.serializer())
// msgVia = 1 // msgVia = 1
) )
) )
......
...@@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource ...@@ -12,7 +12,6 @@ import net.mamoe.mirai.qqandroid.utils.GuidSource
import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag import net.mamoe.mirai.qqandroid.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.qqandroid.utils.guidFlag import net.mamoe.mirai.qqandroid.utils.guidFlag
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.cryptor.decryptBy
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.discardExact
...@@ -307,7 +306,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -307,7 +306,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
@InternalAPI @InternalAPI
@UseExperimental(MiraiDebugAPI::class) @UseExperimental(MiraiDebugAPI::class)
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse = this.debugPrint("login解析").run { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): LoginPacketResponse {
// 00 09 sub cmd // 00 09 sub cmd
// 00 type // 00 type
// 00 02 // 00 02
...@@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -317,13 +316,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
val subCommand = readUShort().toInt() val subCommand = readUShort().toInt()
println("subCommand=$subCommand") // println("subCommand=$subCommand")
val type = readUByte() val type = readUByte()
println("type=$type") // println("type=$type")
discardExact(2) discardExact(2)
val tlvMap: TlvMap = this.readTLVMap() val tlvMap: TlvMap = this.readTLVMap()
tlvMap.printTLVMap() // tlvMap.printTLVMap()
return when (type.toInt()) { return when (type.toInt()) {
0 -> onLoginSuccess(tlvMap, bot) 0 -> onLoginSuccess(tlvMap, bot)
1, 15 -> onErrorMessage(tlvMap) 1, 15 -> onErrorMessage(tlvMap)
...@@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -340,7 +339,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
bot: QQAndroidBot bot: QQAndroidBot
): LoginPacketResponse.DeviceLockLogin { ): LoginPacketResponse.DeviceLockLogin {
bot.client.t104 = tlvMap.getOrFail(0x104) bot.client.t104 = tlvMap.getOrFail(0x104)
println("403: " + tlvMap[0x403]?.toUHexString()) // println("403: " + tlvMap[0x403]?.toUHexString())
return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403)) return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403))
} }
...@@ -382,8 +381,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -382,8 +381,7 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
data = imageData.readRemainingBytes().toIoBuffer(), data = imageData.readRemainingBytes().toIoBuffer(),
sign = sign sign = sign
) )
} } else error("UNKNOWN CAPTCHA QUESTION: $question")
else error("UNKNOWN CAPTCHA QUESTION: $question")
} }
error("UNKNOWN CAPTCHA") error("UNKNOWN CAPTCHA")
...@@ -392,13 +390,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -392,13 +390,13 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
@UseExperimental(MiraiDebugAPI::class) @UseExperimental(MiraiDebugAPI::class)
private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success { private fun onLoginSuccess(tlvMap: TlvMap, bot: QQAndroidBot): LoginPacketResponse.Success {
val client = bot.client val client = bot.client
println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() }) //println("TLV KEYS: " + tlvMap.keys.joinToString { it.contentToString() })
tlvMap[0x150]?.let { client.analysisTlv150(it) } tlvMap[0x150]?.let { client.analysisTlv150(it) }
tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") } // tlvMap[0x305]?.let { println("TLV 0x305=${it.toUHexString()}") }
tlvMap[0x161]?.let { client.analysisTlv161(it) } tlvMap[0x161]?.let { client.analysisTlv161(it) }
tlvMap[0x119]?.let { t119Data -> tlvMap[0x119]?.let { t119Data ->
t119Data.decryptBy(client.tgtgtKey).toReadPacket().debugPrint("0x119data").apply { t119Data.decryptBy(client.tgtgtKey).read {
discardExact(2) // always discarded. 00 1C discardExact(2) // always discarded. 00 1C
// 00 1C // 00 1C
// 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00 // 01 08 00 10 A1 73 76 98 64 E0 38 C6 C8 18 73 FA D3 85 DA D6 01 6A 00 30 1D 99 4A 28 7E B3 B8 AC 74 B9 C4 BB 6D BB 41 72 F7 5C 9F 0F 79 8A 82 4F 1F 69 34 6D 10 D6 BB E8 A3 4A 2B 5D F1 C7 05 3C F8 72 EF CF 67 E4 3C 94 01 06 00 78 B4 ED 9F 44 ED 10 18 A8 85 0A 8A 85 79 45 47 7F 25 AA EE 2C 53 83 80 0A B3 B0 47 3E 95 51 A4 AE 3E CA A0 1D B4 91 F7 BB 2E 94 76 A8 C8 97 02 C4 5B 15 02 B7 03 9A FC C2 58 6D 17 92 46 AE EB 2F 6F 65 B8 69 6C D6 9D AC 18 6F 07 53 AC FE FA BC BD CE 57 13 10 2D 5A C6 50 AA C2 AE 18 D4 FD CD F2 E0 D1 25 29 56 21 35 8F 01 9D D6 69 44 8F 06 D0 23 26 D3 0E E6 E6 B7 01 0C 00 10 73 32 61 4E 2C 72 35 58 68 28 47 3E 2B 6E 52 62 01 0A 00 48 A4 DA 48 FB B4 8D DA 7B 86 D7 A7 FE 01 1B 70 6F 54 F8 55 38 B0 AD 1B 0C 0B B9 F6 94 24 F8 9E 30 32 22 99 0C 22 CD 44 B8 B0 8A A8 65 E1 B8 F0 49 EF E1 23 D7 0D A3 F1 BB 52 B7 4B AF BD 50 EA BF 15 02 78 2B 8B 10 FB 15 01 0D 00 10 29 75 38 72 21 5D 3F 24 37 46 67 79 2B 65 6D 34 01 14 00 60 00 01 5E 19 65 8C 00 58 93 DD 4D 2C 2D 01 44 99 62 B8 7A EF 04 C5 71 0B F1 BE 4C F4 21 F2 97 B0 14 67 0E 14 9F D8 A2 0B 93 40 90 80 F3 59 7A 69 45 D7 D4 53 4C 08 3A 56 1D C9 95 36 2C 7C 5E EE 36 47 5F AE 26 72 76 FD FD 69 E6 0C 2D 3A E8 CF D4 8D 76 C9 17 C3 E3 CD 21 AB 04 6B 70 C5 EC EC 01 0E 00 10 56 48 3E 29 3A 5A 21 74 55 6A 2C 72 58 73 79 71 01 03 00 30 9B A6 5D 85 5C 40 7C 28 E7 05 A9 25 CA F5 FC C0 51 40 85 F3 2F D2 37 F9 09 A6 E6 56 7F 7A 2E 7D 9F B9 1C 00 65 55 D2 A9 60 03 77 AB 6A F5 3F CE 01 33 00 30 F4 3A A7 08 E2 04 FA C8 9D 54 49 DE 63 EA F0 A5 1C C4 03 57 51 B6 AE 0B 55 41 F8 AB 22 F1 DC A3 B0 73 08 55 14 02 BF FF 55 87 42 4C 23 70 91 6A 01 34 00 10 61 C7 02 3F 1D BE A6 27 2F 24 D4 92 95 68 71 EF 05 28 00 1A 7B 22 51 49 4D 5F 69 6E 76 69 74 61 74 69 6F 6E 5F 62 69 74 22 3A 22 31 22 7D 03 22 00 10 CE 1E 2E DC 69 24 4F 9B FF 2F 52 D8 8F 69 DD 40 01 1D 00 76 5F 5E 10 E2 34 36 79 27 23 53 4D 65 6B 6A 33 6D 7D 4E 3C 5F 00 60 00 01 5E 19 65 8C 00 58 67 00 9C 02 E4 BC DB A3 93 98 A1 ED 4C 91 08 6F 0C 06 E0 12 6A DC 14 5B 4D 20 7C 82 83 AE 94 53 A2 4A A0 35 FF 59 9D F3 EF 82 42 61 67 2A 31 E7 87 7E 74 E7 A3 E7 5C A8 3C 87 CF 40 6A 9F E5 F7 20 4E 56 C6 4F 1C 98 3A 8B A9 4F 1D 10 35 C2 3B A1 08 7A 89 0B 25 0C 63 01 1F 00 0A 00 01 51 80 00 00 03 84 00 00 01 38 00 0E 00 00 00 01 01 0A 00 27 8D 00 00 00 00 00 01 1A 00 13 02 5B 06 01 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 05 22 00 14 00 00 00 00 76 E4 B8 DD AB 53 02 9F 5E 19 65 8C 20 02 ED BD 05 37 00 17 01 01 00 00 00 00 76 E4 B8 DD 04 AB 53 02 9F 5E 19 65 8C 20 02 ED BD 01 20 00 0A 4D 39 50 57 50 6E 4C 31 65 4F 01 6D 00 2C 31 7A 50 7A 63 72 70 4D 30 43 6E 31 37 4C 32 32 6E 77 2D 36 7A 4E 71 48 48 59 41 35 48 71 77 41 37 6D 76 4F 63 2D 4A 56 77 47 51 5F 05 12 03 5D 00 0E 00 0A 74 65 6E 70 61 79 2E 63 6F 6D 00 2C 6E 4A 72 55 55 74 63 2A 34 7A 32 76 31 66 6A 75 77 6F 6A 65 73 72 76 4F 68 70 66 45 76 4A 75 55 4B 6D 34 43 2D 76 74 38 4D 77 38 5F 00 00 00 11 6F 70 65 6E 6D 6F 62 69 6C 65 2E 71 71 2E 63 6F 6D 00 2C 78 59 35 65 62 4D 74 48 44 6D 30 53 6F 68 56 71 68 33 43 79 79 34 6F 63 65 4A 46 6A 51 58 65 68 30 44 61 75 55 30 6C 78 65 52 6B 5F 00 00 00 0B 64 6F 63 73 2E 71 71 2E 63 6F 6D 00 2C 64 6A 62 79 47 57 45 4F 34 58 34 6A 36 4A 73 48 45 65 6B 73 69 74 72 78 79 62 57 69 77 49 68 46 45 70 72 4A 59 4F 2D 6B 36 47 6F 5F 00 00 00 0E 63 6F 6E 6E 65 63 74 2E 71 71 2E 63 6F 6D 00 2C 64 4C 31 41 79 32 41 31 74 33 58 36 58 58 2A 74 33 64 4E 70 2A 31 61 2D 50 7A 65 57 67 48 70 2D 65 47 78 6B 59 74 71 62 69 6C 55 5F 00 00 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 2C 75 6A 55 5A 4F 6A 4F 48 52 61 75 6B 32 55 50 38 77 33 34 68 36 69 46 38 2A 77 4E 50 35 2D 66 54 75 37 67 39 56 67 44 57 2A 6B 6F 5F 00 00 00 0A 76 69 70 2E 71 71 2E 63 6F 6D 00 2C 37 47 31 44 6F 54 2D 4D 57 50 63 2D 62 43 46 68 63 62 32 56 38 6E 77 4A 75 41 51 63 54 39 77 45 49 62 57 43 4A 4B 44 4D 6C 6D 34 5F 00 00 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 2C 7A 73 70 5A 56 43 59 45 7A 35 2A 4F 6B 4E 68 6E 74 79 61 69 6E 6F 68 4D 32 6B 41 6C 2A 74 31 63 7A 48 57 77 30 41 6A 4B 50 4B 6B 5F 00 00 00 0B 67 61 6D 65 2E 71 71 2E 63 6F 6D 00 2C 32 6F 2D 51 53 36 65 43 70 37 6A 43 4E 34 6A 74 6E 47 4F 4B 33 67 73 32 63 4A 6F 56 71 58 65 44 48 61 55 39 65 34 2D 32 34 64 30 5F 00 00 00 0C 71 71 77 65 62 2E 71 71 2E 63 6F 6D 00 2C 63 54 4D 79 64 51 43 35 50 74 43 45 51 72 6F 33 53 54 41 66 7A 56 2D 44 76 46 56 35 58 6D 56 6B 49 31 68 4C 55 48 4E 65 76 56 38 5F 00 00 00 0D 6F 66 66 69 63 65 2E 71 71 2E 63 6F 6D 00 2C 6F 73 72 54 36 32 69 37 66 76 6D 49 50 64 6F 58 4B 48 74 38 58 52 59 56 77 72 7A 6E 69 31 58 7A 57 4C 77 2A 71 36 33 44 74 73 6F 5F 00 00 00 09 74 69 2E 71 71 2E 63 6F 6D 00 2C 41 61 77 4D 78 4D 32 79 58 51 47 75 72 75 55 6C 66 53 58 79 5A 57 48 53 78 52 57 58 50 74 6B 6B 4F 78 6F 66 4A 59 47 6C 71 68 34 5F 00 00 00 0B 6D 61 69 6C 2E 71 71 2E 63 6F 6D 00 2C 67 72 57 68 58 77 34 4C 6E 4B 49 4F 67 63 78 45 71 70 33 61 45 67 37 38 46 7A 77 4E 6D 4B 48 56 6E 6F 50 4C 4F 32 6D 57 6D 6E 38 5F 00 00 00 09 71 7A 6F 6E 65 2E 63 6F 6D 00 2C 72 61 47 79 51 35 54 72 4D 55 7A 6E 74 31 4E 52 44 2D 50 72 74 72 41 55 43 35 6A 61 2D 49 47 2D 73 77 4C 6D 49 51 51 41 44 4C 41 5F 00 00 00 0A 6D 6D 61 2E 71 71 2E 63 6F 6D 00 2C 39 73 2D 4F 51 30 67 76 39 42 6A 37 58 71 52 49 4E 30 35 46 32 64 4D 47 67 47 43 58 57 4A 62 68 63 30 38 63 7A 4B 52 76 6B 78 6B 5F 00 00 03 05 00 10 77 75 6E 54 5F 7E 66 7A 72 40 3C 6E 35 50 53 46 01 43 00 40 3A AE 30 87 81 3D EE BA 31 9C EA 9D 0D D4 73 B1 81 12 E0 94 71 73 7A B0 47 3D 09 47 E5 1B E1 E2 06 1A CB A4 E3 71 9E A6 EA 2A 73 5C C8 D3 B1 2A B1 C7 DA 04 A6 6D 12 26 DF 6B 8B EC C7 12 F8 E1 01 18 00 05 00 00 00 01 00 01 63 00 10 67 6B 60 23 24 6A 55 39 4E 58 24 5E 39 2B 7A 69 01 38 00 5E 00 00 00 09 01 06 00 27 8D 00 00 00 00 00 01 0A 00 24 EA 00 00 00 00 00 01 1C 00 1A 5E 00 00 00 00 00 01 02 00 01 51 80 00 00 00 00 01 03 00 00 1C 20 00 00 00 00 01 20 00 01 51 80 00 00 00 00 01 36 00 1B AF 80 00 00 00 00 01 43 00 1B AF 80 00 00 00 00 01 64 00 1B AF 80 00 00 00 00 01 30 00 0E 00 00 5E 19 65 8C 9F 02 53 AB 00 00 00 00
......
...@@ -25,3 +25,9 @@ object TIMPC : BotFactory { ...@@ -25,3 +25,9 @@ object TIMPC : BotFactory {
*/ */
fun Bot(qq: Long, password: String, configuration: BotConfiguration = BotConfiguration.Default): Bot = TIMPCBot(BotAccount(qq, password), configuration) fun Bot(qq: Long, password: String, configuration: BotConfiguration = BotConfiguration.Default): Bot = TIMPCBot(BotAccount(qq, password), configuration)
} }
/**
* 使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
inline fun TIMPC.Bot(qq: Long, password: String, configuration: (BotConfiguration.() -> Unit)): Bot =
this.Bot(qq, password, BotConfiguration().apply(configuration))
\ No newline at end of file
...@@ -91,8 +91,8 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor ...@@ -91,8 +91,8 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor
heartbeatJob?.join() heartbeatJob?.join()
} }
override fun dispose(cause: Throwable?) { override fun close(cause: Throwable?) {
super.dispose(cause) super.close(cause)
this.heartbeatJob?.cancel(CancellationException("handler closed")) this.heartbeatJob?.cancel(CancellationException("handler closed"))
this.heartbeatJob = null this.heartbeatJob = null
...@@ -312,6 +312,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor ...@@ -312,6 +312,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor
else -> error("No decrypter is found") else -> error("No decrypter is found")
} as? D ?: error("Internal error: could not cast decrypter which is found for factory to class Decrypter") } as? D ?: error("Internal error: could not cast decrypter which is found for factory to class Decrypter")
@UseExperimental(MiraiInternalAPI::class)
suspend fun onPacketReceived(packet: Any) {//complex function, but it doesn't matter suspend fun onPacketReceived(packet: Any) {//complex function, but it doesn't matter
when (packet) { when (packet) {
is TouchPacket.TouchResponse.OK -> { is TouchPacket.TouchResponse.OK -> {
......
...@@ -22,6 +22,7 @@ data class BotAccount( ...@@ -22,6 +22,7 @@ data class BotAccount(
/** /**
* 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改. * 标记直接访问 [BotAccount.id], 而不是访问 [Bot.uin]. 这可能会不兼容未来的 API 修改.
*/ */
@MiraiInternalAPI
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) @Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR)
@Experimental(level = Experimental.Level.WARNING) @Experimental(level = Experimental.Level.WARNING)
......
...@@ -6,6 +6,7 @@ import kotlinx.coroutines.* ...@@ -6,6 +6,7 @@ import kotlinx.coroutines.*
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.event.events.BotOfflineEvent
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.closeAndJoin
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.logStacktrace import net.mamoe.mirai.utils.io.logStacktrace
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
...@@ -92,35 +93,39 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -92,35 +93,39 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
try { try {
if (::_network.isInitialized) { if (::_network.isInitialized) {
BotOfflineEvent(this).broadcast() BotOfflineEvent(this).broadcast()
_network.dispose(cause) _network.closeAndJoin(cause)
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Cannot close network handler", e) logger.error("Cannot close network handler", e)
} }
_network = createNetworkHandler(this.coroutineContext)
loginLoop@ while (true) { loginLoop@ while (true) {
_network = createNetworkHandler(this.coroutineContext)
try { try {
_network.login() _network.login()
break@loginLoop break@loginLoop
} catch (e: Exception) { } catch (e: Exception) {
e.logStacktrace() e.logStacktrace()
_network.dispose(e) _network.closeAndJoin(e)
} }
logger.warning("Login failed. Retrying in 3s...") logger.warning("Login failed. Retrying in 3s...")
delay(3000) delay(3000)
} }
while (true) { repeat(1) block@{
repeat(2) {
try { try {
return _network.init() _network.init()
return@block
} catch (e: Exception) { } catch (e: Exception) {
e.logStacktrace() e.logStacktrace()
_network.dispose(e)
} }
logger.warning("Init failed. Retrying in 3s...") logger.warning("Init failed. Retrying in 3s...")
delay(3000) delay(3000)
} }
logger.error("cannot init. some features may be affected")
}
} }
protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N
...@@ -130,12 +135,12 @@ abstract class BotImpl<N : BotNetworkHandler> constructor( ...@@ -130,12 +135,12 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
override fun dispose(throwable: Throwable?) { override fun dispose(throwable: Throwable?) {
if (throwable == null) { if (throwable == null) {
network.dispose() network.close()
this.botJob.complete() this.botJob.complete()
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
} else { } else {
network.dispose(throwable) network.close(throwable)
this.botJob.completeExceptionally(throwable) this.botJob.completeExceptionally(throwable)
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
......
...@@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel ...@@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.io.PlatformDatagramChannel
* - Key 刷新 * - Key 刷新
* - 所有数据包处理和发送 * - 所有数据包处理和发送
* *
* [BotNetworkHandler.dispose] 时将会 [取消][Job.cancel] 所有此作用域下的协程 * [BotNetworkHandler.close] 时将会 [取消][Job.cancel] 所有此作用域下的协程
*/ */
@Suppress("PropertyName") @Suppress("PropertyName")
abstract class BotNetworkHandler : CoroutineScope { abstract class BotNetworkHandler : CoroutineScope {
...@@ -49,6 +49,8 @@ abstract class BotNetworkHandler : CoroutineScope { ...@@ -49,6 +49,8 @@ abstract class BotNetworkHandler : CoroutineScope {
/** /**
* 初始化获取好友列表等值. * 初始化获取好友列表等值.
*
* 不要使用这个 API. 它会在登录完成后被自动调用.
*/ */
@MiraiInternalAPI @MiraiInternalAPI
open suspend fun init() { open suspend fun init() {
...@@ -62,13 +64,18 @@ abstract class BotNetworkHandler : CoroutineScope { ...@@ -62,13 +64,18 @@ abstract class BotNetworkHandler : CoroutineScope {
/** /**
* 关闭网络接口, 停止所有有关协程和任务 * 关闭网络接口, 停止所有有关协程和任务
*/ */
open fun dispose(cause: Throwable? = null) { open fun close(cause: Throwable? = null) {
if (supervisor.isActive) { if (supervisor.isActive) {
if (cause != null) { if (cause != null) {
supervisor.cancel(CancellationException("handler closed", cause)) supervisor.cancel(CancellationException("NetworkHandler closed", cause))
} else { } else {
supervisor.cancel() supervisor.cancel(CancellationException("NetworkHandler closed"))
} }
} }
} }
} }
suspend fun BotNetworkHandler.closeAndJoin(cause: Throwable? = null){
this.close(cause)
this.supervisor.join()
}
\ No newline at end of file
...@@ -4,6 +4,7 @@ package net.mamoe.mirai ...@@ -4,6 +4,7 @@ package net.mamoe.mirai
import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.Context import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.ContextImpl
// Do not use ServiceLoader. Probably not working on MPP // Do not use ServiceLoader. Probably not working on MPP
@PublishedApi @PublishedApi
...@@ -20,7 +21,14 @@ internal val factory: BotFactory = run { ...@@ -20,7 +21,14 @@ internal val factory: BotFactory = run {
No BotFactory found. Please ensure that you've added dependency of protocol modules. No BotFactory found. Please ensure that you've added dependency of protocol modules.
Available modules: Available modules:
- net.mamoe:mirai-core-timpc - net.mamoe:mirai-core-timpc
- net.mamoe:mirai-core-qqandroid (recommended)
You should have at lease one protocol module installed. You should have at lease one protocol module installed.
-------------------------------------------------------
找不到 BotFactory. 请确保你依赖了至少一个协议模块.
可用的协议模块:
- net.mamoe:mirai-core-timpc
- net.mamoe:mirai-core-qqandroid (推荐)
请添加上述任一模块的依赖(与 mirai-core 版本相同)
""".trimIndent() """.trimIndent()
) )
...@@ -35,3 +43,16 @@ fun Bot(context: Context, qq: Long, password: String, configuration: BotConfigur ...@@ -35,3 +43,16 @@ fun Bot(context: Context, qq: Long, password: String, configuration: BotConfigur
*/ */
inline fun Bot(context: Context, qq: Long, password: String, configuration: (BotConfiguration.() -> Unit)): Bot = inline fun Bot(context: Context, qq: Long, password: String, configuration: (BotConfiguration.() -> Unit)): Bot =
factory.Bot(context, qq, password, configuration) factory.Bot(context, qq, password, configuration)
/**
* 加载现有协议的 [BotFactory], 并使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
fun Bot(qq: Long, password: String, configuration: BotConfiguration = BotConfiguration.Default): Bot =
factory.Bot(ContextImpl(), qq, password, configuration)
/**
* 加载现有协议的 [BotFactory], 并使用指定的 [配置][configuration] 构造 [Bot] 实例
*/
inline fun Bot(qq: Long, password: String, configuration: (BotConfiguration.() -> Unit)): Bot =
factory.Bot(ContextImpl(), qq, password, configuration)
\ No newline at end of file
...@@ -17,6 +17,7 @@ import net.mamoe.mirai.message.data.ImageId ...@@ -17,6 +17,7 @@ import net.mamoe.mirai.message.data.ImageId
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.firstOrNull import net.mamoe.mirai.message.data.firstOrNull
import net.mamoe.mirai.message.sendAsImageTo import net.mamoe.mirai.message.sendAsImageTo
import net.mamoe.mirai.timpc.Bot
import net.mamoe.mirai.timpc.TIMPC import net.mamoe.mirai.timpc.TIMPC
import net.mamoe.mirai.utils.suspendToExternalImage import net.mamoe.mirai.utils.suspendToExternalImage
import java.io.File import java.io.File
...@@ -39,10 +40,8 @@ private fun readTestAccount(): BotAccount? { ...@@ -39,10 +40,8 @@ private fun readTestAccount(): BotAccount? {
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
suspend fun main() { suspend fun main() {
val bot = TIMPC.Bot( // JVM 下也可以不写 `TIMPC.` 引用顶层函数 val bot = TIMPC.Bot( // JVM 下也可以不写 `TIMPC.` 引用顶层函数
readTestAccount() ?: BotAccount(//填写你的账号 1994701121,
id = 1994701121, "123456"
passwordPlainText = "123456"
)
) { ) {
// 覆盖默认的配置 // 覆盖默认的配置
randomDeviceName = false randomDeviceName = false
...@@ -134,6 +133,7 @@ fun Bot.messageDSL() { ...@@ -134,6 +133,7 @@ fun Bot.messageDSL() {
reply(message) reply(message)
} }
listener.complete() // 停止监听
// 自定义的 filter, filter 中 it 为转为 String 的消息. // 自定义的 filter, filter 中 it 为转为 String 的消息.
// 也可以用任何能在处理时使用的变量, 如 subject, sender, message // 也可以用任何能在处理时使用的变量, 如 subject, sender, message
......
...@@ -14,6 +14,7 @@ import kotlinx.io.core.IoBuffer ...@@ -14,6 +14,7 @@ import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.subscribeMessages import net.mamoe.mirai.event.subscribeMessages
import net.mamoe.mirai.timpc.Bot
import net.mamoe.mirai.timpc.TIMPC import net.mamoe.mirai.timpc.TIMPC
import net.mamoe.mirai.utils.LoginFailedException import net.mamoe.mirai.utils.LoginFailedException
import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.LoginSolver
......
...@@ -8,7 +8,6 @@ import kotlinx.coroutines.delay ...@@ -8,7 +8,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
...@@ -23,32 +22,18 @@ import net.mamoe.mirai.message.data.Image ...@@ -23,32 +22,18 @@ import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.buildXMLMessage import net.mamoe.mirai.message.data.buildXMLMessage
import net.mamoe.mirai.message.data.getValue import net.mamoe.mirai.message.data.getValue
import net.mamoe.mirai.message.sendAsImageTo import net.mamoe.mirai.message.sendAsImageTo
import net.mamoe.mirai.utils.ContextImpl
import java.io.File import java.io.File
import java.util.* import java.util.*
import javax.swing.filechooser.FileSystemView import javax.swing.filechooser.FileSystemView
import kotlin.random.Random import kotlin.random.Random
private fun readTestAccount(): BotAccount? {
val file = File("testAccount.txt")
if (!file.exists() || !file.canRead()) {
return null
}
val lines = file.readLines()
return try {
BotAccount(lines[0].toLong(), lines[1])
} catch (e: Throwable) {
null
}
}
@Suppress("UNUSED_VARIABLE") @Suppress("UNUSED_VARIABLE")
suspend fun main() { suspend fun main() {
val bot = Bot( val bot = Bot(
readTestAccount() ?: BotAccount( ContextImpl(),
id = 913366033, 913366033,
passwordPlainText = "a18260132383" "a18260132383"
)
) { ) {
// override config here. // override config here.
}.alsoLogin() }.alsoLogin()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment