diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt index cb76190d3..f709304d3 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt @@ -25,9 +25,11 @@ enum class JceCharset(val kotlinCharset: Charset) { UTF8(Charset.forName("UTF8")) } - internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<SerialId>(index)?.id +/** + * Jce 数据结构序列化和反序列化工具, 能将 kotlinx.serialization 通用的注解标记格式的 `class` 序列化为 [ByteArray] + */ class Jce private constructor(private val charset: JceCharset, context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat { private inner class ListWriter( @@ -152,7 +154,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo this.writeHead(STRUCT_END, 0) } } else if (value is ProtoBuf) { - this.encodeTaggedByteArray(popTag(), net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.dump(value)) + this.encodeTaggedByteArray(popTag(), ProtoBufWithNullableSupport.dump(value)) } else { serializer.serialize(this, value) } @@ -417,7 +419,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo @Suppress("UNCHECKED_CAST") override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? { - println("decodeNullableSerializableValue: ${deserializer.getClassName()}") + // println("decodeNullableSerializableValue: ${deserializer.getClassName()}") if (deserializer is NullReader) { return null } @@ -444,7 +446,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo else input.readByteArray(tag).toMutableList() as T } val tag = popTag() - println(tag) +// println(tag) @Suppress("SENSELESS_COMPARISON") // false positive if (input.skipToTagOrNull(tag) { return deserializer.deserialize(JceListReader(input.readInt(0), input)) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/ProtoBufWithNullableSupport.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/ProtoBufWithNullableSupport.kt index 1c06ac7f0..76167692e 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/ProtoBufWithNullableSupport.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/ProtoBufWithNullableSupport.kt @@ -6,16 +6,12 @@ package net.mamoe.mirai.qqandroid.io.serialization import kotlinx.io.* import kotlinx.serialization.* -import kotlinx.serialization.CompositeDecoder.Companion.READ_DONE import kotlinx.serialization.internal.* import kotlinx.serialization.modules.EmptyModule import kotlinx.serialization.modules.SerialModule +import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoNumberType import kotlinx.serialization.protobuf.ProtoType -import kotlinx.serialization.protobuf.ProtobufDecodingException -import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.Varint.decodeSignedVarintInt -import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.Varint.decodeSignedVarintLong -import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.Varint.decodeVarint import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.Varint.encodeVarint internal typealias ProtoDesc = Pair<Int, ProtoNumberType> @@ -28,6 +24,12 @@ internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefa } +/** + * 带有 null (optional) support 的 Protocol buffers 序列化器. + * 所有的为 null 的属性都将不会被序列化. 以此实现可选属性. + * + * 代码复制自 kotlinx.serialization. 修改部分已进行标注 (详见 "MIRAI MODIFY START") + */ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat { internal open inner class ProtobufWriter(val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() { @@ -171,178 +173,6 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac } } - private open inner class ProtobufReader(val decoder: ProtobufDecoder) : TaggedDecoder<ProtoDesc>() { - override val context: SerialModule - get() = this@ProtoBufWithNullableSupport.context - - private val indexByTag: MutableMap<Int, Int> = mutableMapOf() - private fun findIndexByTag(desc: SerialDescriptor, serialId: Int, zeroBasedDefault: Boolean = false): Int = - (0 until desc.elementsCount).firstOrNull { - extractParameters( - desc, - it, - zeroBasedDefault - ).first == serialId - } ?: -1 - - override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder = when (desc.kind) { - StructureKind.LIST -> RepeatedReader(decoder, currentTag) - StructureKind.CLASS, UnionKind.OBJECT, is PolymorphicKind -> - ProtobufReader(makeDelimited(decoder, currentTagOrNull)) - StructureKind.MAP -> MapEntryReader(makeDelimited(decoder, currentTagOrNull), currentTagOrNull) - else -> throw SerializationException("Primitives are not supported at top-level") - } - - override fun decodeTaggedBoolean(tag: ProtoDesc): Boolean = when (val i = decoder.nextInt(ProtoNumberType.DEFAULT)) { - 0 -> false - 1 -> true - else -> throw ProtobufDecodingException("Expected boolean value (0 or 1), found $i") - } - - override fun decodeTaggedByte(tag: ProtoDesc): Byte = decoder.nextInt(tag.second).toByte() - override fun decodeTaggedShort(tag: ProtoDesc): Short = decoder.nextInt(tag.second).toShort() - override fun decodeTaggedInt(tag: ProtoDesc): Int = decoder.nextInt(tag.second) - override fun decodeTaggedLong(tag: ProtoDesc): Long = decoder.nextLong(tag.second) - override fun decodeTaggedFloat(tag: ProtoDesc): Float = decoder.nextFloat() - override fun decodeTaggedDouble(tag: ProtoDesc): Double = decoder.nextDouble() - override fun decodeTaggedChar(tag: ProtoDesc): Char = decoder.nextInt(tag.second).toChar() - override fun decodeTaggedString(tag: ProtoDesc): String = decoder.nextString() - override fun decodeTaggedEnum(tag: ProtoDesc, enumDescription: SerialDescriptor): Int = - findIndexByTag(enumDescription, decoder.nextInt(ProtoNumberType.DEFAULT), zeroBasedDefault = true) - - @Suppress("UNCHECKED_CAST") - override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T = when { - // encode maps as collection of map entries, not merged collection of key-values - deserializer.descriptor is MapLikeDescriptor -> { - val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>) - val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer) - val setOfEntries = HashSetSerializer(mapEntrySerial).deserialize(this) - setOfEntries.associateBy({ it.key }, { it.value }) as T - } - deserializer.descriptor == ByteArraySerializer.descriptor -> decoder.nextObject() as T - else -> deserializer.deserialize(this) - } - - override fun SerialDescriptor.getTag(index: Int) = this.getProtoDesc(index) - - override fun decodeElementIndex(desc: SerialDescriptor): Int { - while (true) { - if (decoder.curId == -1) // EOF - return READ_DONE - val ind = indexByTag.getOrPut(decoder.curId) { findIndexByTag(desc, decoder.curId) } - if (ind == -1) // not found - decoder.skipElement() - else return ind - } - } - } - - private inner class RepeatedReader(decoder: ProtobufDecoder, val targetTag: ProtoDesc) : ProtobufReader(decoder) { - private var ind = -1 - - override fun decodeElementIndex(desc: SerialDescriptor) = if (decoder.curId == targetTag.first) ++ind else READ_DONE - override fun SerialDescriptor.getTag(index: Int): ProtoDesc = targetTag - } - - private inner class MapEntryReader(decoder: ProtobufDecoder, val parentTag: ProtoDesc?) : ProtobufReader(decoder) { - override fun SerialDescriptor.getTag(index: Int): ProtoDesc = - if (index % 2 == 0) 1 to (parentTag?.second ?: ProtoNumberType.DEFAULT) - else 2 to (parentTag?.second ?: ProtoNumberType.DEFAULT) - } - - internal class ProtobufDecoder(val inp: ByteArrayInputStream) { - val curId - get() = curTag.first - private var curTag: Pair<Int, Int> = -1 to -1 - - init { - readTag() - } - - private fun readTag(): Pair<Int, Int> { - val header = decode32(eofAllowed = true) - curTag = if (header == -1) { - -1 to -1 - } else { - val wireType = header and 0b111 - val fieldId = header ushr 3 - fieldId to wireType - } - return curTag - } - - fun skipElement() { - when (curTag.second) { - VARINT -> nextInt(ProtoNumberType.DEFAULT) - i64 -> nextLong(ProtoNumberType.FIXED) - SIZE_DELIMITED -> nextObject() - i32 -> nextInt(ProtoNumberType.FIXED) - else -> throw ProtobufDecodingException("Unsupported start group or end group wire type") - } - } - - @Suppress("NOTHING_TO_INLINE") - private inline fun assertWireType(expected: Int) { - if (curTag.second != expected) throw ProtobufDecodingException("Expected wire type $expected, but found ${curTag.second}") - } - - fun nextObject(): ByteArray { - assertWireType(SIZE_DELIMITED) - val len = decode32() - check(len >= 0) - val ans = inp.readExactNBytes(len) - readTag() - return ans - } - - fun nextInt(format: ProtoNumberType): Int { - val wireType = if (format == ProtoNumberType.FIXED) i32 else VARINT - assertWireType(wireType) - val ans = decode32(format) - readTag() - return ans - } - - fun nextLong(format: ProtoNumberType): Long { - val wireType = if (format == ProtoNumberType.FIXED) i64 else VARINT - assertWireType(wireType) - val ans = decode64(format) - readTag() - return ans - } - - fun nextFloat(): Float { - assertWireType(i32) - val ans = inp.readToByteBuffer(4).order(ByteOrder.LITTLE_ENDIAN).getFloat() - readTag() - return ans - } - - fun nextDouble(): Double { - assertWireType(i64) - val ans = inp.readToByteBuffer(8).order(ByteOrder.LITTLE_ENDIAN).getDouble() - readTag() - return ans - } - - fun nextString(): String { - val bytes = this.nextObject() - return stringFromUtf8Bytes(bytes) - } - - private fun decode32(format: ProtoNumberType = ProtoNumberType.DEFAULT, eofAllowed: Boolean = false): Int = when (format) { - ProtoNumberType.DEFAULT -> decodeVarint(inp, 64, eofAllowed).toInt() - ProtoNumberType.SIGNED -> decodeSignedVarintInt(inp) - ProtoNumberType.FIXED -> inp.readToByteBuffer(4).order(ByteOrder.LITTLE_ENDIAN).getInt() - } - - private fun decode64(format: ProtoNumberType = ProtoNumberType.DEFAULT): Long = when (format) { - ProtoNumberType.DEFAULT -> decodeVarint(inp, 64) - ProtoNumberType.SIGNED -> decodeSignedVarintLong(inp) - ProtoNumberType.FIXED -> inp.readToByteBuffer(8).order(ByteOrder.LITTLE_ENDIAN).getLong() - } - } - /** * Source for all varint operations: * https://github.com/addthis/stream-lib/blob/master/src/main/java/com/clearspring/analytics/util/Varint.java @@ -381,57 +211,10 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac } return out } - - internal fun decodeVarint(inp: InputStream, bitLimit: Int = 32, eofOnStartAllowed: Boolean = false): Long { - var result = 0L - var shift = 0 - var b: Int - do { - if (shift >= bitLimit) { - // Out of range - throw ProtobufDecodingException("Varint too long: exceeded $bitLimit bits") - } - // Get 7 bits from next byte - b = inp.read() - if (b == -1) { - if (eofOnStartAllowed && shift == 0) return -1 - else throw IOException("Unexpected EOF") - } - result = result or (b.toLong() and 0x7FL shl shift) - shift += 7 - } while (b and 0x80 != 0) - return result - } - - internal fun decodeSignedVarintInt(inp: InputStream): Int { - val raw = decodeVarint(inp, 32).toInt() - val temp = raw shl 31 shr 31 xor raw shr 1 - // This extra step lets us deal with the largest signed values by treating - // negative results from read unsigned methods as like unsigned values. - // Must re-flip the top bit if the original read value had it set. - return temp xor (raw and (1 shl 31)) - } - - internal fun decodeSignedVarintLong(inp: InputStream): Long { - val raw = decodeVarint(inp, 64) - val temp = raw shl 63 shr 63 xor raw shr 1 - // This extra step lets us deal with the largest signed values by treating - // negative results from read unsigned methods as like unsigned values - // Must re-flip the top bit if the original read value had it set. - return temp xor (raw and (1L shl 63)) - - } } companion object : BinaryFormat { - public override val context: SerialModule get() = plain.context - - // todo: make more memory-efficient - private fun makeDelimited(decoder: ProtobufDecoder, parentTag: ProtoDesc?): ProtobufDecoder { - if (parentTag == null) return decoder - val bytes = decoder.nextObject() - return ProtobufDecoder(ByteArrayInputStream(bytes)) - } + override val context: SerialModule get() = plain.context private fun SerialDescriptor.getProtoDesc(index: Int): ProtoDesc { return extractParameters(this, index) @@ -457,9 +240,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac } override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T { - val stream = ByteArrayInputStream(bytes) - val reader = ProtobufReader(ProtobufDecoder(stream)) - return reader.decode(deserializer) + return ProtoBuf.load(deserializer, bytes) } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationHelper.kt deleted file mode 100644 index e75bde933..000000000 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationHelper.kt +++ /dev/null @@ -1,16 +0,0 @@ -package net.mamoe.mirai.qqandroid.io.serialization - -import kotlinx.serialization.SerialDescriptor - -/* - * Helper for kotlinx.serialization - */ - -internal inline fun <reified A: Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? { - val candidates = getElementAnnotations(elementIndex).filterIsInstance<A>() - return when (candidates.size) { - 0 -> null - 1 -> candidates[0] - else -> throw IllegalStateException("There are duplicate annotations of type ${A::class} in the descriptor $this") - } -} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationUtils.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationUtils.kt index 09e042a5f..bf3daae17 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationUtils.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/SerializationUtils.kt @@ -2,6 +2,7 @@ package net.mamoe.mirai.qqandroid.io.serialization import kotlinx.io.core.* import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialDescriptor import kotlinx.serialization.SerializationStrategy import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.ProtoBuf @@ -91,4 +92,27 @@ fun <T : ProtoBuf> ByteArray.loadAs(deserializer: DeserializationStrategy<T>): T */ fun <T : ProtoBuf> Input.readRemainingAsProtoBuf(serializer: DeserializationStrategy<T>): T { return ProtoBufWithNullableSupport.load(serializer, this.readBytes()) -} \ No newline at end of file +} + +/** + * 构造 [RequestPacket] 的 [RequestPacket.sBuffer] + */ +fun <T : JceStruct> jceRequestSBuffer(name: String, serializer: SerializationStrategy<T>, jceStruct: T): ByteArray { + return RequestDataVersion3( + mapOf( + name to JCE_STRUCT_HEAD_OF_TAG_0 + jceStruct.toByteArray(serializer) + JCE_STRUCT_TAIL_OF_TAG_0 + ) + ).toByteArray(RequestDataVersion3.serializer()) +} + +private val JCE_STRUCT_HEAD_OF_TAG_0 = byteArrayOf(0x0A) +private val JCE_STRUCT_TAIL_OF_TAG_0 = byteArrayOf(0x0B) + +internal inline fun <reified A : Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? { + val candidates = getElementAnnotations(elementIndex).filterIsInstance<A>() + return when (candidates.size) { + 0 -> null + 1 -> candidates[0] + else -> throw IllegalStateException("There are duplicate annotations of type ${A::class} in the descriptor $this") + } +} diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index ec7a03344..f3eb44953 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -18,7 +18,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketLogger -import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendListPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.* import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc @@ -32,6 +31,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler override val bot: QQAndroidBot by bot.unsafeWeakRef() override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job]) + override val coroutineContext: CoroutineContext = bot.coroutineContext + CoroutineExceptionHandler { _, throwable -> + throwable.logStacktrace("Exception in NetworkHandler") + } + private lateinit var channel: PlatformSocket override suspend fun login() { @@ -88,19 +91,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}") StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>() - println("登陆完成 开始尝试获取friendList") - println("登陆完成 开始尝试获取friendList") - println("登陆完成 开始尝试获取friendList") - println("登陆完成 开始尝试获取friendList") - println("登陆完成 开始尝试获取friendList") - FriendListPacket( - bot.client, - 0, - 20, - 0, - 0 - ).sendAndExpect<FriendListPacket.GetFriendListResponse>() - } /** @@ -168,18 +158,13 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) { - try { - KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int -> - handlePacket(packetFactory, packet, commandName, sequenceId) - if (packet is MultiPacket<*>) { - packet.forEach { - handlePacket(null, it, commandName, sequenceId) - } + KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int -> + handlePacket(packetFactory, packet, commandName, sequenceId) + if (packet is MultiPacket<*>) { + packet.forEach { + handlePacket(null, it, commandName, sequenceId) } } - } finally { - println() - println() // separate for debugging } } @@ -211,7 +196,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler if (packet is Cancellable && packet.cancelled) return } - bot.logger.info(packet) + bot.logger.info("Received packet: $packet") packetFactory?.run { bot.handle(packet) @@ -325,17 +310,19 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler /** * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) */ - suspend fun <E : Packet> OutgoingPacket.sendAndExpect(): E { + suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timoutMillis: Long = 3000): E { val handler = PacketListener(commandName = commandName, sequenceId = sequenceId) packetListeners.addLast(handler) bot.logger.info("Send: ${this.commandName}") channel.send(delegate) - return withTimeoutOrNull(3000) { + return withTimeoutOrNull(timoutMillis) { @Suppress("UNCHECKED_CAST") handler.await() as E + + // 不要 `withTimeout`. timeout 的异常会不知道去哪了. } ?: net.mamoe.mirai.qqandroid.utils.inline { packetListeners.remove(handler) - error("timeout when sending ${this.commandName}") + error("timeout when sending ${commandName}") } } @@ -351,6 +338,4 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } override suspend fun awaitDisconnection() = supervisor.join() - - override val coroutineContext: CoroutineContext = bot.coroutineContext } \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt index a238f8734..41c6dc6af 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidClient.kt @@ -124,6 +124,7 @@ internal open class QQAndroidClient( @PublishedApi internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray() + var outgoingPacketUnknownValue: ByteArray = 0x02B05B8B.toByteArray() var loginState = 0 var t150: Tlv? = null diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendListRequest.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendListRequest.kt index 24a325239..3966bf581 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendListRequest.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendListRequest.kt @@ -3,8 +3,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.jce import kotlinx.serialization.SerialId import kotlinx.serialization.Serializable import net.mamoe.mirai.qqandroid.io.JceStruct -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd50 -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd6b @Serializable internal class GetFriendListReq( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt index 14773d7e5..36adb78a6 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt @@ -23,7 +23,7 @@ class RequestPacket( @Serializable class RequestDataVersion3( - @SerialId(0) val map: Map<String, ByteArray> + @SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的 ) : JceStruct @Serializable diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index 8c8a6d77d..798a30710 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -9,6 +9,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.loadAs import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush +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.StatSvc import net.mamoe.mirai.utils.DefaultLogger @@ -17,6 +18,7 @@ import net.mamoe.mirai.utils.cryptor.adjustToPublicKey import net.mamoe.mirai.utils.cryptor.decryptBy import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.unzip +import net.mamoe.mirai.utils.withSwitch import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import kotlin.jvm.JvmName @@ -53,7 +55,7 @@ internal val DECRYPTER_16_ZERO = ByteArray(16) internal typealias PacketConsumer<T> = suspend (packetFactory: PacketFactory<T>, packet: T, commandName: String, ssoSequenceId: Int) -> Unit @PublishedApi -internal val PacketLogger: MiraiLogger = DefaultLogger("Packet") +internal val PacketLogger: MiraiLogger = DefaultLogger("Packet").withSwitch(false) @UseExperimental(ExperimentalUnsignedTypes::class) internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( @@ -63,7 +65,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( MessageSvc.PushNotify, MessageSvc.PbGetMsg, MessageSvc.PushForceOffline, - MessageSvc.PbSendMsg + MessageSvc.PbSendMsg, + FriendList.GetFriendGroupList ) { // SvcReqMSFLoginNotify 自己的其他设备上限 // MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机 @@ -193,8 +196,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}") commandName = readString(readInt() - 4) - val unknown = readBytes(readInt() - 4) - if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") + bot.client.outgoingPacketUnknownValue = readBytes(readInt() - 4) dataCompressed = readInt() } @@ -213,7 +215,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( // body val packetFactory = findPacketFactory(commandName) - bot.logger.info("Received: $commandName") + bot.logger.debug("Received commandName: $commandName") return IncomingPacket(packetFactory, ssoSequenceId, packet) } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index 557ff7a71..e8cb52115 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -2,7 +2,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact -import kotlinx.io.core.writeFully import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.Packet import net.mamoe.mirai.message.FriendMessage @@ -26,8 +25,6 @@ import net.mamoe.mirai.qqandroid.utils.toRichTextElems import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.toReadPacket -import kotlin.math.absoluteValue -import kotlin.random.Random class MessageSvc { /** @@ -145,11 +142,11 @@ class MessageSvc { richText = ImMsgBody.RichText( elems = message.toRichTextElems() ) - ), - msgSeq = 17041, - 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(), - msgVia = 1 + ) + // msgSeq = 17041, + // 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(), + // msgVia = 1 ) ) } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt index f7ad93dfc..a1efd444a 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt @@ -3,56 +3,57 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.list import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.io.serialization.jceRequestSBuffer import net.mamoe.mirai.qqandroid.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct -import net.mamoe.mirai.qqandroid.io.writeJcePacket import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetFriendListReq -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataStructSvcReqRegister -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.GetImgUrlReq import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd50 -import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd6b -import net.mamoe.mirai.qqandroid.network.protocol.packet.* +import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory -import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImageDownPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.utils.io.debugPrint -import net.mamoe.mirai.utils.io.debugPrintln -internal object FriendListPacket : - PacketFactory<FriendListPacket.GetFriendListResponse>("friendlist.getFriendGroupList") { +internal class FriendList { - class GetFriendListResponse() : Packet + internal object GetFriendGroupList : PacketFactory<GetFriendGroupList.Response>("friendlist.getFriendGroupList") { + class Response : Packet { + override fun toString(): String = "FriendList.GetFriendGroupList.Response" + } - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GetFriendListResponse { - println("aaaa") - return GetFriendListResponse() - } + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + // 00 00 0A D6 10 03 2C 3C 42 72 85 3C F2 56 29 6D 71 71 2E 49 4D 53 65 72 76 69 63 65 2E 46 72 69 65 6E 64 4C 69 73 74 53 65 72 76 69 63 65 53 65 72 76 61 6E 74 4F 62 6A 66 10 47 65 74 46 72 69 65 6E 64 4C 69 73 74 52 65 71 7D 00 01 0A 82 08 00 01 06 06 46 4C 52 45 53 50 1D 00 01 0A 72 0A 00 03 1C 22 76 E4 B8 DD 3C 41 86 9F 50 0A 60 0A 79 00 0A 0A 02 1A F7 2F 11 1C 21 01 2C 36 09 65 27 74 65 72 6E 69 74 79 4C 50 14 6C 7C 8C 9C A0 14 BC C6 00 DC E6 09 65 27 74 65 72 6E 69 74 79 FC 0F FD 10 00 0C FD 11 00 0C FC 12 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 91 DE DC D7 01 88 19 B8 17 FC 16 FC 17 FC 18 F1 19 27 1E FC 1A F6 1B 00 FC 1C F0 1D 01 F2 1E 5C 4F 2E 39 F0 1F 02 F2 20 00 01 1E 27 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C FC 2A FC 2B FC 2C F6 2D 00 F2 2E 5E 0E BE 48 FC 2F FC 30 F6 31 00 FC 32 F0 33 02 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 02 2D 5C 53 A6 1C 21 01 F2 36 06 E6 A2 A8 E5 A4 B4 4C 50 14 6C 7C 8C 9C A0 0A BC C6 00 DC E6 06 E6 A2 A8 E5 A4 B4 FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 2F 02 FA 13 08 00 04 00 01 1A 0C 1C 20 03 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 20 03 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 A6 A7 F1 EA 02 88 19 B8 17 FC 16 FC 17 FC 18 FC 19 F0 1A 0A F6 1B 0F 54 49 4D E7 A7 BB E5 8A A8 E5 9C A8 E7 BA BF FC 1C FC 1D FC 1E F0 1F FF F2 20 00 02 00 00 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 F2 26 5B 74 16 A2 FC 27 FC 28 FD 29 00 0C F0 2A 02 FC 2B FC 2C F6 2D 00 F2 2E 58 89 FB 37 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 02 3E 03 3F A2 1C 21 01 1D 36 09 48 69 6D 31 38 38 6D 6F 65 4C 50 14 6C 7C 8C 9C A0 0A BC C6 00 DC E6 09 48 69 6D 31 38 38 6D 6F 65 FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 2E 01 FA 13 08 00 04 00 01 1A 0C 1C 20 07 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 20 07 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 A2 FF 8C F0 03 88 19 B8 17 FC 16 FC 17 FC 18 FC 19 F0 1A 0A F6 1B 0F 54 49 4D E7 94 B5 E8 84 91 E5 9C A8 E7 BA BF FC 1C F0 1D 02 F2 1E 58 8C 5F D3 F0 1F 02 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 F2 26 59 A6 FC BD FC 27 FC 28 FD 29 00 0C F0 2A 02 FC 2B FC 2C F6 2D 00 F2 2E 5A 9A A6 F9 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 02 59 17 3E 05 1C 21 02 1C 36 09 E3 82 A2 E3 82 A4 E3 83 A9 4C 50 0B 6C 7C 8C 9C A0 0A BC C6 00 DC E6 09 E3 82 A2 E3 82 A4 E3 83 A9 FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 01 07 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B F0 14 01 FD 15 00 00 0A 08 85 FC DC C8 05 88 19 B8 17 F0 16 01 F1 17 08 F9 F0 18 01 F1 19 27 20 F0 1A 01 F6 1B 0C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF FC 1C F0 1D 02 F2 1E 59 4A 8C EA F0 1F 02 F2 20 00 01 20 27 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 F2 26 5D 4F 9D 77 F2 27 59 84 39 2C FC 28 FD 29 00 0C F0 2A 02 FC 2B F0 2C 06 F6 2D 00 F2 2E 5C 4A B7 A2 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 02 76 E4 B8 DD 1C 21 02 5B 36 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 4C 50 0B 6C 7C 8C 9C A0 0A BC C6 00 DC E6 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 01 07 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B F0 14 01 FD 15 00 00 0A 08 DD F1 92 B7 07 88 19 B8 17 F0 16 01 FC 17 F0 18 01 FC 19 F0 1A 01 F6 1B 0C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF FC 1C FC 1D FC 1E F0 1F 01 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C F0 2A 02 FC 2B FC 2C F6 2D 00 F2 2E 5D B4 12 03 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 02 7C BC D3 C1 1C 21 00 ED 36 09 F0 9F 90 B8 6C 69 74 6F 75 4C 50 14 6C 7C 8C 9C A0 0A BC C6 00 DC E6 09 F0 9F 90 B8 6C 69 74 6F 75 FC 0F FD 10 00 0C FD 11 00 0C FC 12 FA 13 08 00 04 00 01 1A 0C 1C 20 07 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 20 07 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B F0 14 01 FD 15 00 00 0A 08 C1 A7 F3 E5 07 88 19 B8 17 F0 16 01 FC 17 FC 18 FC 19 F0 1A 0A F6 1B 0F 54 49 4D E7 A7 BB E5 8A A8 E5 9C A8 E7 BA BF FC 1C F0 1D 01 F2 1E 5D AA 93 F1 F0 1F 01 F2 20 00 02 00 00 F6 21 00 F6 22 00 FC 23 F2 24 5B C3 68 00 FC 25 F2 26 5C 66 22 C5 F2 27 59 93 B9 9D FC 28 FD 29 00 0C F0 2A 02 FC 2B F0 2C 14 F6 2D 00 F2 2E 5C A7 87 30 FC 2F FC 30 F6 31 00 FC 32 F0 33 02 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 03 00 00 00 00 88 C8 FE 49 1C 21 02 3D 36 0A 32 32 39 34 38 37 33 36 37 33 4C 50 14 6C 7C 8C 9C A0 14 BC C6 00 DC E6 0A 32 32 39 34 38 37 33 36 37 33 FC 0F FD 10 00 0C FD 11 00 0C FC 12 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 C9 FC A3 C6 08 88 19 B8 17 FC 16 FC 17 FC 18 FC 19 FC 1A F6 1B 00 FC 1C FC 1D FC 1E F0 1F 01 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C FC 2A FC 2B FC 2C F6 2D 00 FC 2E FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 03 00 00 00 00 A8 32 51 A1 1C 2C 36 01 4E 4C 50 0B 6C 7C 8C 9C A0 0A BC C6 00 D0 01 E6 0F E3 82 AE E3 83 A9 E3 83 86 E3 82 A3 E3 83 8A FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 01 07 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 A1 A3 C9 C1 0A 88 19 B8 17 FC 16 FC 17 FC 18 F1 19 27 1E F0 1A 65 F6 1B 0C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF F1 1C 27 78 FC 1D FC 1E F0 1F 01 F2 20 00 01 1E 27 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C FC 2A FC 2B FC 2C F6 2D 00 F2 2E 59 62 C5 18 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 03 00 00 00 00 B1 89 BE 09 1C 21 02 58 36 01 4E 4C 50 14 6C 7C 8C 9C A0 0A BC C6 17 6C 69 75 6A 69 61 68 75 61 31 32 33 31 32 33 40 31 32 36 2E 63 6F 6D D0 01 E6 09 4E 61 74 75 72 61 6C 48 47 FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 35 02 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B F0 14 71 FD 15 00 00 0A 08 89 FC A6 8C 0B 88 19 B8 17 FC 16 FC 17 F0 18 04 FC 19 F0 1A 04 F6 1B 0E 69 50 68 6F 6E 65 20 58 E5 9C A8 E7 BA BF FC 1C FC 1D FC 1E F0 1F 01 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 F2 26 5D BB 7C 19 FC 27 FC 28 FD 29 00 0C FC 2A FC 2B FC 2C F6 2D 00 F2 2E 5D B5 3E F2 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 0A 03 00 00 00 00 BC 41 BA A7 1C 21 02 3A 36 06 32 33 33 33 33 33 4C 50 14 6C 7C 8C 9C A0 14 BC C6 00 DC E6 06 32 33 33 33 33 33 FC 0F FD 10 00 0C FD 11 00 0C FC 12 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B FC 14 FD 15 00 00 0A 08 A7 F5 86 E2 0B 88 19 B8 17 FC 16 FC 17 FC 18 FC 19 FC 1A F6 1B 00 FC 1C FC 1D FC 1E F0 1F 01 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C FC 2A FC 2B FC 2C F6 2D 00 F2 2E 5A 76 BE 66 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B 8C 9C AC B0 9F C0 04 DC E9 0C FC 0F FC 10 F0 11 07 F2 12 5E 32 AF F9 FC 13 F9 14 0C FC 15 FC 16 FA 17 02 76 E4 B8 DD 1C 21 02 5B 36 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E 4C 50 0B 6C 7C 8C 9C A0 0A BC C6 00 DC E6 0E 73 74 65 61 6D 63 68 69 6E 61 2E 66 75 6E FC 0F FD 10 00 0C FD 11 00 0C F2 12 00 01 01 07 FA 13 08 00 04 00 01 1A 0C 1C 2C 3C 4C 0B 00 02 1A 0C 1C 2C 3C 4C 0B 00 03 1A 0C 1C 2C 3C 4C 0B 00 07 1A 0C 1C 2C 3C 4C 0B 1C 2C 0B F0 14 01 FD 15 00 00 0A 08 DD F1 92 B7 07 88 19 B8 17 F0 16 01 FC 17 F0 18 01 FC 19 F0 1A 01 F6 1B 0C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF FC 1C FC 1D FC 1E F0 1F 01 FC 20 F6 21 00 F6 22 00 FC 23 FC 24 FC 25 FC 26 FC 27 FC 28 FD 29 00 0C F0 2A 02 FC 2B FC 2C F6 2D 00 F2 2E 5D B4 12 03 FC 2F FC 30 F6 31 00 FC 32 FC 33 FD 34 00 0C FC 35 FC 36 FD 37 00 0C FD 38 00 0C FC 39 FC 3A 0B F0 18 01 FC 19 FA 1A 0C 1C 0B 0B 8C 98 0C A8 0C - operator fun invoke( - client: QQAndroidClient, - friendListStartIndex: Int, - friendListCount: Int, - groupListStartIndex: Int, - groupListCount: Int - ): OutgoingPacket { - return buildOutgoingUniPacket(client, key = client.wLoginSigInfo.d2Key) { - writeJceStruct( - RequestPacket.serializer(), - RequestPacket( - sFuncName = "GetFriendListReq", - sServantName = "mqq.IMService.FriendListServiceServantObj", - iVersion = 3, - cPacketType = 0x003, - iMessageType = 0x00000, - iRequestId = 1921334514, - sBuffer = RequestDataVersion3( - mapOf( - "FL" to GetFriendListReq( + println("aaaa") + this.debugPrint() + return Response() + } + + operator fun invoke( + client: QQAndroidClient, + friendListStartIndex: Int, + friendListCount: Int, + groupListStartIndex: Int, + groupListCount: Int + ): OutgoingPacket { + return buildOutgoingUniPacket(client, bodyType = 1, key = client.wLoginSigInfo.d2Key) { + writeJceStruct( + RequestPacket.serializer(), + RequestPacket( + sFuncName = "GetFriendListReq", + sServantName = "mqq.IMService.FriendListServiceServantObj", + iVersion = 3, + cPacketType = 0x003, + iMessageType = 0x00000, + iRequestId = 1921334514, + sBuffer = jceRequestSBuffer( + "FL", + GetFriendListReq.serializer(), + GetFriendListReq( reqtype = 3, ifReflush = if (friendListStartIndex <= 0) { 0 @@ -86,14 +87,11 @@ internal object FriendListPacket : reqMutualmarkAlienation = 1 ).toByteArray(Vec0xd50.ReqBody.serializer()), vecSnsTypelist = listOf(13580L, 13581L, 13582L) - ).toByteArray(GetFriendListReq.serializer()) + ) ) - ).toByteArray(RequestDataVersion3.serializer()) + ) ) - ) - this.build().debugPrint() + } } } - -} - +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt index ca62166bf..766f26ba7 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt @@ -4,11 +4,9 @@ import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.data.Packet import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport -import net.mamoe.mirai.qqandroid.io.serialization.toByteArray +import net.mamoe.mirai.qqandroid.io.serialization.jceRequestSBuffer import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct import net.mamoe.mirai.qqandroid.network.QQAndroidClient -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataStructSvcReqRegister -import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket @@ -61,67 +59,65 @@ class StatSvc { RequestPacket( sServantName = "PushService", sFuncName = "SvcReqRegister", - sBuffer = RequestDataVersion3( - mapOf( - "SvcReqRegister" to RequestDataStructSvcReqRegister( - SvcReqRegister( - cConnType = 0, - lBid = 1 or 2 or 4, - lUin = client.uin, - iStatus = client.onlineStatus.id, - bKikPC = 0, // 是否把 PC 踢下线 - bKikWeak = 0, - timeStamp = 0, - // timeStamp = currentTimeSeconds // millis or seconds?? - iLargeSeq = 1551, // ? - bOpenPush = 1, - iLocaleID = 2052, - bRegType = - (if (regPushReason == RegPushReason.appRegister || - regPushReason == RegPushReason.fillRegProxy || - regPushReason == RegPushReason.createDefaultRegInfo || - regPushReason == RegPushReason.setOnlineStatus - ) 0 else 1).toByte(), - bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0, - iOSVersion = client.device.version.sdk.toLong(), - cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0, - vecGuid = client.device.guid, - strDevName = client.device.model.encodeToString(), - strDevType = client.device.model.encodeToString(), - strOSVer = client.device.version.release.encodeToString(), + sBuffer = jceRequestSBuffer( + "SvcReqRegister", + SvcReqRegister.serializer(), + SvcReqRegister( + cConnType = 0, + lBid = 1 or 2 or 4, + lUin = client.uin, + iStatus = client.onlineStatus.id, + bKikPC = 0, // 是否把 PC 踢下线 + bKikWeak = 0, + timeStamp = 0, + // timeStamp = currentTimeSeconds // millis or seconds?? + iLargeSeq = 1551, // ? + bOpenPush = 1, + iLocaleID = 2052, + bRegType = + (if (regPushReason == RegPushReason.appRegister || + regPushReason == RegPushReason.fillRegProxy || + regPushReason == RegPushReason.createDefaultRegInfo || + regPushReason == RegPushReason.setOnlineStatus + ) 0 else 1).toByte(), + bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0, + iOSVersion = client.device.version.sdk.toLong(), + cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0, + vecGuid = client.device.guid, + strDevName = client.device.model.encodeToString(), + strDevType = client.device.model.encodeToString(), + strOSVer = client.device.version.release.encodeToString(), - uOldSSOIp = 0, - uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String -> - acc or ((s.toLong() shl (index * 16))) - }, - strVendorName = "MIUI", - strVendorOSName = "?ONEPLUS A5000_23_17", - // register 时还需要 - /* - var44.uNewSSOIp = field_127445; - var44.uOldSSOIp = field_127444; - var44.strVendorName = ROMUtil.getRomName(); - var44.strVendorOSName = ROMUtil.getRomVersion(20); - */ - bytes_0x769_reqbody = ProtoBufWithNullableSupport.dump( - Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody( - rpt_config_list = listOf( - Oidb0x769.ConfigSeq( - type = 46, - version = 0 - ), - Oidb0x769.ConfigSeq( - type = 283, - version = 0 - ) - ) + uOldSSOIp = 0, + uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String -> + acc or ((s.toLong() shl (index * 16))) + }, + strVendorName = "MIUI", + strVendorOSName = "?ONEPLUS A5000_23_17", + // register 时还需要 + /* + var44.uNewSSOIp = field_127445; + var44.uOldSSOIp = field_127444; + var44.strVendorName = ROMUtil.getRomName(); + var44.strVendorOSName = ROMUtil.getRomVersion(20); + */ + bytes_0x769_reqbody = ProtoBufWithNullableSupport.dump( + Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody( + rpt_config_list = listOf( + Oidb0x769.ConfigSeq( + type = 46, + version = 0 + ), + Oidb0x769.ConfigSeq( + type = 283, + version = 0 ) - ), - bSetMute = 0 + ) ) - ).toByteArray(RequestDataStructSvcReqRegister.serializer()) + ), + bSetMute = 0 ) - ).toByteArray(RequestDataVersion3.serializer()) + ) ) ) } diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt index 68c1b5cea..b9da65d88 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt @@ -219,7 +219,7 @@ private fun parseSsoFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP commandName = readString(readInt() - 4) DebugLogger.warning("commandName=$commandName") val unknown = readBytes(readInt() - 4) - if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") + //if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") check(readInt() == 0) } @@ -261,7 +261,7 @@ private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP commandName = readString(readInt() - 4) DebugLogger.warning("commandName=$commandName") val unknown = readBytes(readInt() - 4) - if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") + //if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}") check(readInt() == 0) } diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt index dd533ea25..978638517 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/test/JceDataClassGenerator.kt @@ -3,19 +3,23 @@ package test; import java.io.File fun main(){ - val var9 = toJCEInfo( - File( - """ - E:\Projects\QQAndroidFF\app\src\main\java\PushNotifyPack\RequestPushForceOffline.java - """.trimIndent() - ).readText() - ) println( "import kotlinx.serialization.SerialId\n" + "import kotlinx.serialization.Serializable\n" + "import net.mamoe.mirai.qqandroid.io.JceStruct\n" ) - println(var9.toString()) + File( + """ + E:\Projects\QQAndroidFF\app\src\main\java\friendlist\ + """.trimIndent() + ).listFiles()!!.forEach { + try { + println(toJCEInfo(it.readText()).toString()) + } catch (e: Exception) { + println("when processing ${it.path}") + throw e + } + } } @@ -91,7 +95,7 @@ class Property( } fun toStringWithSpacing(maxIDLength:Int): String { - val space = " ".repeat(maxIDLength - (jceID.toString().length)) + val space = " ".repeat((maxIDLength - (jceID.toString().length)).coerceAtLeast(0)) var base = " @SerialId(" + jceID + ") " + space + "val " + name + ":" + type + "" if(!isRequired){ if(defaultValue == null) { @@ -114,7 +118,7 @@ fun toJCEInfo(source:String):JCEInfo{ val info = JCEInfo() val allProperties = mutableMapOf<String,Property>() var inputStreamVariableRegix:String? = null - println(source) + // println(source) source.split("\n").forEach{ when{ it.contains("class") -> { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt index 27d55cf58..889564387 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -92,7 +92,7 @@ abstract class Bot : CoroutineScope { abstract val network: BotNetworkHandler /** - * 登录. + * 登录, 或重新登录. * * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login] * diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt index 5d5f072aa..4341880a9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.CompletableJob import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import net.mamoe.mirai.Bot +import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.io.PlatformDatagramChannel /** @@ -40,7 +41,10 @@ abstract class BotNetworkHandler : CoroutineScope { /** * 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回. * 本函数将挂起直到登录成功. + * + * 不要使用这个 API. 请使用 [Bot.login] */ + @MiraiInternalAPI abstract suspend fun login() /**