C2C Message update

This commit is contained in:
Him188 2020-01-27 19:05:18 +08:00
parent baee2d6366
commit 92c17de1be
4 changed files with 296 additions and 82 deletions

View File

@ -3,6 +3,8 @@ package net.mamoe.mirai.qqandroid.network.io
import kotlinx.io.charsets.Charset
import kotlinx.io.core.*
import kotlinx.io.pool.ObjectPool
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket
import net.mamoe.mirai.utils.io.DebugLogger
import net.mamoe.mirai.utils.io.readIoBuffer
import net.mamoe.mirai.utils.io.readString
import net.mamoe.mirai.utils.io.toIoBuffer
@ -19,9 +21,76 @@ inline class JceHead(private val value: Long) {
}
}
fun ByteArray.asJceInput(charset: Charset = CharsetGBK): JceInput = JceInput(this.toIoBuffer(), charset)
fun <J : JceStruct> ByteArray.readJceStruct(factory: JceStruct.Factory<J>, tag: Int = 0, charset: Charset = CharsetUTF8): J {
this.asJceInput(charset).use {
return it.readJceStruct(factory, tag)
}
}
fun ByteReadPacket.asJceInput(charset: Charset = CharsetGBK): JceInput = JceInput(this.readIoBuffer(), charset)
fun <J : JceStruct> ByteReadPacket.readJceStruct(factory: JceStruct.Factory<J>, tag: Int = 0, charset: Charset = CharsetUTF8): J {
this.asJceInput(charset).use {
return it.readJceStruct(factory, tag)
}
}
fun ByteArray.asJceInput(charset: Charset = CharsetUTF8): JceInput = JceInput(this.toIoBuffer(), charset)
fun <J : JceStruct> ByteReadPacket.readJceRequestBufferMapVersion2ToJceStruct(factory: JceStruct.Factory<J>, charset: Charset = CharsetUTF8): J {
this.use {
val bytes =
readJceRequestBufferMapVersion2(charset).values.also { if (it.size != 1) DebugLogger.debug("读取 jce RequestPacket 时发现多个包在 map 中") }.firstOrNull()
?: error("empty request map")
return bytes.readJceStruct(factory, 0)
}
}
fun <J : JceStruct> ByteReadPacket.readJceRequestBufferMapVersion3ToJceStruct(factory: JceStruct.Factory<J>, charset: Charset = CharsetUTF8): J {
this.use {
val bytes = readJceRequestBufferMapVersion3(charset).values.firstOrNull() ?: error("empty request map")
return bytes.readJceStruct(factory, 0, charset)
}
}
fun ByteReadPacket.readJceRequestBufferMapVersion2(charset: Charset = CharsetUTF8): Map<String, ByteArray> {
this.use {
discardExact(8)
val request = this.asJceInput(charset).use { RequestPacket.newInstanceFrom(it) }
val map = request.sBuffer.asJceInput(charset).withUse {
readNestedMap<String, String, ByteArray>(0)
}
return map.mapValues { it.value.values.first() }
}
}
fun ByteReadPacket.readJceRequestBufferMapVersion3(charset: Charset = CharsetUTF8): Map<String, ByteArray> {
this.use {
discardExact(8)
val request = this.asJceInput(charset).use { RequestPacket.newInstanceFrom(it) }
return request.sBuffer.asJceInput(charset).withUse { readMap(0) }
}
}
fun ByteReadPacket.asJceInput(charset: Charset = CharsetUTF8): JceInput = JceInput(this.readIoBuffer(), charset)
inline fun <R> IoBuffer.useIoBuffer(block: IoBuffer.() -> R): R {
return try {
block(this)
} catch (first: Throwable) {
throw first
} finally {
release(IoBuffer.Pool)
}
}
inline fun <C : Closeable, R> C.withUse(block: C.() -> R): R {
return try {
block(this)
} catch (first: Throwable) {
throw first
} finally {
close()
}
}
@Suppress("MemberVisibilityCanBePrivate")
@UseExperimental(ExperimentalUnsignedTypes::class)
@ -87,7 +156,16 @@ class JceInput(
fun readIntArray(tag: Int): IntArray = readIntArrayOrNull(tag) ?: error("cannot find tag $tag")
fun readBooleanArray(tag: Int): BooleanArray = readBooleanArrayOrNull(tag) ?: error("cannot find tag $tag")
fun <K, V> readMap(defaultKey: K, defaultValue: V, tag: Int): Map<K, V> = readMapOrNull(defaultKey, defaultValue, tag) ?: error("cannot find tag $tag")
inline fun <reified K, reified V> readMap(tag: Int): Map<K, V> = readMapOrNull(tag) ?: error("cannot find tag $tag")
inline fun <reified K, reified InnerK, reified InnerV> readNestedMap(tag: Int): Map<K, Map<InnerK, InnerV>> =
readNestedMapOrNull(tag) ?: error("cannot find tag $tag")
inline fun <reified J : JceStruct> readStringToJceStructMap(factory: JceStruct.Factory<J>, tag: Int): Map<String, J> =
readStringToJceStructMapOrNull(factory, tag) ?: error("cannot find tag $tag")
fun <T> readList(defaultElement: T, tag: Int): List<T> = readListOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
inline fun <reified J: JceStruct> readJceStructList(factory: JceStruct.Factory<J>, tag: Int): List<J> = readJceStructListOrNull( factory, tag) ?: error("cannot find tag $tag")
inline fun <reified T> readList(tag: Int): List<T> = readListOrNull( tag) ?: error("cannot find tag $tag")
inline fun <reified T> readSimpleArray(defaultElement: T, tag: Int): Array<T> = readArrayOrNull(defaultElement, tag) ?: error("cannot find tag $tag")
fun <J : JceStruct> readJceStruct(factory: JceStruct.Factory<J>, tag: Int): J = readJceStructOrNull(factory, tag) ?: error("cannot find tag $tag")
fun readStringArray(tag: Int): Array<String> = readArrayOrNull("", tag) ?: error("cannot find tag $tag")
@ -217,6 +295,17 @@ class JceInput(
}
}
inline fun <reified J : JceStruct> readStringToJceStructMapOrNull(factory: JceStruct.Factory<J>, tag: Int): Map<String, J>? = skipToTagOrNull(tag) {
check(it.type.toInt() == 8) { "type mismatch: ${it.type}" }
val size = readInt(0)
val map = HashMap<String, J>(size)
repeat(size) {
map[readString(0)] = readJceStruct(factory, 1)
}
return map
}
fun <K, V> readMapOrNull(defaultKey: K, defaultValue: V, tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
check(it.type.toInt() == 8) { "type mismatch: ${it.type}" }
val size = readInt(0)
@ -227,6 +316,16 @@ class JceInput(
return map
}
inline fun <reified K, reified InnerK, reified InnerV> readNestedMapOrNull(tag: Int): Map<K, Map<InnerK, InnerV>>? = skipToTagOrNull(tag) {
check(it.type.toInt() == 8) { "type mismatch" }
val size = readInt(0)
val map = HashMap<K, Map<InnerK, InnerV>>(size)
repeat(size) {
map[readSimpleObject(0)] = readMap(1)
}
return map
}
inline fun <reified K, reified V> readMapOrNull(tag: Int): Map<K, V>? = skipToTagOrNull(tag) {
check(it.type.toInt() == 8) { "type mismatch" }
val size = readInt(0)
@ -237,6 +336,26 @@ class JceInput(
return map
}
inline fun <reified J : JceStruct> readJceStructListOrNull(factory: JceStruct.Factory<J>, tag: Int): List<J>? = skipToTagOrNull(tag) { head ->
check(head.type.toInt() == 9) { "type mismatch" }
val size = readInt(0)
val list = ArrayList<J>(size)
repeat(size) {
list.add(readJceStruct(factory, 0))
}
return list
}
inline fun <reified T> readListOrNull(tag: Int): List<T>? = skipToTagOrNull(tag) { head ->
check(head.type.toInt() == 9) { "type mismatch" }
val size = readInt(0)
val list = ArrayList<T>(size)
repeat(size) {
list.add(readSimpleObject(0))
}
return list
}
fun <T> readListOrNull(defaultElement: T, tag: Int): List<T>? = skipToTagOrNull(tag) { head ->
check(head.type.toInt() == 9) { "type mismatch" }
val size = readInt(0)

View File

@ -6,8 +6,8 @@ import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.protocol.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.chat.receive.PushNotify
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
@ -53,7 +53,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
LoginPacket,
StatSvc.Register,
OnlinePush.PbPushGroupMsg,
PushNotify
MessageSvc.PushNotify
) {
fun findPacketFactory(commandName: String): PacketFactory<*>? = this.firstOrNull { it.commandName == commandName }

View File

@ -0,0 +1,164 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.data
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
class RequestPushNotify(
val uin: Long,
val ctype: Byte,
val strService: String,
val strCmd: String,
val vNotifyCookie: ByteArray,
val usMsgType: Int,
val wUserActive: Int,
val wGeneralFlag: Int,
val bindedUin: Long,
val stMsgInfo: MsgInfo,
val msgCtrlBuf: String,
val serverBuf: ByteArray,
val pingFlag: Long,
val svrip: Int
) : Packet, JceStruct() {
override fun writeTo(builder: JceOutput) {
//not needed
}
companion object : Factory<RequestPushNotify> {
override fun newInstanceFrom(input: JceInput): RequestPushNotify {
return RequestPushNotify(
input.read(0L, 0),
input.read(0.toByte(), 1),
input.readString(2),
input.readString(3),
input.read(EMPTY_BYTE_ARRAY, 4),
input.read(0, 5),
input.read(0, 6),
input.read(0, 7),
input.read(0L, 8),
input.readJceStruct(MsgInfo, 9),
input.readString(10),
input.readByteArray(11),
input.readLong(12),
input.readInt(13)
)
}
}
}
class MsgInfo(
val lFromUin: Long,
val uMsgTime: Long,
val shMsgType: Short,
val shMsgSeq: Short,
val strMsg: String,
val uRealMsgTime: Int,
val vMsg: ByteArray,
val uAppShareID: Long,
val vMsgCookies: ByteArray,
val vAppShareCookie: ByteArray,
val lMsgUid: Long,
val lLastChangeTime: Long,
val vCPicInfo: List<CPicInfo>,
val stShareData: ShareData,
val lFromInstId: Long,
val vRemarkOfSender: ByteArray,
val strFromMobile: String,
val strFromName: String,
val vNickName: List<String>,
val stC2CTmpMsgHead: TempMsgHead?
) : JceStruct() {
companion object : Factory<MsgInfo> {
override fun newInstanceFrom(input: JceInput): MsgInfo = with(input) {
return MsgInfo(
readLong(0),
readLong(1),
readShort(2),
readShort(3),
readString(4),
readInt(5),
readByteArray(6),
readLong(7),
readByteArray(8),
readByteArray(9),
readLong(10),
readLong(11),
readJceStructList(CPicInfo, 12),
readJceStruct(ShareData, 13),
readLong(14),
readByteArray(15),
readString(16),
readString(17),
readList(18),
readJceStructOrNull(TempMsgHead, 19)
)
}
}
override fun writeTo(builder: JceOutput) {
// not needed
}
}
class ShareData(
val pkgname: String = "",
val msgtail: String = "",
val picurl: String = "",
val url: String = ""
) : JceStruct() {
companion object : Factory<ShareData> {
override fun newInstanceFrom(input: JceInput): ShareData {
return ShareData(
input.readString(0),
input.readString(1),
input.readString(2),
input.readString(3)
)
}
}
override fun writeTo(builder: JceOutput) {
// not needed
}
}
class TempMsgHead(
val c2c_type: Int,
val serviceType: Int
) : JceStruct() {
override fun writeTo(builder: JceOutput) {
}
companion object : Factory<TempMsgHead> {
override fun newInstanceFrom(input: JceInput): TempMsgHead {
return TempMsgHead(
input.readInt(0),
input.readInt(1)
)
}
}
}
class CPicInfo(
val vPath: ByteArray,
val vHost: ByteArray?
) : JceStruct() {
override fun writeTo(builder: JceOutput) {
}
companion object : Factory<CPicInfo> {
override fun newInstanceFrom(input: JceInput): CPicInfo {
return CPicInfo(
input.readByteArray(0),
input.readByteArray(1)
)
}
}
}

View File

@ -1,88 +1,19 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.io.readJceRequestBufferMapVersion2ToJceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.io.readIoBuffer
import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.data.RequestPushNotify
import net.mamoe.mirai.utils.cryptor.contentToString
internal object PushNotify : PacketFactory<PushNotify.MessageNotification>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): MessageNotification {
return MessageNotification.newInstanceFrom(JceInput(this.apply { discardExact(4) }.readIoBuffer()))
}
class MessageNotification(
val luni: Long,
val ctype: Byte,
val strService: String,
val strCmd: String,
val vNotifyCookie: ByteArray,
val usMsgType: Int,
val wUserActive: Int,
val wGeneralFlag: Int,
val lBindedUni: Long
) : Packet, JceStruct() {
init {
println(this.luni)
println(this.ctype)
println(this.strService)
println(this.strCmd)
println(this.vNotifyCookie.toUHexString())
println(this.usMsgType)
println(this.wUserActive)
println(this.wGeneralFlag)
println(this.lBindedUni)
}
override fun writeTo(builder: JceOutput) {
//not needed
}
companion object : Factory<MessageNotification> {
override fun newInstanceFrom(input: JceInput): MessageNotification {
return MessageNotification(
input.read(0L, 0),
input.read(0.toByte(), 1),
input.readString(2),
input.readString(3),
input.read(EMPTY_BYTE_ARRAY, 4),
input.read(0, 5),
input.read(0, 6),
input.read(0, 7),
input.read(0L, 8)
)
}
class MessageSvc {
internal object PushNotify : PacketFactory<RequestPushNotify>("MessageSvc.PushNotify") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): RequestPushNotify {
val messageNotification = readJceRequestBufferMapVersion2ToJceStruct(RequestPushNotify)
println(messageNotification.contentToString())
TODO()
}
}
class MsgInfo(
val lFromUin: Long,
val uMsgTime: Long,
val shMsgType: Short,
val shMsgSeq: Short,
val strMsg: String,
val uRealMsgTime: Int,
val vMsg: ByteArray,
val uAppShareID: Long,
val vMsgCookies: ByteArray,
val vAppShareCookie: ByteArray,
val lMsgUid: Long,
val lLastChangeTime: Long,
//val vCPicInfo: List<CPicInfo?>,
//val stShareData: shareData,
val lFromInstId: Long,
val vRemarkOfSender: ByteArray,
val strFromMobile: String,
val strFromName: String,
val vNickName: List<String>
//val stC2CTmpMsgHead: TempMsgHead
)
}