Merge remote-tracking branch 'origin/master'

This commit is contained in:
jiahua.liu 2020-02-07 21:25:51 +08:00
commit 009f11ea0b
5 changed files with 180 additions and 69 deletions

View File

@ -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,

View File

@ -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<HttpStatusCode> {
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(

View File

@ -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<HttpStatusCode> {
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,

View File

@ -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<CPicInfo>? = 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<String>? = 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<MsgInfo>,
@SerialId(3) val svrip: Int? = 0,
@SerialId(4) val vSyncCookie: ByteArray? = null,
@SerialId(5) val vUinPairMsg: List<UinPairMsg>? = null,
@SerialId(6) val mPreviews: Map<String, ByteArray>? = 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<DelMsgInfo>,
@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<MsgInfo>? = null
) : JceStruct
}

View File

@ -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<Packet>("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
}
}
}