Shamrock: support upload resource by NtKernel

Signed-off-by: 白池 <whitechi73@outlook.com>
This commit is contained in:
白池 2024-02-25 11:42:42 +08:00
parent 92ebe0c6a8
commit fca66f3259
24 changed files with 309 additions and 91 deletions

View File

@ -6,7 +6,7 @@ import java.util.ArrayList;
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);

View File

@ -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

View File

@ -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(

View File

@ -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.*

View File

@ -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,

View File

@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.ark
package moe.fuqiuluo.qqinterface.servlet.ark.data
import kotlinx.serialization.Serializable

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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,
)

View File

@ -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<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
*/
@ -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<Long>? = null,
@SerialName("up_port") var upPort: ArrayList<Int>? = null,
)
}

View File

@ -1,4 +1,4 @@
package moe.fuqiuluo.shamrock.remote.service.api
package moe.fuqiuluo.qqinterface.servlet.transfile
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo

View File

@ -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>>(

View File

@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.transfile
package moe.fuqiuluo.qqinterface.servlet.transfile.data
import com.tencent.mobileqq.data.MessageRecord

View File

@ -1,4 +1,4 @@
package moe.fuqiuluo.qqinterface.servlet.transfile
package moe.fuqiuluo.qqinterface.servlet.transfile.data
import java.io.File

View File

@ -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<Long>? = null,
@SerialName("up_port") var upPort: ArrayList<Int>? = null,
)

View File

@ -205,27 +205,24 @@ internal object MessageHelper {
fun sendMessageWithMsgId(
contact: Contact,
message: ArrayList<MsgElement>,
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(

View File

@ -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

View File

@ -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<String> = arrayOf("res_id", "peer_id", "message_type")
}

View File

@ -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
)

View File

@ -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

View File

@ -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<String> = arrayOf("file")
}

View File

@ -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

View File

@ -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<Long, suspend MsgRecord.() -> Unit>())
override fun onRecvMsg(msgList: ArrayList<MsgRecord>) {
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)
}