Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Commit

Permalink
Shamrock: support upload resource by NtKernel
Browse files Browse the repository at this point in the history
Signed-off-by: 白池 <[email protected]>
  • Loading branch information
whitechi73 committed Feb 25, 2024
1 parent 92ebe0c commit fca66f3
Show file tree
Hide file tree
Showing 24 changed files with 309 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.util.HashMap;

public interface IKernelMsgService {
void deleteMsg(Contact contact, ArrayList<Long> msgIdList, IOperateCallback callback);
void deleteMsg(Contact contact, ArrayList<Long> msgIdList, IOperateCallback cb);

void fetchLongMsg(Contact contact, long msgId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.ktor.utils.io.core.writeFully
import io.ktor.utils.io.core.writeInt
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler

import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
Expand Down Expand Up @@ -80,11 +81,11 @@ internal object PacketSvc: BaseSvc() {
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, msgPush.toByteArray())
return withTimeoutOrNull(5000L) {
suspendCancellableCoroutine {
AioListener.registerTemporaryMsgListener(msgSeq) {
MessageTempHandler.registerTemporaryMsgListener(msgSeq) {
it.resume(this.msgId)
}
it.invokeOnCancellation {
AioListener.unregisterTemporaryMsgListener(msgSeq)
MessageTempHandler.unregisterTemporaryMsgListener(msgSeq)
}
}
} ?: -1L
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package moe.fuqiuluo.qqinterface.servlet.ark

import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
import kotlin.coroutines.resume
import kotlin.time.Duration.Companion.seconds

internal object ArkMsgSvc: BaseSvc() {
fun tryShareMusic(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import io.ktor.client.request.url
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.http.encodeURLQueryComponent
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.ark.data.Region
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package moe.fuqiuluo.qqinterface.servlet.ark
package moe.fuqiuluo.qqinterface.servlet.ark.data

sealed class ArkAppInfo(
val appId: Long,
val version: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.ark
package moe.fuqiuluo.qqinterface.servlet.ark.data

import kotlinx.serialization.Serializable

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package moe.fuqiuluo.qqinterface.servlet.msg

import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import java.util.Collections

internal object MessageTempHandler {
// 通过MSG SEQ临时监听器
private val tempMessageListenerMap = Collections.synchronizedMap(HashMap<Long, suspend MsgRecord.() -> Unit>())

fun registerTemporaryMsgListener(
msgSeq: Long,
listener: suspend MsgRecord.() -> Unit
) {
LogCenter.log({ "注册临时消息监听器: $msgSeq" }, Level.DEBUG)
tempMessageListenerMap[msgSeq] = listener
}

fun unregisterTemporaryMsgListener(msgSeq: Long) {
tempMessageListenerMap.remove(msgSeq)
}

suspend fun notify(record: MsgRecord): Boolean {
tempMessageListenerMap.firstNotNullOfOrNull {
if (it.key == record.msgSeq) it else null
}?.let {
it.value(record)
tempMessageListenerMap.remove(it.key)
return true
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
import moe.fuqiuluo.qqinterface.servlet.msg.toJson
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
import moe.fuqiuluo.qqinterface.servlet.transfile.*
import moe.fuqiuluo.qqinterface.servlet.transfile.PictureResource
import moe.fuqiuluo.qqinterface.servlet.transfile.Private
import moe.fuqiuluo.qqinterface.servlet.transfile.data.PictureResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Private
import moe.fuqiuluo.qqinterface.servlet.transfile.Transfer
import moe.fuqiuluo.qqinterface.servlet.transfile.Troop
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Troop
import moe.fuqiuluo.shamrock.helper.*
import moe.fuqiuluo.shamrock.helper.ActionMsgException
import moe.fuqiuluo.shamrock.helper.Level
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package moe.fuqiuluo.qqinterface.servlet.msg.maker
import android.graphics.BitmapFactory
import androidx.exifinterface.media.ExifInterface
import com.tencent.mobileqq.app.QQAppInterface
import com.tencent.mobileqq.data.MessageForPic
import com.tencent.mobileqq.emoticon.QQSysFaceUtil
import com.tencent.mobileqq.pb.ByteStringMicro
import com.tencent.mobileqq.qroute.QRoute
Expand All @@ -17,17 +16,17 @@ import kotlinx.serialization.json.JsonPrimitive
import moe.fuqiuluo.qqinterface.servlet.CardSvc
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.LbsSvc
import moe.fuqiuluo.qqinterface.servlet.ark.ArkAppInfo
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
import moe.fuqiuluo.qqinterface.servlet.ark.ArkMsgSvc
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
import moe.fuqiuluo.qqinterface.servlet.transfile.*
import moe.fuqiuluo.qqinterface.servlet.transfile.FileTransfer
import moe.fuqiuluo.qqinterface.servlet.transfile.PictureResource
import moe.fuqiuluo.qqinterface.servlet.transfile.Private
import moe.fuqiuluo.qqinterface.servlet.transfile.data.PictureResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Private
import moe.fuqiuluo.qqinterface.servlet.transfile.Transfer
import moe.fuqiuluo.qqinterface.servlet.transfile.Troop
import moe.fuqiuluo.qqinterface.servlet.transfile.VideoResource
import moe.fuqiuluo.qqinterface.servlet.transfile.VoiceResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Troop
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VideoResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VoiceResource
import moe.fuqiuluo.shamrock.helper.ActionMsgException
import moe.fuqiuluo.shamrock.helper.ContactHelper
import moe.fuqiuluo.shamrock.helper.IllegalParamsException
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package moe.fuqiuluo.qqinterface.servlet.structures

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UploadResult(
@SerialName("files") val files: List<CommFileInfo>
)

@Serializable
data class CommFileInfo(
@SerialName("mode_id") val modeId: Long,
@SerialName("name") val fileName: String,
@SerialName("size") val fileSize: Long,
@SerialName("md5") val md5: String,
@SerialName("uuid") val uuid: String,
@SerialName("sub_id") val subId: String,
)
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
package moe.fuqiuluo.qqinterface.servlet.transfile

import android.graphics.BitmapFactory
import androidx.exifinterface.media.ExifInterface
import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.IOperateCallback
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.PicElement
import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
import kotlinx.atomicfu.atomic
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray
import protobuf.oidb.TrpcOidb
Expand All @@ -31,13 +46,115 @@ import protobuf.oidb.cmd0x388.Cmd0x388ReqBody
import protobuf.oidb.cmd0x388.Cmd0x388RspBody
import protobuf.oidb.cmd0x388.TryUpImgReq
import java.io.File
import kotlin.coroutines.resume
import kotlin.random.Random
import kotlin.random.nextUInt
import kotlin.random.nextULong
import kotlin.time.Duration

internal object NtV2RichMediaSvc: BaseSvc() {
private const val GROUP_PIC_UPLOAD_TO = "100000000"

private val requestIdSeq = atomic(2L)

/**
* 批量上传图片
*/
suspend fun tryUploadGroupPicByNt(
imageFiles: ArrayList<File>,
timeout: Duration
): Result<MutableList<CommonFileInfo>> {
require(imageFiles.size in 1 .. 10) { "imageFiles.size() must be in 1 .. 10" }

val messages = imageFiles.map { file ->
val elem = MsgElement()
runCatching {
elem.elementType = MsgConstant.KELEMTYPEPIC
val pic = PicElement()
pic.md5HexStr = QQNTWrapperUtil.CppProxy.genFileMd5Hex(file.absolutePath)
val msgService = NTServiceFetcher.kernelService.msgService!!
val originalPath = msgService.getRichMediaFilePathForMobileQQSend(
RichMediaFilePathInfo(
2, 0, pic.md5HexStr, file.name, 1, 0, null, "", true
)
)
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
originalPath
) != file.length()
) {
val thumbPath = msgService.getRichMediaFilePathForMobileQQSend(
RichMediaFilePathInfo(
2, 0, pic.md5HexStr, file.name, 2, 720, null, "", true
)
)
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, thumbPath)
}
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(file.absolutePath, options)
val exifInterface = ExifInterface(file.absolutePath)
val orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
if (orientation != ExifInterface.ORIENTATION_ROTATE_90 && orientation != ExifInterface.ORIENTATION_ROTATE_270) {
pic.picWidth = options.outWidth
pic.picHeight = options.outHeight
} else {
pic.picWidth = options.outHeight
pic.picHeight = options.outWidth
}
pic.sourcePath = file.absolutePath
pic.fileSize = QQNTWrapperUtil.CppProxy.getFileSize(file.absolutePath)
pic.original = true
pic.picType = FileUtils.getPicType(file)
elem.picElement = pic
}.onFailure {
LogCenter.log(it.stackTraceToString(), Level.WARN)
elem.elementType = 0
}
return@map elem
}.filter {
it.elementType == MsgConstant.KELEMTYPEPIC
}
if (messages.isEmpty()) {
return Result.failure(Exception("no valid image files"))
}
val result: MutableList<CommonFileInfo> = withTimeoutOrNull(timeout) {
suspendCancellableCoroutine {
val result = mutableListOf<CommonFileInfo>()
val uniseq = MessageHelper.generateMsgId(MsgConstant.KCHATTYPEGROUP)
val contact = Contact(MsgConstant.KCHATTYPEGROUP, GROUP_PIC_UPLOAD_TO, GROUP_PIC_UPLOAD_TO)
RichMediaUploadHandler.registerListener(uniseq.qqMsgId) upload@{
if (uniseq.qqMsgId == msgId) {
result.add(commonFileInfo)
}
return@upload false
}
MessageHelper.sendMessageWithMsgId(
contact = contact,
message = ArrayList(messages),
uniseq = uniseq.qqMsgId
) { code, _ ->
NTServiceFetcher.kernelService
.wrapperSession.msgService
.deleteMsg(contact, arrayListOf(uniseq.qqMsgId), null)
RichMediaUploadHandler.removeListener(uniseq.qqMsgId)
if (code != 110 && code != 4) {
it.resume(null)
} else {
it.resume(result)
}
}
it.invokeOnCancellation {
RichMediaUploadHandler.removeListener(uniseq.qqMsgId)
}
}
} ?: return Result.failure(Exception("timeout"))
return Result.success(result)
}

/**
* 获取NT图片的RKEY
*/
Expand Down Expand Up @@ -173,6 +290,9 @@ internal object NtV2RichMediaSvc: BaseSvc() {
LogCenter.log("requestUploadPic => rsp: $rsp")
}

/**
* 使用OldBDH获取图片上传状态以及图片上传服务器
*/
suspend fun requestUploadGroupPic(
groupId: ULong,
md5: String,
Expand Down Expand Up @@ -215,13 +335,5 @@ internal object NtV2RichMediaSvc: BaseSvc() {
)
}
}
}

@Serializable
data class TryUpPicData(
@SerialName("ukey") val uKey: ByteArray,
@SerialName("exist") val exist: Boolean,
@SerialName("file_id") val fileId: ULong,
@SerialName("up_ip") var upIp: ArrayList<Long>? = null,
@SerialName("up_port") var upPort: ArrayList<Int>? = null,
)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moe.fuqiuluo.shamrock.remote.service.api
package moe.fuqiuluo.qqinterface.servlet.transfile

import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package moe.fuqiuluo.qqinterface.servlet.transfile

import com.tencent.mobileqq.data.MessageForPic
import com.tencent.mobileqq.data.MessageForShortVideo
import com.tencent.mobileqq.data.MessageRecord
import com.tencent.mobileqq.transfile.FileMsg
import com.tencent.mobileqq.transfile.TransferRequest
import moe.fuqiuluo.shamrock.utils.MD5
import java.io.File
import moe.fuqiuluo.qqinterface.servlet.transfile.ResourceType.*
import moe.fuqiuluo.shamrock.helper.TransfileHelper
import moe.fuqiuluo.qqinterface.servlet.transfile.data.ResourceType.*
import moe.fuqiuluo.qqinterface.servlet.transfile.data.ContactType
import moe.fuqiuluo.qqinterface.servlet.transfile.data.PictureResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Resource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.ResourceType
import moe.fuqiuluo.qqinterface.servlet.transfile.data.TransTarget
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VideoResource
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VoiceResource

internal object Transfer: FileTransfer() {
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.transfile
package moe.fuqiuluo.qqinterface.servlet.transfile.data

import com.tencent.mobileqq.data.MessageRecord

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.transfile
package moe.fuqiuluo.qqinterface.servlet.transfile.data

import java.io.File

Expand Down
Loading

0 comments on commit fca66f3

Please sign in to comment.