From e7bff905361b3cc000258900dd7f966f0f424a1f Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 7 Feb 2020 20:35:10 +0800 Subject: [PATCH 1/2] Add OnlinePush.ReqPush --- .../protocol/data/jce/OnlinePushPack.kt | 87 +++++++++++++++++++ .../packet/chat/receive/OnlinePush.kt | 22 +++++ 2 files changed, 109 insertions(+) create mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt new file mode 100644 index 000000000..770eb0bb6 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt @@ -0,0 +1,87 @@ +package net.mamoe.mirai.qqandroid.network.protocol.data.jce + +import kotlinx.serialization.SerialId +import kotlinx.serialization.Serializable +import net.mamoe.mirai.qqandroid.io.JceStruct + +class OnlinePushPack { + @Serializable + internal class DelMsgInfo( + @SerialId(0) val fromUin: Long, + @SerialId(1) val uMsgTime: Long, + @SerialId(2) val shMsgSeq: Short, + @SerialId(3) val vMsgCookies: ByteArray? = null, + @SerialId(4) val wCmd: Short? = null, + @SerialId(5) val uMsgType: Long? = null, + @SerialId(6) val uAppId: Long? = null, + @SerialId(7) val sendTime: Long? = null, + @SerialId(8) val ssoSeq: Int? = null, + @SerialId(9) val ssoIp: Int? = null, + @SerialId(10) val clientIp: Int? = null + ) : JceStruct + + @Serializable + internal class DeviceInfo( + @SerialId(0) val netType: Byte? = null, + @SerialId(1) val devType: String? = "", + @SerialId(2) val oSVer: String? = "", + @SerialId(3) val vendorName: String? = "", + @SerialId(4) val vendorOSName: String? = "", + @SerialId(5) val iOSIdfa: String? = "" + ) : JceStruct + + @Serializable + internal class Name( + @SerialId(0) val fromUin: Long, + @SerialId(1) val uMsgTime: Long, + @SerialId(2) val shMsgType: Short, + @SerialId(3) val shMsgSeq: Short, + @SerialId(4) val msg: String = "", + @SerialId(5) val uRealMsgTime: Int? = null, + @SerialId(6) val vMsg: ByteArray? = null, + @SerialId(7) val uAppShareID: Long? = null, + @SerialId(8) val vMsgCookies: ByteArray? = null, + @SerialId(9) val vAppShareCookie: ByteArray? = null, + @SerialId(10) val msgUid: Long? = null, + @SerialId(11) val lastChangeTime: Long? = 1L, + @SerialId(12) val vCPicInfo: List? = null, + @SerialId(13) val stShareData: ShareData? = null, + @SerialId(14) val fromInstId: Long? = null, + @SerialId(15) val vRemarkOfSender: ByteArray? = null, + @SerialId(16) val fromMobile: String? = "", + @SerialId(17) val fromName: String? = "", + @SerialId(18) val vNickName: List? = null, + @SerialId(19) val stC2CTmpMsgHead: TempMsgHead? = null + ) : JceStruct + + @Serializable + internal class SvcReqPushMsg( + @SerialId(0) val uin: Long, + @SerialId(1) val uMsgTime: Long, + @SerialId(2) val vMsgInfos: List, + @SerialId(3) val svrip: Int? = 0, + @SerialId(4) val vSyncCookie: ByteArray? = null, + @SerialId(5) val vUinPairMsg: List? = null, + @SerialId(6) val mPreviews: Map? = null, + @SerialId(7) val wUserActive: Int? = null, + @SerialId(12) val wGeneralFlag: Int? = null + ) : JceStruct + + @Serializable + internal class SvcRespPushMsg( + @SerialId(0) val uin: Long, + @SerialId(1) val vDelInfos: List, + @SerialId(2) val svrip: Int, + @SerialId(3) val pushToken: ByteArray? = null, + @SerialId(4) val serviceType: Int? = null, + @SerialId(5) val deviceInfo: DeviceInfo? = null + ) : JceStruct + + @Serializable + internal class UinPairMsg( + @SerialId(1) val uLastReadTime: Long? = null, + @SerialId(2) val peerUin: Long? = null, + @SerialId(3) val uMsgCompleted: Long? = null, + @SerialId(4) val vMsgInfos: List? = null + ) : JceStruct +} \ No newline at end of file diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt index 68f0cc9e6..cfd3fb275 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt @@ -4,16 +4,21 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.contact.MemberPermission +import net.mamoe.mirai.data.NoPakcet import net.mamoe.mirai.data.Packet import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.MsgInfo +import net.mamoe.mirai.qqandroid.network.protocol.data.jce.OnlinePushPack import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.utils.toMessageChain +import net.mamoe.mirai.utils.cryptor.contentToString internal inline class GroupMessageOrNull(val delegate: GroupMessage?) : Packet { override fun toString(): String { @@ -70,4 +75,21 @@ internal class OnlinePush { return null } } + + internal object ReqPush : IncomingPacketFactory("OnlinePush.ReqPush") { + @UseExperimental(ExperimentalStdlibApi::class) + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet { + val reqPushMsg = decodeUniPacket(OnlinePushPack.SvcReqPushMsg.serializer(), "req") + reqPushMsg.vMsgInfos.forEach { msgInfo: MsgInfo -> + + } + println(reqPushMsg.contentToString()) + return NoPakcet + } + + + override suspend fun QQAndroidBot.handle(packet: Packet, sequenceId: Int): OutgoingPacket? { + return null + } + } } \ No newline at end of file From a66c71dd8de73dd9271d6e44f809971e347f318b Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 7 Feb 2020 21:09:35 +0800 Subject: [PATCH 2/2] Friend image upload done --- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 23 +++---- .../mirai/qqandroid/network/highway/Codec.kt | 57 ------------------ .../network/highway/HighwayHelper.kt | 60 ++++++++++++++++++- 3 files changed, 71 insertions(+), 69 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 1311f453b..5e0225119 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -10,6 +10,7 @@ import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.NotOnlineImageFromFile import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper +import net.mamoe.mirai.qqandroid.network.highway.postImage import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore @@ -50,7 +51,6 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin ) { "send message failed" } } } - override suspend fun uploadImage(image: ExternalImage): Image = try { bot.network.run { val response = LongConn.OffPicUp( @@ -78,16 +78,17 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin resourceId = response.resourceId ) is LongConn.OffPicUp.Response.RequireUpload -> { - HighwayHelper.uploadImage( - client = bot.client, - serverIp = response.serverIp[0].toIpV4AddressString(), - serverPort = response.serverPort[0], - imageInput = image.input, - inputSize = image.inputSize.toInt(), - md5 = image.md5, - uKey = response.uKey, - commandId = 1 - ) + Http.postImage("0x6ff0070", bot.uin, null, imageInput = image.input, inputSize = image.inputSize, uKeyHex = response.uKey.toUHexString("")) +// HighwayHelper.uploadImage( +// client = bot.client, +// serverIp = response.serverIp[0].toIpV4AddressString(), +// serverPort = response.serverPort[0], +// imageInput = image.input, +// inputSize = image.inputSize.toInt(), +// md5 = image.md5, +// uKey = response.uKey, +// commandId = 1 +// ) return NotOnlineImageFromFile( filepath = response.resourceId, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt index 2e0649d07..a569afa65 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/Codec.kt @@ -1,66 +1,9 @@ package net.mamoe.mirai.qqandroid.network.highway -import io.ktor.client.HttpClient -import io.ktor.client.request.post -import io.ktor.http.ContentType -import io.ktor.http.HttpStatusCode -import io.ktor.http.URLProtocol -import io.ktor.http.content.OutgoingContent -import io.ktor.http.userAgent -import kotlinx.coroutines.io.ByteWriteChannel import kotlinx.io.core.* -import kotlinx.io.pool.useInstance import net.mamoe.mirai.qqandroid.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY -import net.mamoe.mirai.utils.io.ByteArrayPool - -@Suppress("SpellCheckingInspection") -internal suspend inline fun HttpClient.postImage( - htcmd: String, - uin: Long, - groupcode: Long?, - imageInput: Input, - inputSize: Long, - uKeyHex: String -): Boolean = try { - post { - url { - protocol = URLProtocol.HTTP - host = "htdata2.qq.com" - path("cgi-bin/httpconn") - - parameters["htcmd"] = htcmd - parameters["uin"] = uin.toString() - - if (groupcode != null) parameters["groupcode"] = groupcode.toString() - - parameters["term"] = "pc" - parameters["ver"] = "5603" - parameters["filesize"] = inputSize.toString() - parameters["range"] = 0.toString() - parameters["ukey"] = uKeyHex - - userAgent("QQClient") - } - - body = object : OutgoingContent.WriteChannelContent() { - override val contentType: ContentType = ContentType.Image.Any - override val contentLength: Long = inputSize - - override suspend fun writeTo(channel: ByteWriteChannel) { - ByteArrayPool.useInstance { buffer: ByteArray -> - var size: Int - while (imageInput.readAvailable(buffer).also { size = it } != 0) { - channel.writeFully(buffer, 0, size) - } - } - } - } - } == HttpStatusCode.OK -} finally { - imageInput.close() -} object Highway { fun RequestDataTrans( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt index bcd7fe054..0977c85c7 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt @@ -1,18 +1,76 @@ package net.mamoe.mirai.qqandroid.network.highway +import io.ktor.client.HttpClient +import io.ktor.client.request.post +import io.ktor.http.ContentType +import io.ktor.http.HttpStatusCode +import io.ktor.http.URLProtocol +import io.ktor.http.content.OutgoingContent +import io.ktor.http.userAgent +import kotlinx.coroutines.io.ByteWriteChannel import kotlinx.io.core.Input +import kotlinx.io.core.readAvailable import kotlinx.io.core.use +import kotlinx.io.pool.useInstance import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead import net.mamoe.mirai.qqandroid.network.protocol.packet.withUse import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.io.ByteArrayPool import net.mamoe.mirai.utils.io.PlatformSocket import net.mamoe.mirai.utils.io.discardExact + +@Suppress("SpellCheckingInspection") +internal suspend inline fun HttpClient.postImage( + htcmd: String, + uin: Long, + groupcode: Long?, + imageInput: Input, + inputSize: Long, + uKeyHex: String +): Boolean = try { + post { + url { + protocol = URLProtocol.HTTP + host = "htdata2.qq.com" + path("cgi-bin/httpconn") + + parameters["htcmd"] = htcmd + parameters["uin"] = uin.toString() + + if (groupcode != null) parameters["groupcode"] = groupcode.toString() + + parameters["term"] = "pc" + parameters["ver"] = "5603" + parameters["filesize"] = inputSize.toString() + parameters["range"] = 0.toString() + parameters["ukey"] = uKeyHex + + userAgent("QQClient") + } + + body = object : OutgoingContent.WriteChannelContent() { + override val contentType: ContentType = ContentType.Image.Any + override val contentLength: Long = inputSize + + override suspend fun writeTo(channel: ByteWriteChannel) { + ByteArrayPool.useInstance { buffer: ByteArray -> + var size: Int + while (imageInput.readAvailable(buffer).also { size = it } != 0) { + channel.writeFully(buffer, 0, size) + } + } + } + } + } == HttpStatusCode.OK +} finally { + imageInput.close() +} + @UseExperimental(MiraiInternalAPI::class) internal object HighwayHelper { - suspend fun uploadImage( client: QQAndroidClient, serverIp: String,