Shamrock: Support télécharger un fichier obtenir un ID de fichier

This commit is contained in:
WhiteChi 2023-11-25 19:55:00 +08:00
parent 1a814e565a
commit 4fedab719b
6 changed files with 105 additions and 29 deletions

View File

@ -327,10 +327,10 @@ internal sealed class MessageElemConverter: IMessageConvert {
val fileSize = fileMsg.fileSize val fileSize = fileMsg.fileSize
val expireTime = fileMsg.expireTime ?: 0 val expireTime = fileMsg.expireTime ?: 0
val fileId = fileMsg.fileUuid val fileId = fileMsg.fileUuid
val bizId = fileMsg.fileBizId val bizId = fileMsg.fileBizId ?: 0
val fileSubId = fileMsg.fileSubId ?: "" val fileSubId = fileMsg.fileSubId ?: ""
val url = if (chatType == MsgConstant.KCHATTYPEC2C) RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId) val url = if (chatType == MsgConstant.KCHATTYPEC2C) RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, fileMsg.fileBizId) else RichProtoSvc.getGroupFileDownUrl(peerId.toLong(), fileId, bizId)
return MessageSegment( return MessageSegment(
type = "file", type = "file",

View File

@ -5,12 +5,15 @@ import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.FileElement import com.tencent.qqnt.kernel.nativeinterface.FileElement
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import com.tencent.qqnt.msg.api.IMsgUtilApi import com.tencent.qqnt.msg.api.IMsgUtilApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
@ -19,11 +22,13 @@ import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.TransfileHelper import moe.fuqiuluo.shamrock.helper.TransfileHelper
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import kotlin.coroutines.resume
internal object UploadGroupFile : IActionHandler() { internal object UploadGroupFile : IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String { override suspend fun internalHandle(session: ActionSession): String {
@ -94,16 +99,36 @@ internal object UploadGroupFile : IActionHandler() {
msgElement.elementType = MsgConstant.KELEMTYPEFILE msgElement.elementType = MsgConstant.KELEMTYPEFILE
msgElement.fileElement = fileElement msgElement.fileElement = fileElement
// 根据文件大小调整超时时间
val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP) val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP)
val info = (withTimeoutOrNull((srcFile.length() / (300 * 1024)) * 1000 + 5000) {
val msgService = QRoute.api(IMsgService::class.java) val msgService = QRoute.api(IMsgService::class.java)
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId)
suspendCancellableCoroutine<FileTransNotifyInfo?> {
msgService.sendMsgWithMsgId( msgService.sendMsgWithMsgId(
MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, groupId), msgIdPair.second, arrayListOf(msgElement) contact, msgIdPair.second, arrayListOf(msgElement)
) { code, reason -> ) { code, reason ->
LogCenter.log("群文件消息发送异常(code = $code, reason = $reason)") LogCenter.log("群文件消息发送异常(code = $code, reason = $reason)")
it.resume(null)
} }
RichMediaUploadHandler.registerListener(msgIdPair.second) {
it.resume(this)
return@registerListener true
}
}
} ?: return error("上传文件失败", echo)).also {
if (it.commonFileInfo == null) {
return error(it.fileErrMsg ?: "上传文件失败", echo)
}
}.commonFileInfo
return ok(data = FileUploadResult( return ok(data = FileUploadResult(
msgHash = msgIdPair.first msgHash = msgIdPair.first,
bizid = info.bizType ?: 0,
md5 = info.md5,
sha = info.sha,
sha3 = info.sha3,
fileId = info.uuid
), echo = echo) ), echo = echo)
} }
@ -114,5 +139,10 @@ internal object UploadGroupFile : IActionHandler() {
@Serializable @Serializable
data class FileUploadResult( data class FileUploadResult(
@SerialName("msg_id") val msgHash: Int, @SerialName("msg_id") val msgHash: Int,
@SerialName("bizid") val bizid: Int,
@SerialName("md5") val md5: String,
@SerialName("sha") val sha: String,
@SerialName("sha3") val sha3: String,
@SerialName("file_id") val fileId: String
) )
} }

View File

@ -5,12 +5,15 @@ import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.FileElement import com.tencent.qqnt.kernel.nativeinterface.FileElement
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import com.tencent.qqnt.msg.api.IMsgUtilApi import com.tencent.qqnt.msg.api.IMsgUtilApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
@ -20,11 +23,13 @@ import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.TransfileHelper import moe.fuqiuluo.shamrock.helper.TransfileHelper
import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import kotlin.coroutines.resume
internal object UploadPrivateFile : IActionHandler() { internal object UploadPrivateFile : IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String { override suspend fun internalHandle(session: ActionSession): String {
@ -95,26 +100,40 @@ internal object UploadPrivateFile : IActionHandler() {
msgElement.elementType = MsgConstant.KELEMTYPEFILE msgElement.elementType = MsgConstant.KELEMTYPEFILE
msgElement.fileElement = fileElement msgElement.fileElement = fileElement
// 根据文件大小调整超时时间
val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEC2C) val msgIdPair = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEC2C)
val info = (withTimeoutOrNull((srcFile.length() / (300 * 1024)) * 1000 + 5000) {
val msgService = QRoute.api(IMsgService::class.java) val msgService = QRoute.api(IMsgService::class.java)
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, userId)
suspendCancellableCoroutine<FileTransNotifyInfo?> {
msgService.sendMsgWithMsgId( msgService.sendMsgWithMsgId(
MessageHelper.generateContact(MsgConstant.KCHATTYPEC2C, userId), msgIdPair.second, arrayListOf(msgElement) contact, msgIdPair.second, arrayListOf(msgElement)
) { code, reason -> ) { code, reason ->
if (code != 0)
LogCenter.log("私聊文件消息发送异常(code = $code, reason = $reason)") LogCenter.log("私聊文件消息发送异常(code = $code, reason = $reason)")
it.resume(null)
} }
RichMediaUploadHandler.registerListener(msgIdPair.second) {
it.resume(this)
return@registerListener true
}
}
} ?: return error("上传文件失败", echo)).also {
if (it.commonFileInfo == null) {
return error(it.fileErrMsg ?: "上传文件失败", echo)
}
}.commonFileInfo
return ok(data = FileUploadResult( return ok(data = UploadGroupFile.FileUploadResult(
msgHash = msgIdPair.first msgHash = msgIdPair.first,
bizid = info.bizType ?: 0,
md5 = info.md5,
sha = info.sha,
sha3 = info.sha3,
fileId = info.uuid
), echo = echo) ), echo = echo)
} }
override val requiredParams: Array<String> = arrayOf("user_id", "file", "name") override val requiredParams: Array<String> = arrayOf("user_id", "file", "name")
override fun path(): String = "upload_private_file" override fun path(): String = "upload_private_file"
@Serializable
data class FileUploadResult(
@SerialName("msg_id") val msgHash: Int,
)
} }

View File

@ -0,0 +1,27 @@
package moe.fuqiuluo.shamrock.remote.service.api
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo
internal object RichMediaUploadHandler {
private val listeners by lazy {
mutableMapOf<Long, FileTransNotifyInfo.() -> Boolean>()
}
fun registerListener(key: Long, value: FileTransNotifyInfo.() -> Boolean) {
listeners[key] = value
}
fun removeListener(key: Long) {
listeners.remove(key)
}
fun notify(info: FileTransNotifyInfo): Boolean {
listeners[info.msgId]?.let {
if (it(info)) {
listeners.remove(info.msgId)
return true
}
}
return false
}
}

View File

@ -15,6 +15,7 @@ import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.db.MessageDB import moe.fuqiuluo.shamrock.helper.db.MessageDB
import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter
import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler
import moe.fuqiuluo.shamrock.remote.service.data.push.MessageTempSource import moe.fuqiuluo.shamrock.remote.service.data.push.MessageTempSource
import moe.fuqiuluo.shamrock.remote.service.data.push.PostType import moe.fuqiuluo.shamrock.remote.service.data.push.PostType
import mqq.app.MobileQQ import mqq.app.MobileQQ
@ -347,6 +348,11 @@ internal object AioListener: IKernelMsgListener {
} }
} }
override fun onRichMediaUploadComplete(notifyInfo: FileTransNotifyInfo) {
//LogCenter.log("onRichMediaUploadComplete($notifyInfo)", Level.DEBUG)
RichMediaUploadHandler.notify(notifyInfo)
}
override fun onRecvOnlineFileMsg(arrayList: ArrayList<MsgRecord>?) { override fun onRecvOnlineFileMsg(arrayList: ArrayList<MsgRecord>?) {
LogCenter.log(("onRecvOnlineFileMsg" + arrayList?.joinToString { ", " }), Level.DEBUG) LogCenter.log(("onRecvOnlineFileMsg" + arrayList?.joinToString { ", " }), Level.DEBUG)
} }
@ -359,10 +365,6 @@ internal object AioListener: IKernelMsgListener {
} }
override fun onRichMediaUploadComplete(fileTransNotifyInfo: FileTransNotifyInfo) {
}
override fun onSearchGroupFileInfoUpdate(searchGroupFileResult: SearchGroupFileResult?) { override fun onSearchGroupFileInfoUpdate(searchGroupFileResult: SearchGroupFileResult?) {
LogCenter.log("onSearchGroupFileInfoUpdate($searchGroupFileResult)", Level.DEBUG) LogCenter.log("onSearchGroupFileInfoUpdate($searchGroupFileResult)", Level.DEBUG)
} }

View File

@ -336,7 +336,6 @@ internal object PrimitiveListener {
val targetUid = pb[1, 3, 2, 3].asUtf8String val targetUid = pb[1, 3, 2, 3].asUtf8String
val type = pb[1, 3, 2, 4].asInt val type = pb[1, 3, 2, 4].asInt
val operation = ContactHelper.getUinByUidAsync(pb[1, 3, 2, 5].asUtf8String).toLong() val operation = ContactHelper.getUinByUidAsync(pb[1, 3, 2, 5].asUtf8String).toLong()
// 131 passive | 130 active | 3 kick_self
val target = ContactHelper.getUinByUidAsync(targetUid).toLong() val target = ContactHelper.getUinByUidAsync(targetUid).toLong()
val subtype = when(type) { val subtype = when(type) {
@ -344,7 +343,6 @@ internal object PrimitiveListener {
131 -> NoticeSubType.Kick 131 -> NoticeSubType.Kick
3 -> NoticeSubType.KickMe 3 -> NoticeSubType.KickMe
else -> { else -> {
NoticeSubType.Kick NoticeSubType.Kick
} }
} }