mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
parent
b4c40e236a
commit
9ad66f2f92
@ -32,9 +32,9 @@ class ProtobufProcessor(
|
||||
}.toList()
|
||||
|
||||
if (actions.isNotEmpty()) {
|
||||
actions.forEach { clz ->
|
||||
if (clz.isInternal()) return@forEach
|
||||
if (clz.isPrivate()) return@forEach
|
||||
actions.forEachIndexed { index, clz ->
|
||||
if (clz.isInternal()) return@forEachIndexed
|
||||
if (clz.isPrivate()) return@forEachIndexed
|
||||
|
||||
val packageName = "protobuf.auto"
|
||||
val fileSpecBuilder = FileSpec.scriptBuilder("FastProtobuf", packageName)
|
||||
@ -58,7 +58,7 @@ class ProtobufProcessor(
|
||||
codeGenerator.createNewFile(
|
||||
dependencies = Dependencies.ALL_FILES,
|
||||
packageName = packageName,
|
||||
fileName = clz.simpleName.asString() + "\$FP"
|
||||
fileName = "FP${clz.simpleName.asString().hashCode()}"
|
||||
).use { outputStream ->
|
||||
outputStream.writer().use {
|
||||
fileSpecBuilder.build().writeTo(it)
|
||||
|
@ -9,5 +9,6 @@ data class TrpcOidb(
|
||||
@ProtoNumber(1) val cmd: Int = Int.MIN_VALUE,
|
||||
@ProtoNumber(2) val service: Int = Int.MIN_VALUE,
|
||||
@ProtoNumber(4) val buffer: ByteArray,
|
||||
//@ProtoNumber(11) val traceParams: Map<String, String> = mapOf(),
|
||||
@ProtoNumber(12) val flag: Int = Int.MIN_VALUE,
|
||||
): Protobuf<TrpcOidb>
|
135
protobuf/src/main/java/protobuf/oidb/cmd0x11c5/Oidb0x11c5.kt
Normal file
135
protobuf/src/main/java/protobuf/oidb/cmd0x11c5/Oidb0x11c5.kt
Normal file
@ -0,0 +1,135 @@
|
||||
@file:OptIn(ExperimentalSerializationApi::class)
|
||||
package protobuf.oidb.cmd0x11c5
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import moe.fuqiuluo.symbols.Protobuf
|
||||
|
||||
@Serializable
|
||||
data class Oidb0x11c5Req(
|
||||
@ProtoNumber(1) val head: MultiMediaRoutingHead,
|
||||
@ProtoNumber(3) val dataInfo: MultiMediaDataInfo
|
||||
): Protobuf<Oidb0x11c5Req>
|
||||
|
||||
@Serializable
|
||||
data class Oidb0x11c5Resp(
|
||||
@ProtoNumber(1) val head: Head,
|
||||
@ProtoNumber(3) val result: DownloadResult?
|
||||
): Protobuf<Oidb0x11c5Resp> {
|
||||
companion object {
|
||||
@Serializable
|
||||
data class Head(
|
||||
//@ProtoNumber(1) val request: Request,
|
||||
@ProtoNumber(3) val msg: String
|
||||
) {
|
||||
/*companion object {
|
||||
@Serializable
|
||||
data class Request(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
@ProtoNumber(2) val u2: UInt
|
||||
)
|
||||
}*/
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class DownloadResult(
|
||||
@ProtoNumber(1) val rkeyParam: String,
|
||||
@ProtoNumber(2) val expire: UInt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class MultiMediaDataInfo(
|
||||
@ProtoNumber(1) val multiMedia: MultiMedia,
|
||||
@ProtoNumber(2) val ext: EXT,
|
||||
) {
|
||||
companion object {
|
||||
@Serializable
|
||||
data class MultiMedia(
|
||||
@ProtoNumber(1) val picture: Picture,
|
||||
@ProtoNumber(2) val fileId: String,
|
||||
@ProtoNumber(3) val u1: UInt,
|
||||
@ProtoNumber(4) val u2: UInt,
|
||||
@ProtoNumber(5) val u3: UInt,
|
||||
@ProtoNumber(6) val u4: UInt,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Picture(
|
||||
@ProtoNumber(1) val size: ULong,
|
||||
@ProtoNumber(2) val md5: String,
|
||||
@ProtoNumber(3) val sha: String,
|
||||
@ProtoNumber(4) val fileName: String,
|
||||
@ProtoNumber(5) val u1: U3,
|
||||
@ProtoNumber(6) val width: UInt,
|
||||
@ProtoNumber(7) val height: UInt,
|
||||
@ProtoNumber(8) val u2: UInt,
|
||||
@ProtoNumber(9) val u3: UInt,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class U3(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
@ProtoNumber(2) val u2: UInt,
|
||||
@ProtoNumber(3) val u3: UInt,
|
||||
@ProtoNumber(4) val u4: UInt,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class EXT(
|
||||
@ProtoNumber(2) val u1: U1,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class U1(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
@ProtoNumber(3) val u2: UInt,
|
||||
@ProtoNumber(4) val u3: U2,
|
||||
@ProtoNumber(5) val u4: UInt,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class U2(
|
||||
@ProtoNumber(1) val u1: ByteArray,
|
||||
@ProtoNumber(2) val u2: ByteArray,
|
||||
@ProtoNumber(3) val u3: ByteArray,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class MultiMediaRoutingHead(
|
||||
@ProtoNumber(1) val request: Request,
|
||||
@ProtoNumber(2) val peerUser: PeerUser,
|
||||
@ProtoNumber(3) val u1: U1
|
||||
) {
|
||||
companion object {
|
||||
@Serializable
|
||||
data class U1(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Request(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
@ProtoNumber(2) val u2: UInt
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PeerUser(
|
||||
@ProtoNumber(101) val u1: UInt,
|
||||
@ProtoNumber(102) val u2: UInt,
|
||||
@ProtoNumber(200) val u3: UInt,
|
||||
@ProtoNumber(201) val peer: Peer,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Peer(
|
||||
@ProtoNumber(1) val u1: UInt,
|
||||
@ProtoNumber(2) val uid: String
|
||||
)
|
||||
}
|
||||
}
|
@ -184,19 +184,20 @@ internal object MsgElementConverter {
|
||||
element: MsgElement
|
||||
): MessageSegment {
|
||||
val image = element.picElement
|
||||
val md5 = image.md5HexStr ?: image.fileName
|
||||
val md5 = (image.md5HexStr ?: image.fileName
|
||||
.replace("{", "")
|
||||
.replace("}", "")
|
||||
.replace("-", "").split(".")[0]
|
||||
.replace("-", "").split(".")[0])
|
||||
.uppercase()
|
||||
|
||||
ImageDB.getInstance().imageMappingDao().insert(
|
||||
ImageMapping(md5.uppercase(), chatType, image.fileSize)
|
||||
ImageMapping(md5, chatType, image.fileSize)
|
||||
)
|
||||
|
||||
//LogCenter.log(image.toString())
|
||||
|
||||
val originalUrl = image.originImageUrl ?: ""
|
||||
//LogCenter.log({ "receive image: $image" }, Level.DEBUG)
|
||||
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
|
||||
|
||||
return MessageSegment(
|
||||
type = "image",
|
||||
@ -208,7 +209,16 @@ internal object MsgElementConverter {
|
||||
md5
|
||||
)
|
||||
|
||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(originalUrl, md5)
|
||||
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(
|
||||
originalUrl = originalUrl,
|
||||
md5 = md5,
|
||||
fileId = image.fileUuid,
|
||||
width = image.picWidth.toUInt(),
|
||||
height = image.picHeight.toUInt(),
|
||||
sha = "",
|
||||
fileSize = image.fileSize.toULong(),
|
||||
peer = peerId
|
||||
)
|
||||
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(originalUrl, md5)
|
||||
else -> throw UnsupportedOperationException("Not supported chat type: $chatType")
|
||||
},
|
||||
|
@ -6,15 +6,15 @@ import com.tencent.mobileqq.transfile.FileMsg
|
||||
import com.tencent.mobileqq.transfile.api.IProtoReqManager
|
||||
import com.tencent.mobileqq.transfile.protohandler.RichProto
|
||||
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
|
||||
import io.ktor.util.Identity.decode
|
||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.decodeFromByteArray
|
||||
import kotlinx.serialization.encodeToByteArray
|
||||
|
||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||
import moe.fuqiuluo.shamrock.helper.Level
|
||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
|
||||
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
||||
import moe.fuqiuluo.shamrock.tools.slice
|
||||
import moe.fuqiuluo.shamrock.tools.toHexString
|
||||
@ -23,6 +23,11 @@ import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
|
||||
import moe.fuqiuluo.symbols.decodeProtobuf
|
||||
import mqq.app.MobileQQ
|
||||
import protobuf.auto.toByteArray
|
||||
import protobuf.oidb.TrpcOidb
|
||||
import protobuf.oidb.cmd0x11c5.MultiMediaDataInfo
|
||||
import protobuf.oidb.cmd0x11c5.MultiMediaRoutingHead
|
||||
import protobuf.oidb.cmd0x11c5.Oidb0x11c5Req
|
||||
import protobuf.oidb.cmd0x11c5.Oidb0x11c5Resp
|
||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
|
||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
|
||||
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
|
||||
@ -34,22 +39,11 @@ import tencent.im.oidb.oidb_sso
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
private const val GPRO_PIC = "gchat.qpic.cn"
|
||||
private const val GPRO_PIC_NT = "multimedia.nt.qq.com.cn"
|
||||
private const val MULTIMEDIA_DOMAIN = "multimedia.nt.qq.com.cn"
|
||||
private const val C2C_PIC = "c2cpicdw.qpic.cn"
|
||||
|
||||
internal object RichProtoSvc: BaseSvc() {
|
||||
var multiMediaRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
|
||||
/*@Deprecated("Use RichProtoSvc.getQQDns instead", ReplaceWith("getQQDns(domain)"))
|
||||
fun getQQDns(domain: String) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("domain", "xxx")
|
||||
bundle.putInt("businessType", 1)
|
||||
val result = BinderMethodProxy
|
||||
.callServer(QIPCClientHelper.getInstance().client, "InnerDnsModule", "reqDomain2IpList", bundle)
|
||||
if (result.isSuccess) {
|
||||
val ipList: ArrayList<IpData> = result.data.getParcelableArrayList("ip")!!
|
||||
}
|
||||
}*/
|
||||
|
||||
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
|
||||
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, Oidb0xfc2ReqBody(
|
||||
@ -167,7 +161,7 @@ internal object RichProtoSvc: BaseSvc() {
|
||||
md5: String,
|
||||
): String {
|
||||
val isNtServer = originalUrl.startsWith("/download")
|
||||
val domain = if (isNtServer) GPRO_PIC_NT else GPRO_PIC
|
||||
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else GPRO_PIC
|
||||
if (originalUrl.isNotEmpty()) {
|
||||
if (isNtServer && !originalUrl.contains("rkey=")) {
|
||||
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
|
||||
@ -177,19 +171,38 @@ internal object RichProtoSvc: BaseSvc() {
|
||||
return "https://$domain/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2"
|
||||
}
|
||||
|
||||
fun getC2CPicDownUrl(
|
||||
suspend fun getC2CPicDownUrl(
|
||||
originalUrl: String,
|
||||
md5: String
|
||||
md5: String,
|
||||
peer: String = "",
|
||||
fileId: String = "",
|
||||
sha: String = "",
|
||||
fileSize: ULong = 0uL,
|
||||
width: UInt = 0u,
|
||||
height: UInt = 0u
|
||||
): String {
|
||||
val isNtServer = originalUrl.startsWith("/download")
|
||||
val domain = if (isNtServer) GPRO_PIC_NT else C2C_PIC
|
||||
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else C2C_PIC
|
||||
if (originalUrl.isNotEmpty()) {
|
||||
if (fileId.isNotEmpty()) getC2CNtPicRKey(
|
||||
peer = ContactHelper.getUidByUinAsync(peer.toLong()),
|
||||
fileId = fileId,
|
||||
md5 = md5,
|
||||
sha = sha,
|
||||
fileSize = fileSize,
|
||||
width = width,
|
||||
height = height
|
||||
).onSuccess {
|
||||
if (isNtServer && !originalUrl.contains("rkey=")) {
|
||||
return "https://$domain$originalUrl$it"
|
||||
}
|
||||
}
|
||||
if (isNtServer && !originalUrl.contains("rkey=")) {
|
||||
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
|
||||
}
|
||||
return "https://$domain$originalUrl"
|
||||
}
|
||||
return "https://$$domain/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
|
||||
return "https://$$domain/offpic_new/0/123-0-${md5}/0?term=2"
|
||||
}
|
||||
|
||||
fun getGuildPicDownUrl(
|
||||
@ -197,7 +210,7 @@ internal object RichProtoSvc: BaseSvc() {
|
||||
md5: String
|
||||
): String {
|
||||
val isNtServer = originalUrl.startsWith("/download")
|
||||
val domain = if (isNtServer) GPRO_PIC_NT else GPRO_PIC
|
||||
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else GPRO_PIC
|
||||
if (originalUrl.isNotEmpty()) {
|
||||
if (isNtServer && !originalUrl.contains("rkey=")) {
|
||||
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
|
||||
@ -207,6 +220,71 @@ internal object RichProtoSvc: BaseSvc() {
|
||||
return "https://$domain/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
|
||||
}
|
||||
|
||||
suspend fun getC2CNtPicRKey(
|
||||
peer: String,
|
||||
fileId: String,
|
||||
md5: String,
|
||||
sha: String,
|
||||
fileSize: ULong,
|
||||
width: UInt,
|
||||
height: UInt
|
||||
): Result<String> {
|
||||
val req = run {
|
||||
Oidb0x11c5Req(
|
||||
MultiMediaRoutingHead(
|
||||
request = MultiMediaRoutingHead.Companion.Request(u1 = 2u, u2 = 200u),
|
||||
peerUser = MultiMediaRoutingHead.Companion.PeerUser(u1 = 2u, u2 = 1u, u3 = 1u, peer = MultiMediaRoutingHead.Companion.Peer(
|
||||
u1 = 2u,
|
||||
uid = peer
|
||||
)),
|
||||
u1 = MultiMediaRoutingHead.Companion.U1(2u)
|
||||
),
|
||||
MultiMediaDataInfo(
|
||||
MultiMediaDataInfo.Companion.MultiMedia(
|
||||
MultiMediaDataInfo.Companion.Picture(
|
||||
size = fileSize,
|
||||
md5 = md5.lowercase(),
|
||||
sha = sha.lowercase(),
|
||||
fileName = "${md5}.jpg",
|
||||
u1 = MultiMediaDataInfo.Companion.U3(
|
||||
u1 = 1u,
|
||||
u2 = 1000u,
|
||||
u3 = 0u,
|
||||
u4 = 0u
|
||||
),
|
||||
width = width,
|
||||
height = height,
|
||||
u2 = 0u,
|
||||
u3 = 1u
|
||||
),
|
||||
fileId = fileId,
|
||||
u1 = 1u,
|
||||
u2 = 0u,
|
||||
u3 = 0u,
|
||||
u4 = 0u
|
||||
),
|
||||
MultiMediaDataInfo.Companion.EXT(
|
||||
u1 = MultiMediaDataInfo.Companion.U1(
|
||||
u1 = 0u,
|
||||
u2 = 0u,
|
||||
u3 = MultiMediaDataInfo.Companion.U2(
|
||||
u1 = EMPTY_BYTE_ARRAY,
|
||||
u2 = EMPTY_BYTE_ARRAY,
|
||||
u3 = EMPTY_BYTE_ARRAY
|
||||
),
|
||||
u4 = 1u
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}.toByteArray()
|
||||
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x11c5_200", 0x11c5, 200, req, true)?.slice(4)
|
||||
buffer?.decodeProtobuf<TrpcOidb>()?.buffer?.decodeProtobuf<Oidb0x11c5Resp>()?.result?.rkeyParam?.let {
|
||||
return Result.success(it)
|
||||
}
|
||||
return Result.failure(Exception("unable to get c2c nt pic"))
|
||||
}
|
||||
|
||||
suspend fun getC2CVideoDownUrl(
|
||||
peerId: String,
|
||||
md5: ByteArray,
|
||||
|
@ -18,7 +18,7 @@ internal object GetImage: IActionHandler() {
|
||||
return invoke(file, echo)
|
||||
}
|
||||
|
||||
operator fun invoke(file: String, echo: JsonElement = EmptyJsonString): String {
|
||||
suspend operator fun invoke(file: String, echo: JsonElement = EmptyJsonString): String {
|
||||
val fileMd5 = file
|
||||
.replace("{", "")
|
||||
.replace("}", "")
|
||||
|
Loading…
x
Reference in New Issue
Block a user