Commit ed99f8f8 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/protocol/packet/list/FriendListPacket.kt
parents 704e301c b3e9d553
...@@ -25,9 +25,11 @@ enum class JceCharset(val kotlinCharset: Charset) { ...@@ -25,9 +25,11 @@ enum class JceCharset(val kotlinCharset: Charset) {
UTF8(Charset.forName("UTF8")) UTF8(Charset.forName("UTF8"))
} }
internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<SerialId>(index)?.id 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 { class Jce private constructor(private val charset: JceCharset, context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
private inner class ListWriter( private inner class ListWriter(
...@@ -152,7 +154,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo ...@@ -152,7 +154,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
this.writeHead(STRUCT_END, 0) this.writeHead(STRUCT_END, 0)
} }
} else if (value is ProtoBuf) { } else if (value is ProtoBuf) {
this.encodeTaggedByteArray(popTag(), net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.dump(value)) this.encodeTaggedByteArray(popTag(), ProtoBufWithNullableSupport.dump(value))
} else { } else {
serializer.serialize(this, value) serializer.serialize(this, value)
} }
...@@ -417,7 +419,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo ...@@ -417,7 +419,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? { override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
println("decodeNullableSerializableValue: ${deserializer.getClassName()}") // println("decodeNullableSerializableValue: ${deserializer.getClassName()}")
if (deserializer is NullReader) { if (deserializer is NullReader) {
return null return null
} }
...@@ -444,7 +446,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo ...@@ -444,7 +446,7 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
else input.readByteArray(tag).toMutableList() as T else input.readByteArray(tag).toMutableList() as T
} }
val tag = popTag() val tag = popTag()
println(tag) // println(tag)
@Suppress("SENSELESS_COMPARISON") // false positive @Suppress("SENSELESS_COMPARISON") // false positive
if (input.skipToTagOrNull(tag) { if (input.skipToTagOrNull(tag) {
return deserializer.deserialize(JceListReader(input.readInt(0), input)) return deserializer.deserialize(JceListReader(input.readInt(0), input))
......
...@@ -6,16 +6,12 @@ package net.mamoe.mirai.qqandroid.io.serialization ...@@ -6,16 +6,12 @@ package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.* import kotlinx.io.*
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.CompositeDecoder.Companion.READ_DONE
import kotlinx.serialization.internal.* import kotlinx.serialization.internal.*
import kotlinx.serialization.modules.EmptyModule import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule import kotlinx.serialization.modules.SerialModule
import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumberType import kotlinx.serialization.protobuf.ProtoNumberType
import kotlinx.serialization.protobuf.ProtoType 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 import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport.Varint.encodeVarint
internal typealias ProtoDesc = Pair<Int, ProtoNumberType> internal typealias ProtoDesc = Pair<Int, ProtoNumberType>
...@@ -28,6 +24,12 @@ internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefa ...@@ -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 { class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : AbstractSerialFormat(context), BinaryFormat {
internal open inner class ProtobufWriter(val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() { internal open inner class ProtobufWriter(val encoder: ProtobufEncoder) : TaggedEncoder<ProtoDesc>() {
...@@ -171,178 +173,6 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac ...@@ -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: * Source for all varint operations:
* https://github.com/addthis/stream-lib/blob/master/src/main/java/com/clearspring/analytics/util/Varint.java * 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 ...@@ -381,57 +211,10 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
} }
return out 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 { companion object : BinaryFormat {
public override val context: SerialModule get() = plain.context 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))
}
private fun SerialDescriptor.getProtoDesc(index: Int): ProtoDesc { private fun SerialDescriptor.getProtoDesc(index: Int): ProtoDesc {
return extractParameters(this, index) return extractParameters(this, index)
...@@ -457,9 +240,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac ...@@ -457,9 +240,7 @@ class ProtoBufWithNullableSupport(context: SerialModule = EmptyModule) : Abstrac
} }
override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T { override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
val stream = ByteArrayInputStream(bytes) return ProtoBuf.load(deserializer, bytes)
val reader = ProtobufReader(ProtobufDecoder(stream))
return reader.decode(deserializer)
} }
} }
......
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")
}
}
...@@ -2,6 +2,7 @@ package net.mamoe.mirai.qqandroid.io.serialization ...@@ -2,6 +2,7 @@ package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.core.* import kotlinx.io.core.*
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.SerializationStrategy
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.ProtoBuf import net.mamoe.mirai.qqandroid.io.ProtoBuf
...@@ -92,3 +93,26 @@ fun <T : ProtoBuf> ByteArray.loadAs(deserializer: DeserializationStrategy<T>): T ...@@ -92,3 +93,26 @@ fun <T : ProtoBuf> ByteArray.loadAs(deserializer: DeserializationStrategy<T>): T
fun <T : ProtoBuf> Input.readRemainingAsProtoBuf(serializer: DeserializationStrategy<T>): T { fun <T : ProtoBuf> Input.readRemainingAsProtoBuf(serializer: DeserializationStrategy<T>): T {
return ProtoBufWithNullableSupport.load(serializer, this.readBytes()) return ProtoBufWithNullableSupport.load(serializer, this.readBytes())
} }
/**
* 构造 [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")
}
}
...@@ -18,7 +18,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.KnownPacketFactories ...@@ -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.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory 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.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
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.* import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket.LoginPacketResponse.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
...@@ -32,6 +31,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -32,6 +31,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
override val bot: QQAndroidBot by bot.unsafeWeakRef() override val bot: QQAndroidBot by bot.unsafeWeakRef()
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job]) 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 private lateinit var channel: PlatformSocket
override suspend fun login() { override suspend fun login() {
...@@ -88,19 +91,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -88,19 +91,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}") println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>() 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,7 +158,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -168,7 +158,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) { private suspend inline fun <P : Packet> generifiedParsePacket(input: Input) {
try {
KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int -> KnownPacketFactories.parseIncomingPacket(bot, input) { packetFactory: PacketFactory<P>, packet: P, commandName: String, sequenceId: Int ->
handlePacket(packetFactory, packet, commandName, sequenceId) handlePacket(packetFactory, packet, commandName, sequenceId)
if (packet is MultiPacket<*>) { if (packet is MultiPacket<*>) {
...@@ -177,10 +166,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -177,10 +166,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
} }
} }
} finally {
println()
println() // separate for debugging
}
} }
/** /**
...@@ -211,7 +196,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -211,7 +196,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (packet is Cancellable && packet.cancelled) return if (packet is Cancellable && packet.cancelled) return
} }
bot.logger.info(packet) bot.logger.info("Received packet: $packet")
packetFactory?.run { packetFactory?.run {
bot.handle(packet) bot.handle(packet)
...@@ -325,17 +310,19 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -325,17 +310,19 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
/** /**
* 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms) * 发送一个包, 并挂起直到接收到指定的返回包或超时(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) val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler) packetListeners.addLast(handler)
bot.logger.info("Send: ${this.commandName}") bot.logger.info("Send: ${this.commandName}")
channel.send(delegate) channel.send(delegate)
return withTimeoutOrNull(3000) { return withTimeoutOrNull(timoutMillis) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
handler.await() as E handler.await() as E
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
} ?: net.mamoe.mirai.qqandroid.utils.inline { } ?: net.mamoe.mirai.qqandroid.utils.inline {
packetListeners.remove(handler) packetListeners.remove(handler)
error("timeout when sending ${this.commandName}") error("timeout when sending ${commandName}")
} }
} }
...@@ -351,6 +338,4 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -351,6 +338,4 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
override suspend fun awaitDisconnection() = supervisor.join() override suspend fun awaitDisconnection() = supervisor.join()
override val coroutineContext: CoroutineContext = bot.coroutineContext
} }
\ No newline at end of file
...@@ -124,6 +124,7 @@ internal open class QQAndroidClient( ...@@ -124,6 +124,7 @@ internal open class QQAndroidClient(
@PublishedApi @PublishedApi
internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray() internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray()
var outgoingPacketUnknownValue: ByteArray = 0x02B05B8B.toByteArray()
var loginState = 0 var loginState = 0
var t150: Tlv? = null var t150: Tlv? = null
......
...@@ -3,8 +3,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.jce ...@@ -3,8 +3,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.SerialId import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct 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 @Serializable
internal class GetFriendListReq( internal class GetFriendListReq(
......
...@@ -23,7 +23,7 @@ class RequestPacket( ...@@ -23,7 +23,7 @@ class RequestPacket(
@Serializable @Serializable
class RequestDataVersion3( class RequestDataVersion3(
@SerialId(0) val map: Map<String, ByteArray> @SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
) : JceStruct ) : JceStruct
@Serializable @Serializable
......
...@@ -9,6 +9,7 @@ import net.mamoe.mirai.qqandroid.io.serialization.loadAs ...@@ -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.data.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc 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.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.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.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
...@@ -17,6 +18,7 @@ import net.mamoe.mirai.utils.cryptor.adjustToPublicKey ...@@ -17,6 +18,7 @@ import net.mamoe.mirai.utils.cryptor.adjustToPublicKey
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.unzip import net.mamoe.mirai.utils.unzip
import net.mamoe.mirai.utils.withSwitch
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
...@@ -53,7 +55,7 @@ internal val DECRYPTER_16_ZERO = ByteArray(16) ...@@ -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 internal typealias PacketConsumer<T> = suspend (packetFactory: PacketFactory<T>, packet: T, commandName: String, ssoSequenceId: Int) -> Unit
@PublishedApi @PublishedApi
internal val PacketLogger: MiraiLogger = DefaultLogger("Packet") internal val PacketLogger: MiraiLogger = DefaultLogger("Packet").withSwitch(false)
@UseExperimental(ExperimentalUnsignedTypes::class) @UseExperimental(ExperimentalUnsignedTypes::class)
internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
...@@ -63,7 +65,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -63,7 +65,8 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
MessageSvc.PushNotify, MessageSvc.PushNotify,
MessageSvc.PbGetMsg, MessageSvc.PbGetMsg,
MessageSvc.PushForceOffline, MessageSvc.PushForceOffline,
MessageSvc.PbSendMsg MessageSvc.PbSendMsg,
FriendList.GetFriendGroupList
) { ) {
// SvcReqMSFLoginNotify 自己的其他设备上限 // SvcReqMSFLoginNotify 自己的其他设备上限
// MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机 // MessageSvc.PushReaded 电脑阅读了别人的消息, 告知手机
...@@ -193,8 +196,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -193,8 +196,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}") PacketLogger.verbose("sso(inner)extraData = ${extraData.toUHexString()}")
commandName = readString(readInt() - 4) commandName = readString(readInt() - 4)
val unknown = readBytes(readInt() - 4) bot.client.outgoingPacketUnknownValue = readBytes(readInt() - 4)
if (unknown.toInt() != 0x02B05B8B) DebugLogger.debug("got new unknown: ${unknown.toUHexString()}")
dataCompressed = readInt() dataCompressed = readInt()
} }
...@@ -213,7 +215,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -213,7 +215,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
// body // body
val packetFactory = findPacketFactory(commandName) val packetFactory = findPacketFactory(commandName)
bot.logger.info("Received: $commandName") bot.logger.debug("Received commandName: $commandName")
return IncomingPacket(packetFactory, ssoSequenceId, packet) return IncomingPacket(packetFactory, ssoSequenceId, packet)
} }
......
...@@ -2,7 +2,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive ...@@ -2,7 +2,6 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.writeFully
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.message.FriendMessage import net.mamoe.mirai.message.FriendMessage
...@@ -26,8 +25,6 @@ import net.mamoe.mirai.qqandroid.utils.toRichTextElems ...@@ -26,8 +25,6 @@ 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.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.toReadPacket import net.mamoe.mirai.utils.io.toReadPacket
import kotlin.math.absoluteValue
import kotlin.random.Random
class MessageSvc { class MessageSvc {
/** /**
...@@ -145,11 +142,11 @@ class MessageSvc { ...@@ -145,11 +142,11 @@ class MessageSvc {
richText = ImMsgBody.RichText( richText = ImMsgBody.RichText(
elems = message.toRichTextElems() elems = message.toRichTextElems()
) )
), )
msgSeq = 17041, // msgSeq = 17041,
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() } ?: "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 // msgVia = 1
) )
) )
} }
......
...@@ -3,34 +3,34 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.list ...@@ -3,34 +3,34 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.list
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot 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.toByteArray
import net.mamoe.mirai.qqandroid.io.serialization.writeJceStruct 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.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetFriendListReq 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.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.Vec0xd50
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Vec0xd6b import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.*
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket 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.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.debugPrint
import net.mamoe.mirai.utils.io.debugPrintln
internal object FriendListPacket : internal class FriendList {
PacketFactory<FriendListPacket.GetFriendListResponse>("friendlist.getFriendGroupList") {
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): 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
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GetFriendListResponse {
println("aaaa") println("aaaa")
return GetFriendListResponse() this.debugPrint()
return Response()
} }
operator fun invoke( operator fun invoke(
...@@ -40,7 +40,7 @@ internal object FriendListPacket : ...@@ -40,7 +40,7 @@ internal object FriendListPacket :
groupListStartIndex: Int, groupListStartIndex: Int,
groupListCount: Int groupListCount: Int
): OutgoingPacket { ): OutgoingPacket {
return buildOutgoingUniPacket(client, key = client.wLoginSigInfo.d2Key) { return buildOutgoingUniPacket(client, bodyType = 1, key = client.wLoginSigInfo.d2Key) {
writeJceStruct( writeJceStruct(
RequestPacket.serializer(), RequestPacket.serializer(),
RequestPacket( RequestPacket(
...@@ -50,9 +50,10 @@ internal object FriendListPacket : ...@@ -50,9 +50,10 @@ internal object FriendListPacket :
cPacketType = 0x003, cPacketType = 0x003,
iMessageType = 0x00000, iMessageType = 0x00000,
iRequestId = 1921334514, iRequestId = 1921334514,
sBuffer = RequestDataVersion3( sBuffer = jceRequestSBuffer(
mapOf( "FL",
"FL" to GetFriendListReq( GetFriendListReq.serializer(),
GetFriendListReq(
reqtype = 3, reqtype = 3,
ifReflush = if (friendListStartIndex <= 0) { ifReflush = if (friendListStartIndex <= 0) {
0 0
...@@ -86,14 +87,11 @@ internal object FriendListPacket : ...@@ -86,14 +87,11 @@ internal object FriendListPacket :
reqMutualmarkAlienation = 1 reqMutualmarkAlienation = 1
).toByteArray(Vec0xd50.ReqBody.serializer()), ).toByteArray(Vec0xd50.ReqBody.serializer()),
vecSnsTypelist = listOf(13580L, 13581L, 13582L) vecSnsTypelist = listOf(13580L, 13581L, 13582L)
).toByteArray(GetFriendListReq.serializer())
) )
).toByteArray(RequestDataVersion3.serializer())
) )
) )
this.build().debugPrint() )
}
} }
} }
} }
\ No newline at end of file
...@@ -4,11 +4,9 @@ import kotlinx.io.core.ByteReadPacket ...@@ -4,11 +4,9 @@ import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport 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.io.serialization.writeJceStruct
import net.mamoe.mirai.qqandroid.network.QQAndroidClient 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.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister import net.mamoe.mirai.qqandroid.network.protocol.data.jce.SvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
...@@ -61,9 +59,9 @@ class StatSvc { ...@@ -61,9 +59,9 @@ class StatSvc {
RequestPacket( RequestPacket(
sServantName = "PushService", sServantName = "PushService",
sFuncName = "SvcReqRegister", sFuncName = "SvcReqRegister",
sBuffer = RequestDataVersion3( sBuffer = jceRequestSBuffer(
mapOf( "SvcReqRegister",
"SvcReqRegister" to RequestDataStructSvcReqRegister( SvcReqRegister.serializer(),
SvcReqRegister( SvcReqRegister(
cConnType = 0, cConnType = 0,
lBid = 1 or 2 or 4, lBid = 1 or 2 or 4,
...@@ -119,9 +117,7 @@ class StatSvc { ...@@ -119,9 +117,7 @@ class StatSvc {
), ),
bSetMute = 0 bSetMute = 0
) )
).toByteArray(RequestDataStructSvcReqRegister.serializer())
) )
).toByteArray(RequestDataVersion3.serializer())
) )
) )
} }
......
...@@ -219,7 +219,7 @@ private fun parseSsoFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP ...@@ -219,7 +219,7 @@ private fun parseSsoFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP
commandName = readString(readInt() - 4) commandName = readString(readInt() - 4)
DebugLogger.warning("commandName=$commandName") DebugLogger.warning("commandName=$commandName")
val unknown = readBytes(readInt() - 4) 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) check(readInt() == 0)
} }
...@@ -261,7 +261,7 @@ private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP ...@@ -261,7 +261,7 @@ private fun parseUniFrame(input: ByteReadPacket): KnownPacketFactories.IncomingP
commandName = readString(readInt() - 4) commandName = readString(readInt() - 4)
DebugLogger.warning("commandName=$commandName") DebugLogger.warning("commandName=$commandName")
val unknown = readBytes(readInt() - 4) 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) check(readInt() == 0)
} }
......
...@@ -3,19 +3,23 @@ package test; ...@@ -3,19 +3,23 @@ package test;
import java.io.File import java.io.File
fun main(){ fun main(){
val var9 = toJCEInfo(
File(
"""
E:\Projects\QQAndroidFF\app\src\main\java\PushNotifyPack\RequestPushForceOffline.java
""".trimIndent()
).readText()
)
println( println(
"import kotlinx.serialization.SerialId\n" + "import kotlinx.serialization.SerialId\n" +
"import kotlinx.serialization.Serializable\n" + "import kotlinx.serialization.Serializable\n" +
"import net.mamoe.mirai.qqandroid.io.JceStruct\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( ...@@ -91,7 +95,7 @@ class Property(
} }
fun toStringWithSpacing(maxIDLength:Int): String { 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 + "" var base = " @SerialId(" + jceID + ") " + space + "val " + name + ":" + type + ""
if(!isRequired){ if(!isRequired){
if(defaultValue == null) { if(defaultValue == null) {
...@@ -114,7 +118,7 @@ fun toJCEInfo(source:String):JCEInfo{ ...@@ -114,7 +118,7 @@ fun toJCEInfo(source:String):JCEInfo{
val info = JCEInfo() val info = JCEInfo()
val allProperties = mutableMapOf<String,Property>() val allProperties = mutableMapOf<String,Property>()
var inputStreamVariableRegix:String? = null var inputStreamVariableRegix:String? = null
println(source) // println(source)
source.split("\n").forEach{ source.split("\n").forEach{
when{ when{
it.contains("class") -> { it.contains("class") -> {
......
...@@ -92,7 +92,7 @@ abstract class Bot : CoroutineScope { ...@@ -92,7 +92,7 @@ abstract class Bot : CoroutineScope {
abstract val network: BotNetworkHandler abstract val network: BotNetworkHandler
/** /**
* 登录. * 登录, 或重新登录.
* *
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login] * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
* *
......
...@@ -7,6 +7,7 @@ import kotlinx.coroutines.CompletableJob ...@@ -7,6 +7,7 @@ import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.PlatformDatagramChannel import net.mamoe.mirai.utils.io.PlatformDatagramChannel
/** /**
...@@ -40,7 +41,10 @@ abstract class BotNetworkHandler : CoroutineScope { ...@@ -40,7 +41,10 @@ abstract class BotNetworkHandler : CoroutineScope {
/** /**
* 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回. * 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回.
* 本函数将挂起直到登录成功. * 本函数将挂起直到登录成功.
*
* 不要使用这个 API. 请使用 [Bot.login]
*/ */
@MiraiInternalAPI
abstract suspend fun login() abstract suspend fun login()
/** /**
......
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