diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/Contact.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/Contact.java new file mode 100644 index 0000000..9497f66 --- /dev/null +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/Contact.java @@ -0,0 +1,51 @@ +package com.tencent.qqnt.kernel.nativeinterface; + +import java.io.Serializable; + +public final class Contact implements IKernelModel, Serializable { + int chatType; + String guildId; + String peerUid; + long serialVersionUID; + + public Contact() { + this.serialVersionUID = 1L; + this.peerUid = ""; + this.guildId = ""; + } + + public int getChatType() { + return this.chatType; + } + + public String getGuildId() { + return this.guildId; + } + + public String getPeerUid() { + return this.peerUid; + } + + public void setChatType(int i2) { + this.chatType = i2; + } + + public void setGuildId(String str) { + this.guildId = str; + } + + public void setPeerUid(String str) { + this.peerUid = str; + } + + public String toString() { + return "Contact{chatType=" + this.chatType + ",peerUid=" + this.peerUid + ",guildId=" + this.guildId + ",}"; + } + + public Contact(int i2, String str, String str2) { + this.serialVersionUID = 1L; + this.chatType = i2; + this.peerUid = str; + this.guildId = str2; + } +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/NtV2RichMediaSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/NtV2RichMediaSvc.kt index 8341ccf..6a4d048 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/NtV2RichMediaSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/NtV2RichMediaSvc.kt @@ -15,6 +15,7 @@ import com.tencent.qqnt.kernel.nativeinterface.VideoElement import com.tencent.qqnt.kernelpublic.nativeinterface.Contact import kotlinx.atomicfu.atomic import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData @@ -45,6 +46,7 @@ import protobuf.oidb.cmd0x11c5.IndexNode import protobuf.oidb.cmd0x11c5.MultiMediaReqHead import protobuf.oidb.cmd0x11c5.NtV2RichMediaReq import protobuf.oidb.cmd0x11c5.NtV2RichMediaRsp +import protobuf.oidb.cmd0x11c5.RKeyInfo import protobuf.oidb.cmd0x11c5.SceneInfo import protobuf.oidb.cmd0x11c5.UploadInfo import protobuf.oidb.cmd0x11c5.UploadReq @@ -63,6 +65,8 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds internal object NtV2RichMediaSvc: QQInterfaces() { + private lateinit var cacheRKeyInfo: DownloadRkeyRsp + private var lastRefreshRKeyTime = 0L private val requestIdSeq = atomic(2L) fun fetchGroupResUploadTo(): String { @@ -325,6 +329,9 @@ internal object NtV2RichMediaSvc: QQInterfaces() { } suspend fun getTempNtRKey(): Result { + if (System.currentTimeMillis() - lastRefreshRKeyTime < 60 * 60_000 && ::cacheRKeyInfo.isInitialized) { + return Result.success(cacheRKeyInfo) + } runCatching { val req = NtV2RichMediaReq( head = MultiMediaReqHead( @@ -350,6 +357,8 @@ internal object NtV2RichMediaSvc: QQInterfaces() { } val trpc = fromServiceMsg.decodeToTrpcOidb() trpc.buffer.decodeProtobuf().downloadRkeyRsp?.let { + cacheRKeyInfo = it + lastRefreshRKeyTime = System.currentTimeMillis() return Result.success(it) } }.onFailure { diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt index 9277877..2636bb3 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt @@ -11,6 +11,7 @@ import com.tencent.qqnt.kernel.nativeinterface.PicElement import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.serialization.ExperimentalSerializationApi import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc.getNtPicRKey +import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc.getTempNtRKey import moe.fuqiuluo.shamrock.helper.ContactHelper import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter @@ -231,6 +232,23 @@ internal object RichProtoSvc: QQInterfaces() { val isNtServer = originalUrl.startsWith("/download") val domain = if (isNtServer) MULTIMEDIA_DOMAIN else GPRO_PIC if (originalUrl.isNotEmpty()) { + // 高于QQ9.0.70,可以直接请求获取rkey + val tmpRKey = getTempNtRKey().onFailure { + LogCenter.log("getTempNtRKey: ${it.stackTraceToString()}", Level.WARN) + } + if (tmpRKey.isSuccess) { + val tmpRKeyRsp = tmpRKey.getOrThrow() + val tmpRKeyMap = hashMapOf() + tmpRKeyRsp.rkeys?.forEach { rKeyInfo -> + tmpRKeyMap[rKeyInfo.type] = rKeyInfo.rkey + } + val rkey = tmpRKeyMap[10u] + if (rkey != null) { + return "https://$MULTIMEDIA_DOMAIN$originalUrl$rkey" + } + } + + // 低于QQ9.0.70但是支持nt资源的客户端支持 if (isNtServer && !originalUrl.contains("rkey=")) { getNtPicRKey( fileId = fileId, @@ -267,6 +285,21 @@ internal object RichProtoSvc: QQInterfaces() { val isNtServer = storeId == 1 || originalUrl.startsWith("/download") val domain = if (isNtServer) MULTIMEDIA_DOMAIN else C2C_PIC if (originalUrl.isNotEmpty()) { + // 高于QQ9.0.70,可以直接请求获取rkey + val tmpRKey = getTempNtRKey() + if (tmpRKey.isSuccess) { + val tmpRKeyRsp = tmpRKey.getOrThrow() + val tmpRKeyMap = hashMapOf() + tmpRKeyRsp.rkeys?.forEach { rKeyInfo -> + tmpRKeyMap[rKeyInfo.type] = rKeyInfo.rkey + } + val rkey = tmpRKeyMap[20u] + if (rkey != null) { + return "https://$MULTIMEDIA_DOMAIN$originalUrl$rkey" + } + } + + // 低于QQ9.0.70但是支持nt资源的客户端支持 if (fileId.isNotEmpty()) getNtPicRKey( fileId = fileId, md5 = md5, diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt index bbd7b18..c9f8f3c 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/AioListener.kt @@ -42,6 +42,7 @@ internal object AioListener : IKernelMsgListener { try { if (MessageTempHandler.notify(record)) return if (record.msgSeq < 0) return + if (record.chatType == MsgConstant.KCHATTYPETEMPPUBLICACCOUNT) return // 订阅号不处理 val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)