Commit 745cb748 authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents 7d1a6ef8 592d328e
......@@ -308,10 +308,10 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
input: JceInput
) : JceDecoder(input) {
override fun endStructure(desc: SerialDescriptor) {
while (input.peakHead().type != STRUCT_END) {
input.readHead()
while (input.input.canRead() && input.peakHeadOrNull()?.type != STRUCT_END) {
input.readHeadOrNull() ?: return
}
input.readHead()
input.readHeadOrNull()
}
}
......@@ -513,6 +513,9 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
@PublishedApi
internal fun peakHead(): JceHead = input.makeView().readHead() ?: error("no enough data to read head")
@PublishedApi
internal fun peakHeadOrNull(): JceHead? = input.makeView().readHead()
@Suppress("NOTHING_TO_INLINE") // 避免 stacktrace 出现两个 readHead
private inline fun IoBuffer.readHead(): JceHead? {
if (endOfInput) return null
......
......@@ -334,7 +334,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
val handler = PacketListener(commandName = commandName, sequenceId = sequenceId)
packetListeners.addLast(handler)
bot.logger.info("Send: ${this.commandName}")
var lastException: Exception? = null
repeat(retry + 1) {
try {
......@@ -343,18 +342,19 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
lastException = e
}
}
packetListeners.remove(handler)
throw lastException!!
}
private suspend inline fun <E : Packet> OutgoingPacket.doSendAndReceive(timeoutMillis: Long = 3000, handler: PacketListener): E {
channel.send(delegate)
bot.logger.info("Send: ${this.commandName}")
return withTimeoutOrNull(timeoutMillis) {
@Suppress("UNCHECKED_CAST")
handler.await() as E
// 不要 `withTimeout`. timeout 的异常会不知道去哪了.
} ?: net.mamoe.mirai.qqandroid.utils.inline {
packetListeners.remove(handler)
error("timeout when sending $commandName")
error("timeout when receiving response of $commandName")
}
}
......
......@@ -96,6 +96,8 @@ internal open class QQAndroidClient(
val apkVersionName: ByteArray = "8.2.0".toByteArray()
private val messageSequenceId: AtomicInt = atomic(0)
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndIncrement()
val appClientVersion: Int = 0
......
......@@ -25,8 +25,9 @@ internal data class RequestPushNotify(
@SerialId(13) val svrip: Int?
) : JceStruct, Packet
@Suppress("ArrayInDataClass")
@Serializable
internal class MsgInfo(
internal data class MsgInfo(
@SerialId(0) val lFromUin: Long = 0L,
@SerialId(1) val uMsgTime: Long = 0L,
@SerialId(2) val shMsgType: Short,
......@@ -51,7 +52,7 @@ internal class MsgInfo(
@Serializable
class ShareData(
internal class ShareData(
@SerialId(0) val pkgname: String = "",
@SerialId(1) val msgtail: String = "",
@SerialId(2) val picurl: String = "",
......@@ -59,13 +60,13 @@ class ShareData(
) : JceStruct
@Serializable
class TempMsgHead(
internal class TempMsgHead(
@SerialId(0) val c2c_type: Int? = 0,
@SerialId(1) val serviceType: Int? = 0
) : JceStruct
@Serializable
class CPicInfo(
internal class CPicInfo(
@SerialId(0) val vPath: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(1) val vHost: ByteArray? = EMPTY_BYTE_ARRAY
) : JceStruct
\ No newline at end of file
......@@ -8,7 +8,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
private val EMPTY_MAP = mapOf<String, String>()
@Serializable
class RequestPacket(
internal class RequestPacket(
@SerialId(1) val iVersion: Short = 3,
@SerialId(2) val cPacketType: Byte = 0,
@SerialId(3) val iMessageType: Int = 0,
......@@ -22,16 +22,16 @@ class RequestPacket(
) : JceStruct
@Serializable
class RequestDataVersion3(
internal class RequestDataVersion3(
@SerialId(0) val map: Map<String, ByteArray> // 注意: ByteArray 不能直接放序列化的 JceStruct!! 要放类似 RequestDataStructSvcReqRegister 的
) : JceStruct
@Serializable
class RequestDataVersion2(
internal class RequestDataVersion2(
@SerialId(0) val map: Map<String, Map<String, ByteArray>>
) : JceStruct
@Serializable
class RequestDataStructSvcReqRegister(
internal class RequestDataStructSvcReqRegister(
@SerialId(0) val struct: SvcReqRegister
) : JceStruct
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.data.jce
import kotlinx.serialization.Polymorphic
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct
@Serializable
class SvcReqRegister(
internal class SvcReqRegister(
@SerialId(0) val lUin: Long = 0L,
@SerialId(1) val lBid: Long = 0L,
@SerialId(2) val cConnType: Byte = 0,
......
......@@ -6,9 +6,9 @@ import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Serializable
class Vec0xd50 : ProtoBuf {
internal class Vec0xd50 : ProtoBuf {
@Serializable
class ExtSnsFrdData(
internal class ExtSnsFrdData(
@SerialId(1) val frdUin: Long = 0L,
@SerialId(91001) val musicSwitch: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(101001) val mutualmarkAlienation: ByteArray = EMPTY_BYTE_ARRAY,
......@@ -18,7 +18,7 @@ class Vec0xd50 : ProtoBuf {
) : ProtoBuf
@Serializable
class RspBody(
internal class RspBody(
@SerialId(1) val msgUpdateData: List<Vec0xd50.ExtSnsFrdData>? = null,
@SerialId(11) val over: Int = 0,
@SerialId(12) val nextStart: Int = 0,
......@@ -26,7 +26,7 @@ class Vec0xd50 : ProtoBuf {
) : ProtoBuf
@Serializable
class ReqBody(
internal class ReqBody(
@SerialId(1) val appid: Long = 0L,
@SerialId(2) val maxPkgSize: Int = 0,
@SerialId(3) val startTime: Int = 0,
......@@ -41,28 +41,28 @@ class Vec0xd50 : ProtoBuf {
) : ProtoBuf
@Serializable
class KSingRelationInfo(
internal class KSingRelationInfo(
@SerialId(1) val flag: Int = 0
) : ProtoBuf
}
@Serializable
class Vec0xd6b : ProtoBuf {
internal class Vec0xd6b : ProtoBuf {
@Serializable
class ReqBody(
internal class ReqBody(
@SerialId(1) val maxPkgSize: Int = 0,
@SerialId(2) val startTime: Int = 0,
@SerialId(11) val uinList: List<Long>? = null
) : ProtoBuf
@Serializable
class RspBody(
internal class RspBody(
@SerialId(11) val msgMutualmarkData: List<Vec0xd6b.MutualMarkData>? = null,
@SerialId(12) val uint64UnfinishedUins: List<Long>? = null
) : ProtoBuf
@Serializable
class MutualMarkData(
internal class MutualMarkData(
@SerialId(1) val frdUin: Long = 0L,
@SerialId(2) val result: Int = 0
// @SerialId(11) val mutualmarkInfo: List<Mutualmark.MutualMark>? = null
......@@ -70,9 +70,9 @@ class Vec0xd6b : ProtoBuf {
}
@Serializable
class Mutualmark : ProtoBuf {
internal class Mutualmark : ProtoBuf {
@Serializable
class MutualmarkInfo(
internal class MutualmarkInfo(
@SerialId(1) val lastActionTime: Long = 0L,
@SerialId(2) val level: Int = 0,
@SerialId(3) val lastChangeTime: Long = 0L,
......@@ -86,7 +86,7 @@ class Mutualmark : ProtoBuf {
) : ProtoBuf
@Serializable
class ResourceInfo17(
internal class ResourceInfo17(
@SerialId(1) val dynamicUrl: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(2) val staticUrl: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(3) val cartoonUrl: ByteArray = EMPTY_BYTE_ARRAY,
......
......@@ -2,8 +2,8 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.currentTimeSeconds
interface ImgReq : ProtoBuf
......
......@@ -9,16 +9,16 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
* msf.msgcomm.msg_comm
*/
@Serializable
class MsgComm : ProtoBuf {
internal class MsgComm : ProtoBuf {
@Serializable
class AppShareInfo(
internal class AppShareInfo(
@SerialId(1) val appshareId: Int = 0,
@SerialId(2) val appshareCookie: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(3) val appshareResource: PluginInfo? = null
) : ProtoBuf
@Serializable
class C2CTmpMsgHead(
internal class C2CTmpMsgHead(
@SerialId(1) val c2cType: Int = 0,
@SerialId(2) val serviceType: Int = 0,
@SerialId(3) val groupUin: Long = 0L,
......@@ -33,7 +33,7 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class ContentHead(
internal class ContentHead(
@SerialId(1) val pkgNum: Int = 0,
@SerialId(2) val pkgIndex: Int = 0,
@SerialId(3) val divSeq: Int = 0,
......@@ -41,7 +41,7 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class DiscussInfo(
internal class DiscussInfo(
@SerialId(1) val discussUin: Long = 0L,
@SerialId(2) val discussType: Int = 0,
@SerialId(3) val discussInfoSeq: Long = 0L,
......@@ -50,13 +50,13 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class ExtGroupKeyInfo(
internal class ExtGroupKeyInfo(
@SerialId(1) val curMaxSeq: Int = 0,
@SerialId(2) val curTime: Long = 0L
) : ProtoBuf
@Serializable
class GroupInfo(
internal class GroupInfo(
@SerialId(1) val groupCode: Long = 0L,
@SerialId(2) val groupType: Int = 0,
@SerialId(3) val groupInfoSeq: Long = 0L,
......@@ -68,7 +68,7 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class Msg(
internal class Msg(
@SerialId(1) val msgHead: MsgHead,
@SerialId(2) val contentHead: ContentHead? = null,
@SerialId(3) val msgBody: ImMsgBody.MsgBody,
......@@ -76,7 +76,7 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class MsgHead(
internal class MsgHead(
@SerialId(1) val fromUin: Long = 0L,
@SerialId(2) val toUin: Long = 0L,
@SerialId(3) val msgType: Int = 0,
......@@ -108,19 +108,19 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class MsgType0x210(
internal class MsgType0x210(
@SerialId(1) val subMsgType: Int = 0,
@SerialId(2) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class MutilTransHead(
internal class MutilTransHead(
@SerialId(1) val status: Int = 0,
@SerialId(2) val msgId: Int = 0
) : ProtoBuf
@Serializable
class PluginInfo(
internal class PluginInfo(
@SerialId(1) val resId: Int = 0,
@SerialId(2) val pkgName: String = "",
@SerialId(3) val newVer: Int = 0,
......@@ -135,13 +135,13 @@ class MsgComm : ProtoBuf {
) : ProtoBuf
@Serializable
class Uin2Nick(
internal class Uin2Nick(
@SerialId(1) val uin: Long = 0L,
@SerialId(2) val nick: String = ""
) : ProtoBuf
@Serializable
class UinPairMsg(
internal class UinPairMsg(
@SerialId(1) val lastReadTime: Int = 0,
@SerialId(2) val peerUin: Long = 0L,
@SerialId(3) val msgCompleted: Int = 0,
......
......@@ -6,9 +6,9 @@ import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Serializable
class MsgOnlinePush {
internal class MsgOnlinePush {
@Serializable
class PbPushMsg(
internal class PbPushMsg(
@SerialId(1) val msg: MsgComm.Msg,
@SerialId(2) val svrip: Int = 0,
@SerialId(3) val pushToken: ByteArray = EMPTY_BYTE_ARRAY,
......
......@@ -48,6 +48,7 @@ internal val EMPTY_BYTE_ARRAY = ByteArray(0)
*
* byte[] body encrypted by 16 zero
*/
@Deprecated("危险", level = DeprecationLevel.ERROR)
@UseExperimental(MiraiInternalAPI::class)
internal inline fun PacketFactory<*>.buildOutgoingPacket(
client: QQAndroidClient,
......@@ -102,7 +103,7 @@ internal inline fun PacketFactory<*>.buildOutgoingUniPacket(
writeStringUtf8(it)
}
encryptAndWrite(key) {
writeUniPacket(commandName, extraData) {
writeUniPacket(commandName, client.outgoingPacketUnknownValue, extraData) {
body(sequenceId)
}
}
......@@ -113,6 +114,7 @@ internal inline fun PacketFactory<*>.buildOutgoingUniPacket(
@UseExperimental(MiraiInternalAPI::class)
internal inline fun BytePacketBuilder.writeUniPacket(
commandName: String,
unknownData: ByteArray,
extraData: ByteReadPacket = BRP_STUB,
body: BytePacketBuilder.() -> Unit
) {
......@@ -123,7 +125,7 @@ internal inline fun BytePacketBuilder.writeUniPacket(
}
writeInt(4 + 4)
writeInt(45112203) // 02 B0 5B 8B
writeFully(unknownData) // 02 B0 5B 8B
if (extraData === BRP_STUB) {
writeInt(0x04)
......@@ -243,7 +245,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket(
}
writeInt(4 + 4)
writeInt(45112203) // 02 B0 5B 8B
writeFully(client.outgoingPacketUnknownValue) // 02 B0 5B 8B
client.device.imei.let {
writeInt(it.length + 4)
......
......@@ -25,8 +25,10 @@ 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 {
internal class MessageSvc {
/**
* 告知要刷新好友消息
*/
......@@ -119,8 +121,16 @@ class MessageSvc {
}
}
internal object PbSendMsg : PacketFactory<MsgSvc.PbSendMsgResp>("MessageSvc.PbSendMsg") {
object Response : Packet
internal object PbSendMsg : PacketFactory<PbSendMsg.Response>("MessageSvc.PbSendMsg") {
sealed class Response : Packet {
object SUCCESS : Response() {
override fun toString(): String = "MessageSvc.PbSendMsg.Response.SUCCESS"
}
data class Failed(val errorCode: Int, val errorMessage: String) : Response() {
override fun toString(): String = "MessageSvc.PbSendMsg.Response.FAILED(errorCode=$errorCode, errorMessage=$errorMessage"
}
}
/**
* 发送好友消息
......@@ -142,18 +152,53 @@ class MessageSvc {
richText = ImMsgBody.RichText(
elems = message.toRichTextElems()
)
),
msgSeq = client.atomicNextMessageSequenceId(),
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,
}
/**
* 发送群消息
*/
fun ToGroup(
client: QQAndroidClient,
groupId: Long,
message: MessageChain
): OutgoingPacket = buildOutgoingUniPacket(client) {
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 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 40 01".hexToBytes())
///return@buildOutgoingUniPacket
writeProtoBuf(
MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq(
routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupId)), // TODO: 2020/1/30 确认这里是 id 还是 internalId
contentHead = MsgComm.ContentHead(pkgNum = 1),
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = message.toRichTextElems()
)
),
msgSeq = client.atomicNextMessageSequenceId(),
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
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): MsgSvc.PbSendMsgResp {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
discardExact(4)
return readRemainingAsProtoBuf(MsgSvc.PbSendMsgResp.serializer())
val response = readRemainingAsProtoBuf(MsgSvc.PbSendMsgResp.serializer())
return if (response.result == 0) {
Response.SUCCESS
} else {
Response.Failed(response.errtype, response.errmsg)
}
}
}
}
......
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