diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelMsgService.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelMsgService.java index ac9af0c..60d4638 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelMsgService.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelMsgService.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.HashMap; public interface IKernelMsgService { - void deleteMsg(Contact contact, ArrayList msgIdList, IOperateCallback callback); + void deleteMsg(Contact contact, ArrayList msgIdList, IOperateCallback cb); void fetchLongMsg(Contact contact, long msgId); diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/PacketSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/PacketSvc.kt index b49e026..fc99577 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/PacketSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/PacketSvc.kt @@ -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 @@ -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 diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsg.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsgSvc.kt similarity index 90% rename from xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsg.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsgSvc.kt index afeada2..0a3c47d 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsg.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkMsgSvc.kt @@ -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( diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/WeatherSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/WeatherSvc.kt index fbb904b..92ee562 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/WeatherSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/WeatherSvc.kt @@ -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.* diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkAppInfo.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/ArkAppInfo.kt similarity index 95% rename from xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkAppInfo.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/ArkAppInfo.kt index ccb5f12..21f44b5 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/ArkAppInfo.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/ArkAppInfo.kt @@ -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, diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/Region.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/Region.kt similarity index 75% rename from xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/Region.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/Region.kt index 2841abd..6f96120 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/Region.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/ark/data/Region.kt @@ -1,4 +1,4 @@ -package moe.fuqiuluo.qqinterface.servlet.ark +package moe.fuqiuluo.qqinterface.servlet.ark.data import kotlinx.serialization.Serializable diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/MessageTempHandler.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/MessageTempHandler.kt new file mode 100644 index 0000000..c2f16dd --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/MessageTempHandler.kt @@ -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 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 + } +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/ElemMaker.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/ElemMaker.kt index 2a2d855..3bed2fa 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/ElemMaker.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/ElemMaker.kt @@ -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 diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/NtMsgElementMaker.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/NtMsgElementMaker.kt index abee890..41d91a1 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/NtMsgElementMaker.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/maker/NtMsgElementMaker.kt @@ -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 @@ -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 diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/RichMedia.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/RichMedia.kt new file mode 100644 index 0000000..dc4ce58 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/structures/RichMedia.kt @@ -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 +) + +@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, +) \ 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 3d41f83..1127da4 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 @@ -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 @@ -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, + timeout: Duration + ): Result> { + 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 = withTimeoutOrNull(timeout) { + suspendCancellableCoroutine { + val result = mutableListOf() + 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 */ @@ -173,6 +290,9 @@ internal object NtV2RichMediaSvc: BaseSvc() { LogCenter.log("requestUploadPic => rsp: $rsp") } + /** + * 使用OldBDH获取图片上传状态以及图片上传服务器 + */ suspend fun requestUploadGroupPic( groupId: ULong, md5: String, @@ -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? = null, - @SerialName("up_port") var upPort: ArrayList? = null, - ) -} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/RichMediaUploadHandler.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichMediaUploadHandler.kt similarity index 92% rename from xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/RichMediaUploadHandler.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichMediaUploadHandler.kt index 196e5ff..e5f01ae 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/RichMediaUploadHandler.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichMediaUploadHandler.kt @@ -1,4 +1,4 @@ -package moe.fuqiuluo.shamrock.remote.service.api +package moe.fuqiuluo.qqinterface.servlet.transfile import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Transfer.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Transfer.kt index bdb7225..d1b7c4a 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Transfer.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Transfer.kt @@ -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 Boolean>>( diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Contact.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Contact.kt similarity index 90% rename from xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Contact.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Contact.kt index 83fbf97..382242f 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Contact.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Contact.kt @@ -1,4 +1,4 @@ -package moe.fuqiuluo.qqinterface.servlet.transfile +package moe.fuqiuluo.qqinterface.servlet.transfile.data import com.tencent.mobileqq.data.MessageRecord diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Resource.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Resource.kt similarity index 90% rename from xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Resource.kt rename to xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Resource.kt index 8f07ef9..f8d82fe 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/Resource.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/Resource.kt @@ -1,4 +1,4 @@ -package moe.fuqiuluo.qqinterface.servlet.transfile +package moe.fuqiuluo.qqinterface.servlet.transfile.data import java.io.File diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/RichMedia.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/RichMedia.kt new file mode 100644 index 0000000..18b3ef2 --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/data/RichMedia.kt @@ -0,0 +1,13 @@ +package moe.fuqiuluo.qqinterface.servlet.transfile.data + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@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? = null, + @SerialName("up_port") var upPort: ArrayList? = null, +) \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MessageHelper.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MessageHelper.kt index 9ca5a75..56a1525 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MessageHelper.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MessageHelper.kt @@ -205,27 +205,24 @@ internal object MessageHelper { fun sendMessageWithMsgId( contact: Contact, message: ArrayList, + uniseq: Long, callback: IOperateCallback ): SendMsgResult { - val uniseq = generateMsgId(contact.chatType) val nonMsg: Boolean = message.isEmpty() - return if (!nonMsg) { + if (!nonMsg) { val service = QRoute.api(IMsgService::class.java) - if (callback is MsgSvc.MessageCallback) { - callback.msgHash = uniseq.msgHashId - } - service.sendMsg( contact, - uniseq.qqMsgId, + uniseq, message, callback ) - - uniseq.copy(msgTime = System.currentTimeMillis()) - } else { - uniseq.copy(msgTime = 0, msgHashId = 0) } + return SendMsgResult( + msgTime = if (nonMsg) 0 else System.currentTimeMillis(), + msgHashId = 0, + qqMsgId = uniseq + ) } suspend fun sendMessageNoCb( diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MusicHelper.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MusicHelper.kt index 751f612..f221723 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MusicHelper.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/helper/MusicHelper.kt @@ -3,7 +3,7 @@ package moe.fuqiuluo.shamrock.helper import io.ktor.client.request.get import io.ktor.client.statement.bodyAsText import kotlinx.serialization.json.Json -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.shamrock.tools.GlobalClient import moe.fuqiuluo.shamrock.tools.asInt diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendMsgByResid.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendMsgByResid.kt index 8207ee4..60b53bf 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendMsgByResid.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendMsgByResid.kt @@ -24,8 +24,7 @@ internal object SendMsgByResid : IActionHandler() { val resId = session.getString("res_id") val peerId = session.getString("peer_id") val messageType = session.getString("message_type") - invoke(resId, peerId, messageType) - return ok("ok", session.echo) + return invoke(peerId, resId, messageType, session.echo) } suspend operator fun invoke(peerId: String, resId: String, messageType: String, echo: JsonElement = EmptyJsonString): String { @@ -55,4 +54,6 @@ internal object SendMsgByResid : IActionHandler() { BaseSvc.sendBufferAW("MessageSvc.PbSendMsg", true, req.toByteArray()) return ok("ok", echo) } + + override val requiredParams: Array = arrayOf("res_id", "peer_id", "message_type") } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendPrivateMessage.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendPrivateMessage.kt index 98ad3eb..fcd355b 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendPrivateMessage.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/SendPrivateMessage.kt @@ -1,15 +1,18 @@ package moe.fuqiuluo.shamrock.remote.action.handlers import com.tencent.qqnt.kernel.nativeinterface.MsgConstant +import moe.fuqiuluo.qqinterface.servlet.TicketSvc import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler import moe.fuqiuluo.shamrock.tools.jsonArray import moe.fuqiuluo.symbols.OneBotHandler -@OneBotHandler("send_private_msg", ["send_private_message"]) +@OneBotHandler("send_private_msg", ["send_private_message", "send_friend_msg"]) internal object SendPrivateMessage : IActionHandler() { override suspend fun internalHandle(session: ActionSession): String { - val userId = session.getLong("user_id") + val userId = session.getString("user_id").let { + if (it == "self") TicketSvc.getUin() else it + } val groupId = session.getLongOrNull("group_id") val chatType = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP val retryCnt = session.getIntOrNull("retry_cnt") @@ -19,21 +22,21 @@ internal object SendPrivateMessage : IActionHandler() { val message = session.getString("message") SendMessage( chatType = chatType, - peerId = userId.toString(), + peerId = userId, message = message, autoEscape = autoEscape, echo = session.echo, - fromId = groupId?.toString() ?: userId.toString(), + fromId = groupId?.toString() ?: userId, retryCnt = retryCnt ?: 5, recallDuration = recallDuration ) } else { SendMessage( chatType = chatType, - peerId = userId.toString(), + peerId = userId, message = if (session.isArray("message")) session.getArray("message") else listOf(session.getObject("message")).jsonArray, echo = session.echo, - fromId = groupId?.toString() ?: userId.toString(), + fromId = groupId?.toString() ?: userId, retryCnt = retryCnt ?: 5, recallDuration = recallDuration ) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupFile.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupFile.kt index ef5f423..b39042d 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupFile.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupFile.kt @@ -22,7 +22,7 @@ import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.TransfileHelper import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler -import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler +import moe.fuqiuluo.qqinterface.servlet.transfile.RichMediaUploadHandler import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.MD5 diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupPic.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupPic.kt new file mode 100644 index 0000000..d129c8f --- /dev/null +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadGroupPic.kt @@ -0,0 +1,63 @@ +package moe.fuqiuluo.shamrock.remote.action.handlers + +import kotlinx.serialization.json.JsonElement +import moe.fuqiuluo.qqinterface.servlet.structures.CommFileInfo +import moe.fuqiuluo.qqinterface.servlet.structures.UploadResult +import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc +import moe.fuqiuluo.shamrock.remote.action.ActionSession +import moe.fuqiuluo.shamrock.remote.action.IActionHandler +import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig +import moe.fuqiuluo.shamrock.tools.EmptyJsonString +import moe.fuqiuluo.shamrock.utils.FileUtils +import moe.fuqiuluo.symbols.OneBotHandler +import kotlin.time.Duration.Companion.seconds + +@OneBotHandler("upload_group_image", ["upload_group_pic"]) +internal object UploadGroupPic: IActionHandler() { + override suspend fun internalHandle(session: ActionSession): String { + val pic = session.getString("file") + return invoke(pic, session.echo) + } + + suspend operator fun invoke( + picture: String, + echo: JsonElement = EmptyJsonString + ): String { + if (ShamrockConfig.isDev()) { + val file = picture.let { + val md5 = it.replace( + regex = "[{}\\-]".toRegex(), + replacement = "" + ).split(".")[0].lowercase() + if (md5.length == 32) { + FileUtils.getFileByMd5(it) + } else { + FileUtils.parseAndSave(it) + } + } + if (!file.exists()) { + return logic("picture file is not exists", echo) + } + NtV2RichMediaSvc.tryUploadGroupPicByNt( + imageFiles = arrayListOf(file), + timeout = 30.seconds + ).onSuccess { + return ok(UploadResult(it.map { + CommFileInfo( + modeId = it.fileModelId, + fileName = it.fileName, + fileSize = it.fileSize, + md5 = it.md5, + uuid = it.uuid, + subId = it.subId + ) + }), echo) + }.onFailure { + return logic("upload failed: ${it.message ?: it.toString()}", echo) + } + } + return logic("upload failed", echo) + } + + override val requiredParams: Array = arrayOf("file") +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadPrivateFile.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadPrivateFile.kt index e475b19..99c6d3b 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadPrivateFile.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/UploadPrivateFile.kt @@ -14,16 +14,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement -import moe.fuqiuluo.shamrock.helper.ContactHelper import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.TransfileHelper import moe.fuqiuluo.shamrock.remote.action.ActionSession import moe.fuqiuluo.shamrock.remote.action.IActionHandler -import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler +import moe.fuqiuluo.qqinterface.servlet.transfile.RichMediaUploadHandler import moe.fuqiuluo.shamrock.tools.EmptyJsonString import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.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 bfcac03..ffcf7e2 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 @@ -9,6 +9,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import moe.fuqiuluo.qqinterface.servlet.MsgSvc import moe.fuqiuluo.qqinterface.servlet.TicketSvc +import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler import moe.fuqiuluo.qqinterface.servlet.msg.toCQCode import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig @@ -16,7 +17,7 @@ import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.db.MessageDB import moe.fuqiuluo.shamrock.remote.service.api.GlobalEventTransmitter -import moe.fuqiuluo.shamrock.remote.service.api.RichMediaUploadHandler +import moe.fuqiuluo.qqinterface.servlet.transfile.RichMediaUploadHandler import moe.fuqiuluo.shamrock.remote.service.data.push.MessageTempSource import moe.fuqiuluo.shamrock.remote.service.data.push.PostType import java.util.ArrayList @@ -24,9 +25,6 @@ import java.util.Collections import kotlin.collections.HashMap internal object AioListener : IKernelMsgListener { - // 通过MSG SEQ临时监听器 - private val tempMessageListenerMap = Collections.synchronizedMap(HashMap Unit>()) - override fun onRecvMsg(msgList: ArrayList) { if (msgList.isEmpty()) return @@ -37,27 +35,9 @@ internal object AioListener : IKernelMsgListener { } } - fun registerTemporaryMsgListener( - msgSeq: Long, - listener: suspend MsgRecord.() -> Unit - ) { - LogCenter.log({ "注册临时消息监听器: $msgSeq" }, Level.DEBUG) - tempMessageListenerMap[msgSeq] = listener - } - - fun unregisterTemporaryMsgListener(msgSeq: Long) { - tempMessageListenerMap.remove(msgSeq) - } - private suspend fun handleMsg(record: MsgRecord) { try { - tempMessageListenerMap.firstNotNullOfOrNull { - if (it.key == record.msgSeq) it else null - }?.let { - it.value(record) - tempMessageListenerMap.remove(it.key) - return - } + if (MessageTempHandler.notify(record)) return if (record.msgSeq < 0) return val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId) @@ -432,7 +412,7 @@ internal object AioListener : IKernelMsgListener { } override fun onRichMediaUploadComplete(notifyInfo: FileTransNotifyInfo) { - LogCenter.log("onRichMediaUploadComplete($notifyInfo)", Level.DEBUG) + LogCenter.log("[BDH] 资源上传完成(${notifyInfo.trasferStatus}, ${notifyInfo.fileId}, ${notifyInfo.msgId}, ${notifyInfo.commonFileInfo})") RichMediaUploadHandler.notify(notifyInfo) }