mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 21:02:28 +08:00
C2C Message update
This commit is contained in:
parent
baee2d6366
commit
92c17de1be
@ -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)
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
)
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user