mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
6 Commits
92ebe0c6a8
...
252a3527a8
Author | SHA1 | Date | |
---|---|---|---|
252a3527a8 | |||
ea4cf06edf | |||
1424efd7f8 | |||
eb807a0332 | |||
e9a3a82b68 | |||
fca66f3259 |
@ -39,11 +39,26 @@ data class CustomFace(
|
|||||||
@ProtoNumber(32) var width400: UInt? = null,
|
@ProtoNumber(32) var width400: UInt? = null,
|
||||||
@ProtoNumber(33) var height400: UInt? = null,
|
@ProtoNumber(33) var height400: UInt? = null,
|
||||||
@ProtoNumber(34) var pbReserve: PbReserve? = null,
|
@ProtoNumber(34) var pbReserve: PbReserve? = null,
|
||||||
){
|
) {
|
||||||
companion object{
|
companion object {
|
||||||
@Serializable
|
@Serializable
|
||||||
data class PbReserve(
|
data class PbReserve(
|
||||||
@ProtoNumber(1) var field1: Int? = null
|
@ProtoNumber(1) var field1: Int? = null,
|
||||||
|
@ProtoNumber(3) var field3: Int? = null,
|
||||||
|
@ProtoNumber(4) var field4: Int? = null,
|
||||||
|
@ProtoNumber(10) var field10: Int? = null,
|
||||||
|
@ProtoNumber(21) var field21: Object1? = null,
|
||||||
|
@ProtoNumber(31) var field31: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Object1(
|
||||||
|
@ProtoNumber(1) var field1: Int? = null,
|
||||||
|
@ProtoNumber(2) var field2: String? = null,
|
||||||
|
@ProtoNumber(3) var field3: Int? = null,
|
||||||
|
@ProtoNumber(4) var field4: Int? = null,
|
||||||
|
@ProtoNumber(5) var field5: Int? = null,
|
||||||
|
@ProtoNumber(7) var md5Str: String? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,16 +5,16 @@ import kotlinx.serialization.protobuf.ProtoNumber
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class NotOnlineImage(
|
data class NotOnlineImage(
|
||||||
@ProtoNumber(1) val filePath: ByteArray? = null,
|
@ProtoNumber(1) val filePath: String? = null,
|
||||||
@ProtoNumber(2) val fileLen: UInt? = null,
|
@ProtoNumber(2) val fileLen: UInt? = null,
|
||||||
@ProtoNumber(3) val downloadPath: ByteArray? = null,
|
@ProtoNumber(3) val downloadPath: String? = null,
|
||||||
@ProtoNumber(4) val oldVerSendFile: ByteArray? = null,
|
@ProtoNumber(4) val oldVerSendFile: ByteArray? = null,
|
||||||
@ProtoNumber(5) val imgType: UInt? = null,
|
@ProtoNumber(5) val imgType: UInt? = null,
|
||||||
@ProtoNumber(6) val previewsImage: ByteArray? = null,
|
@ProtoNumber(6) val previewsImage: ByteArray? = null,
|
||||||
@ProtoNumber(7) val picMd5: ByteArray? = null,
|
@ProtoNumber(7) val picMd5: ByteArray? = null,
|
||||||
@ProtoNumber(8) val picHeight: UInt? = null,
|
@ProtoNumber(8) val picHeight: UInt? = null,
|
||||||
@ProtoNumber(9) val picWidth: UInt? = null,
|
@ProtoNumber(9) val picWidth: UInt? = null,
|
||||||
@ProtoNumber(10) val resId: ByteArray? = null, // md5 + ".jpg"
|
@ProtoNumber(10) val resId: String? = null, // md5 + ".jpg"
|
||||||
@ProtoNumber(11) val flag: ByteArray? = null,
|
@ProtoNumber(11) val flag: ByteArray? = null,
|
||||||
@ProtoNumber(12) val thumbUrl: String? = null,
|
@ProtoNumber(12) val thumbUrl: String? = null,
|
||||||
@ProtoNumber(13) val original: Boolean? = null,
|
@ProtoNumber(13) val original: Boolean? = null,
|
||||||
@ -39,8 +39,23 @@ data class NotOnlineImage(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class PbReserve(
|
data class PbReserve(
|
||||||
@ProtoNumber(1) var field1: Int? = null,
|
@ProtoNumber(1) var field1: Int? = null,
|
||||||
|
@ProtoNumber(3) var field3: Int? = null,
|
||||||
|
@ProtoNumber(4) var field4: Int? = null,
|
||||||
@ProtoNumber(8) var field8: String? = null,
|
@ProtoNumber(8) var field8: String? = null,
|
||||||
@ProtoNumber(30) var url: String? = null
|
@ProtoNumber(10) var field10: Int? = null,
|
||||||
|
@ProtoNumber(20) var field20: Object1? = null,
|
||||||
|
@ProtoNumber(30) var url: String? = null,
|
||||||
|
@ProtoNumber(31) var md5Str: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Object1(
|
||||||
|
@ProtoNumber(1) var field1: Int? = null,
|
||||||
|
@ProtoNumber(2) var field2: String? = null,
|
||||||
|
@ProtoNumber(3) var field3: Int? = null,
|
||||||
|
@ProtoNumber(4) var field4: Int? = null,
|
||||||
|
@ProtoNumber(5) var field5: Int? = null,
|
||||||
|
@ProtoNumber(7) var field7: String? = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public interface IKernelMsgService {
|
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);
|
void fetchLongMsg(Contact contact, long msgId);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import io.ktor.utils.io.core.writeFully
|
|||||||
import io.ktor.utils.io.core.writeInt
|
import io.ktor.utils.io.core.writeInt
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.msg.MessageTempHandler
|
||||||
|
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
|
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
|
||||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
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())
|
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, msgPush.toByteArray())
|
||||||
return withTimeoutOrNull(5000L) {
|
return withTimeoutOrNull(5000L) {
|
||||||
suspendCancellableCoroutine {
|
suspendCancellableCoroutine {
|
||||||
AioListener.registerTemporaryMsgListener(msgSeq) {
|
MessageTempHandler.registerTemporaryMsgListener(msgSeq) {
|
||||||
it.resume(this.msgId)
|
it.resume(this.msgId)
|
||||||
}
|
}
|
||||||
it.invokeOnCancellation {
|
it.invokeOnCancellation {
|
||||||
AioListener.unregisterTemporaryMsgListener(msgSeq)
|
MessageTempHandler.unregisterTemporaryMsgListener(msgSeq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: -1L
|
} ?: -1L
|
||||||
|
@ -1,16 +1,9 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.ark
|
package moe.fuqiuluo.qqinterface.servlet.ark
|
||||||
|
|
||||||
import com.tencent.mobileqq.pb.ByteStringMicro
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
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.BaseSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
import moe.fuqiuluo.qqinterface.servlet.ark.data.ArkAppInfo
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
|
||||||
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
|
|
||||||
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
|
import tencent.im.oidb.cmd0xb77.oidb_cmd0xb77
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
internal object ArkMsgSvc: BaseSvc() {
|
internal object ArkMsgSvc: BaseSvc() {
|
||||||
fun tryShareMusic(
|
fun tryShareMusic(
|
@ -6,10 +6,10 @@ import io.ktor.client.request.url
|
|||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.encodeURLQueryComponent
|
import io.ktor.http.encodeURLQueryComponent
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
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.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.ark
|
package moe.fuqiuluo.qqinterface.servlet.ark.data
|
||||||
|
|
||||||
sealed class ArkAppInfo(
|
sealed class ArkAppInfo(
|
||||||
val appId: Long,
|
val appId: Long,
|
||||||
val version: String,
|
val version: String,
|
@ -1,4 +1,4 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.ark
|
package moe.fuqiuluo.qqinterface.servlet.ark.data
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
@ -2,7 +2,7 @@ package moe.fuqiuluo.qqinterface.servlet.msg
|
|||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.converter.ElemConverter
|
import moe.fuqiuluo.qqinterface.servlet.msg.converter.ElemConverter
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.converter.MsgElementConverter
|
import moe.fuqiuluo.qqinterface.servlet.msg.converter.NtMsgElementConverter
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
@ -56,7 +56,7 @@ internal suspend fun List<MsgElement>.toSegments(chatType: Int, peerId: String,
|
|||||||
val messageData = arrayListOf<MessageSegment>()
|
val messageData = arrayListOf<MessageSegment>()
|
||||||
this.forEach { msg ->
|
this.forEach { msg ->
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val converter = MsgElementConverter[msg.elementType]
|
val converter = NtMsgElementConverter[msg.elementType]
|
||||||
converter?.invoke(chatType, peerId, subPeer, msg)
|
converter?.invoke(chatType, peerId, subPeer, msg)
|
||||||
?: throw UnsupportedOperationException("不支持的消息element类型:${msg.elementType}")
|
?: throw UnsupportedOperationException("不支持的消息element类型:${msg.elementType}")
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -21,23 +21,23 @@ import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
|||||||
|
|
||||||
internal typealias IMsgElementConverter = suspend (Int, String, String, MsgElement) -> MessageSegment
|
internal typealias IMsgElementConverter = suspend (Int, String, String, MsgElement) -> MessageSegment
|
||||||
|
|
||||||
internal object MsgElementConverter {
|
internal object NtMsgElementConverter {
|
||||||
private val convertMap = hashMapOf(
|
private val convertMap = hashMapOf(
|
||||||
MsgConstant.KELEMTYPETEXT to MsgElementConverter::convertTextElem,
|
MsgConstant.KELEMTYPETEXT to NtMsgElementConverter::convertTextElem,
|
||||||
MsgConstant.KELEMTYPEFACE to MsgElementConverter::convertFaceElem,
|
MsgConstant.KELEMTYPEFACE to NtMsgElementConverter::convertFaceElem,
|
||||||
MsgConstant.KELEMTYPEPIC to MsgElementConverter::convertImageElem,
|
MsgConstant.KELEMTYPEPIC to NtMsgElementConverter::convertImageElem,
|
||||||
MsgConstant.KELEMTYPEPTT to MsgElementConverter::convertVoiceElem,
|
MsgConstant.KELEMTYPEPTT to NtMsgElementConverter::convertVoiceElem,
|
||||||
MsgConstant.KELEMTYPEVIDEO to MsgElementConverter::convertVideoElem,
|
MsgConstant.KELEMTYPEVIDEO to NtMsgElementConverter::convertVideoElem,
|
||||||
MsgConstant.KELEMTYPEMARKETFACE to MsgElementConverter::convertMarketFaceElem,
|
MsgConstant.KELEMTYPEMARKETFACE to NtMsgElementConverter::convertMarketFaceElem,
|
||||||
MsgConstant.KELEMTYPEARKSTRUCT to MsgElementConverter::convertStructJsonElem,
|
MsgConstant.KELEMTYPEARKSTRUCT to NtMsgElementConverter::convertStructJsonElem,
|
||||||
MsgConstant.KELEMTYPEREPLY to MsgElementConverter::convertReplyElem,
|
MsgConstant.KELEMTYPEREPLY to NtMsgElementConverter::convertReplyElem,
|
||||||
MsgConstant.KELEMTYPEGRAYTIP to MsgElementConverter::convertGrayTipsElem,
|
MsgConstant.KELEMTYPEGRAYTIP to NtMsgElementConverter::convertGrayTipsElem,
|
||||||
MsgConstant.KELEMTYPEFILE to MsgElementConverter::convertFileElem,
|
MsgConstant.KELEMTYPEFILE to NtMsgElementConverter::convertFileElem,
|
||||||
MsgConstant.KELEMTYPEMARKDOWN to MsgElementConverter::convertMarkdownElem,
|
MsgConstant.KELEMTYPEMARKDOWN to NtMsgElementConverter::convertMarkdownElem,
|
||||||
//MsgConstant.KELEMTYPEMULTIFORWARD to MsgElementConverter::convertXmlMultiMsgElem,
|
//MsgConstant.KELEMTYPEMULTIFORWARD to MsgElementConverter::convertXmlMultiMsgElem,
|
||||||
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MsgElementConverter::convertXmlLongMsgElem,
|
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MsgElementConverter::convertXmlLongMsgElem,
|
||||||
MsgConstant.KELEMTYPEFACEBUBBLE to MsgElementConverter::convertBubbleFaceElem,
|
MsgConstant.KELEMTYPEFACEBUBBLE to NtMsgElementConverter::convertBubbleFaceElem,
|
||||||
MsgConstant.KELEMTYPEINLINEKEYBOARD to MsgElementConverter::convertInlineKeyboardElem
|
MsgConstant.KELEMTYPEINLINEKEYBOARD to NtMsgElementConverter::convertInlineKeyboardElem
|
||||||
)
|
)
|
||||||
|
|
||||||
operator fun get(type: Int): IMsgElementConverter? = convertMap[type]
|
operator fun get(type: Int): IMsgElementConverter? = convertMap[type]
|
@ -2,7 +2,7 @@ package moe.fuqiuluo.qqinterface.servlet.msg.maker
|
|||||||
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.*
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
import moe.fuqiuluo.qqinterface.servlet.CardSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||||
@ -11,23 +11,12 @@ import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
|||||||
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toJson
|
import moe.fuqiuluo.qqinterface.servlet.msg.toJson
|
||||||
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
import moe.fuqiuluo.qqinterface.servlet.msg.toSegments
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.*
|
import moe.fuqiuluo.qqinterface.servlet.transfile.NtV2RichMediaSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.PictureResource
|
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.Private
|
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.Transfer
|
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.Troop
|
|
||||||
import moe.fuqiuluo.shamrock.helper.*
|
import moe.fuqiuluo.shamrock.helper.*
|
||||||
import moe.fuqiuluo.shamrock.helper.ActionMsgException
|
|
||||||
import moe.fuqiuluo.shamrock.helper.Level
|
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
|
||||||
import moe.fuqiuluo.shamrock.helper.LogicException
|
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper.messageArrayToMessageElements
|
import moe.fuqiuluo.shamrock.helper.MessageHelper.messageArrayToMessageElements
|
||||||
import moe.fuqiuluo.shamrock.helper.ParamsException
|
|
||||||
import moe.fuqiuluo.shamrock.tools.*
|
import moe.fuqiuluo.shamrock.tools.*
|
||||||
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
import moe.fuqiuluo.shamrock.utils.DeflateTools
|
||||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
|
||||||
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
import protobuf.message.Elem
|
import protobuf.message.Elem
|
||||||
import protobuf.message.element.*
|
import protobuf.message.element.*
|
||||||
@ -36,8 +25,9 @@ import java.io.File
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextULong
|
import kotlin.random.nextULong
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
internal typealias IMessageElementMaker = suspend (Int, Long, String, JsonObject) -> Result<Elem>
|
internal typealias IElemMaker = suspend (Int, Long, String, JsonObject) -> Result<Elem>
|
||||||
|
|
||||||
internal object ElemMaker {
|
internal object ElemMaker {
|
||||||
private val makerArray = hashMapOf(
|
private val makerArray = hashMapOf(
|
||||||
@ -46,30 +36,30 @@ internal object ElemMaker {
|
|||||||
"face" to ElemMaker::createFaceElem,
|
"face" to ElemMaker::createFaceElem,
|
||||||
"pic" to ElemMaker::createImageElem,
|
"pic" to ElemMaker::createImageElem,
|
||||||
"image" to ElemMaker::createImageElem,
|
"image" to ElemMaker::createImageElem,
|
||||||
// "voice" to MessageElementMaker::createRecordElem,
|
// "voice" to ElemMaker::createRecordElem,
|
||||||
// "record" to MessageElementMaker::createRecordElem,
|
// "record" to ElemMaker::createRecordElem,
|
||||||
// "video" to MessageElementMaker::createVideoElem,
|
// "video" to ElemMaker::createVideoElem,
|
||||||
"markdown" to ElemMaker::createMarkdownElem,
|
"markdown" to ElemMaker::createMarkdownElem,
|
||||||
"button" to ElemMaker::createButtonElem,
|
"button" to ElemMaker::createButtonElem,
|
||||||
"inline_keyboard" to ElemMaker::createButtonElem,
|
"inline_keyboard" to ElemMaker::createButtonElem,
|
||||||
"dice" to ElemMaker::createNewDiceElem,
|
"dice" to ElemMaker::createNewDiceElem,
|
||||||
"rps" to ElemMaker::createNewRpsElem,
|
"rps" to ElemMaker::createNewRpsElem,
|
||||||
"poke" to ElemMaker::createPokeElem,
|
"poke" to ElemMaker::createPokeElem,
|
||||||
// "anonymous" to MessageElementMaker::createAnonymousElem,
|
// "anonymous" to ElemMaker::createAnonymousElem,
|
||||||
// "share" to MessageElementMaker::createShareElem,
|
// "share" to ElemMaker::createShareElem,
|
||||||
// "contact" to MessageElementMaker::createContactElem,
|
// "contact" to ElemMaker::createContactElem,
|
||||||
// "location" to MessageElementMaker::createLocationElem,
|
// "location" to ElemMaker::createLocationElem,
|
||||||
// "music" to MessageElementMaker::createMusicElem,
|
// "music" to ElemMaker::createMusicElem,
|
||||||
"reply" to ElemMaker::createReplyElem,
|
"reply" to ElemMaker::createReplyElem,
|
||||||
// "touch" to MessageElementMaker::createTouchElem,
|
// "touch" to ElemMaker::createTouchElem,
|
||||||
"weather" to ElemMaker::createWeatherElem,
|
"weather" to ElemMaker::createWeatherElem,
|
||||||
"json" to ElemMaker::createJsonElem,
|
"json" to ElemMaker::createJsonElem,
|
||||||
// "node" to MessageMaker::createNodeElem,
|
//"forward" to MessageMaker::createForwardElem,
|
||||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||||
//"bubble_face" to MessageElementMaker::createBubbleFaceElem,
|
//"bubble_face" to ElemMaker::createBubbleFaceElem,
|
||||||
)
|
)
|
||||||
|
|
||||||
operator fun get(type: String): IMessageElementMaker? = makerArray[type]
|
operator fun get(type: String): IElemMaker? = makerArray[type]
|
||||||
|
|
||||||
private suspend fun createTextElem(
|
private suspend fun createTextElem(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
@ -226,26 +216,6 @@ internal object ElemMaker {
|
|||||||
}
|
}
|
||||||
requireNotNull(file)
|
requireNotNull(file)
|
||||||
|
|
||||||
val md5HexStr = QQNTWrapperUtil.CppProxy.genFileMd5Hex(file.absolutePath)
|
|
||||||
val msgService = NTServiceFetcher.kernelService.msgService!!
|
|
||||||
val originalPath = msgService.getRichMediaFilePathForMobileQQSend(
|
|
||||||
RichMediaFilePathInfo(
|
|
||||||
2, 0, 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, md5HexStr, file.name, 2, 720, null, "", true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
|
||||||
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, thumbPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
val options = BitmapFactory.Options()
|
val options = BitmapFactory.Options()
|
||||||
options.inJustDecodeBounds = true
|
options.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeFile(file.absolutePath, options)
|
BitmapFactory.decodeFile(file.absolutePath, options)
|
||||||
@ -264,53 +234,77 @@ internal object ElemMaker {
|
|||||||
picHeight = options.outWidth
|
picHeight = options.outWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val uploadRet = NtV2RichMediaSvc.tryUploadResourceByNt(
|
||||||
|
chatType = chatType,
|
||||||
|
elementType = MsgConstant.KELEMTYPEPIC,
|
||||||
|
resources = arrayListOf(file),
|
||||||
|
timeout = 30.seconds
|
||||||
|
).getOrThrow().first()
|
||||||
|
LogCenter.log(uploadRet.toString(), Level.DEBUG)
|
||||||
|
|
||||||
val elem = when (chatType) {
|
val elem = when (chatType) {
|
||||||
MsgConstant.KCHATTYPEGROUP -> {
|
MsgConstant.KCHATTYPEGROUP -> Elem(
|
||||||
Transfer with Troop(peerId) trans PictureResource(file)
|
|
||||||
Elem(
|
|
||||||
customFace = CustomFace(
|
customFace = CustomFace(
|
||||||
filePath = "${md5HexStr.substring(0, 8)}-${md5HexStr.substring(8, 4)}-${
|
filePath = uploadRet.fileName,
|
||||||
md5HexStr.substring(
|
fileId = uploadRet.uuid.toUInt(),
|
||||||
12,
|
|
||||||
4
|
|
||||||
)
|
|
||||||
}-${md5HexStr.substring(16, 4)}-${md5HexStr.substring(20, 12)}.${FileUtils.getFileType(file)}",
|
|
||||||
fileId = 0u,
|
|
||||||
serverIp = 0u,
|
serverIp = 0u,
|
||||||
serverPort = 0u,
|
serverPort = 0u,
|
||||||
fileType = 1001u,
|
fileType = FileUtils.getPicType(file).toUInt(),
|
||||||
useful = 1u,
|
useful = 1u,
|
||||||
md5 = md5HexStr.hex2ByteArray(),
|
md5 = uploadRet.md5.hex2ByteArray(),
|
||||||
bizType = data["subType"].asIntOrNull?.toUInt(),
|
bizType = data["subType"].asIntOrNull?.toUInt(),
|
||||||
imageType = FileUtils.getPicType(file).toUInt(),
|
imageType = FileUtils.getPicType(file).toUInt(),
|
||||||
width = picWidth.toUInt(),
|
width = picWidth.toUInt(),
|
||||||
height = picHeight.toUInt(),
|
height = picHeight.toUInt(),
|
||||||
size = QQNTWrapperUtil.CppProxy.getFileSize(file.absolutePath).toUInt(),
|
size = uploadRet.fileSize.toUInt(),
|
||||||
origin = isOriginal,
|
origin = isOriginal,
|
||||||
thumbWidth = 0u,
|
thumbWidth = 0u,
|
||||||
thumbHeight = 0u,
|
thumbHeight = 0u,
|
||||||
pbReserve = CustomFace.Companion.PbReserve(field1 = 0)
|
pbReserve = CustomFace.Companion.PbReserve(
|
||||||
|
field1 = 0,
|
||||||
|
field3 = 0,
|
||||||
|
field4 = 0,
|
||||||
|
field10 = 0,
|
||||||
|
field21 = CustomFace.Companion.Object1(
|
||||||
|
field1 = 0,
|
||||||
|
field2 = "",
|
||||||
|
field3 = 0,
|
||||||
|
field4 = 0,
|
||||||
|
field5 = 0,
|
||||||
|
md5Str = uploadRet.md5
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
MsgConstant.KCHATTYPEC2C -> {
|
MsgConstant.KCHATTYPEC2C -> Elem(
|
||||||
Transfer with Private(peerId) trans PictureResource(file)
|
|
||||||
Elem(
|
|
||||||
notOnlineImage = NotOnlineImage(
|
notOnlineImage = NotOnlineImage(
|
||||||
filePath = "${md5HexStr}.${FileUtils.getFileType(file)}".toByteArray(),
|
filePath = uploadRet.fileName,
|
||||||
fileLen = QQNTWrapperUtil.CppProxy.getFileSize(file.absolutePath).toUInt(),
|
fileLen = uploadRet.fileSize.toUInt(),
|
||||||
downloadPath = "".toByteArray(),
|
downloadPath = uploadRet.uuid,
|
||||||
imgType = FileUtils.getPicType(file).toUInt(),
|
imgType = FileUtils.getPicType(file).toUInt(),
|
||||||
picMd5 = md5HexStr.hex2ByteArray(),
|
picMd5 = uploadRet.md5.hex2ByteArray(),
|
||||||
picHeight = picWidth.toUInt(),
|
picHeight = picWidth.toUInt(),
|
||||||
picWidth = picHeight.toUInt(),
|
picWidth = picHeight.toUInt(),
|
||||||
resId = "".toByteArray(),
|
resId = uploadRet.uuid,
|
||||||
original = isOriginal, // true
|
original = isOriginal, // true
|
||||||
pbReserve = NotOnlineImage.Companion.PbReserve(field1 = 0)
|
pbReserve = NotOnlineImage.Companion.PbReserve(
|
||||||
|
field1 = 0,
|
||||||
|
field3 = 0,
|
||||||
|
field4 = 0,
|
||||||
|
field10 = 0,
|
||||||
|
field20 = NotOnlineImage.Companion.Object1(
|
||||||
|
field1 = 0,
|
||||||
|
field2 = "",
|
||||||
|
field3 = 0,
|
||||||
|
field4 = 0,
|
||||||
|
field5 = 0,
|
||||||
|
field7 = "",
|
||||||
|
),
|
||||||
|
md5Str = uploadRet.md5
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
else -> throw LogicException("Not supported chatType($chatType) for PictureMsg")
|
else -> throw LogicException("Not supported chatType($chatType) for PictureMsg")
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package moe.fuqiuluo.qqinterface.servlet.msg.maker
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import com.tencent.mobileqq.app.QQAppInterface
|
import com.tencent.mobileqq.app.QQAppInterface
|
||||||
import com.tencent.mobileqq.data.MessageForPic
|
|
||||||
import com.tencent.mobileqq.emoticon.QQSysFaceUtil
|
import com.tencent.mobileqq.emoticon.QQSysFaceUtil
|
||||||
import com.tencent.mobileqq.pb.ByteStringMicro
|
import com.tencent.mobileqq.pb.ByteStringMicro
|
||||||
import com.tencent.mobileqq.qroute.QRoute
|
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.CardSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.LbsSvc
|
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.ArkMsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
import moe.fuqiuluo.qqinterface.servlet.ark.WeatherSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.*
|
import moe.fuqiuluo.qqinterface.servlet.transfile.*
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.FileTransfer
|
import moe.fuqiuluo.qqinterface.servlet.transfile.FileTransfer
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.PictureResource
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.PictureResource
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.Private
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Private
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.Transfer
|
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.qqinterface.servlet.transfile.VideoResource
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VideoResource
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.VoiceResource
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VoiceResource
|
||||||
import moe.fuqiuluo.shamrock.helper.ActionMsgException
|
import moe.fuqiuluo.shamrock.helper.ActionMsgException
|
||||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
||||||
import moe.fuqiuluo.shamrock.helper.IllegalParamsException
|
import moe.fuqiuluo.shamrock.helper.IllegalParamsException
|
||||||
@ -84,7 +83,6 @@ internal object NtMsgElementMaker {
|
|||||||
"new_dice" to NtMsgElementMaker::createNewDiceElem,
|
"new_dice" to NtMsgElementMaker::createNewDiceElem,
|
||||||
"new_rps" to NtMsgElementMaker::createNewRpsElem,
|
"new_rps" to NtMsgElementMaker::createNewRpsElem,
|
||||||
"basketball" to NtMsgElementMaker::createBasketballElem,
|
"basketball" to NtMsgElementMaker::createBasketballElem,
|
||||||
//"node" to MessageMaker::createNodeElem,
|
|
||||||
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
//"multi_msg" to MessageMaker::createLongMsgStruct,
|
||||||
"bubble_face" to NtMsgElementMaker::createBubbleFaceElem,
|
"bubble_face" to NtMsgElementMaker::createBubbleFaceElem,
|
||||||
"button" to NtMsgElementMaker::createInlineKeywordElem,
|
"button" to NtMsgElementMaker::createInlineKeywordElem,
|
||||||
@ -180,17 +178,6 @@ internal object NtMsgElementMaker {
|
|||||||
return Result.success(elem)
|
return Result.success(elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
// private suspend fun createNodeElem(
|
|
||||||
// chatType: Int,
|
|
||||||
// msgId: Long,
|
|
||||||
// peerId: String,
|
|
||||||
// data: JsonObject
|
|
||||||
// ): Result<MsgElement> {
|
|
||||||
// data.checkAndThrow("data")
|
|
||||||
// SendForwardMessage(MsgConstant.KCHATTYPEC2C, TicketSvc.getUin(), data["content"].asJsonArray)
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
private suspend fun createBasketballElem(
|
private suspend fun createBasketballElem(
|
||||||
chatType: Int,
|
chatType: Int,
|
||||||
msgId: Long,
|
msgId: Long,
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
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,
|
||||||
|
@SerialName("sha") val sha: String,
|
||||||
|
)
|
@ -1,14 +1,46 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.media.MediaMetadataRetriever
|
||||||
|
import androidx.exifinterface.media.ExifInterface
|
||||||
|
import com.tencent.mobileqq.qroute.QRoute
|
||||||
|
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.CommonFileInfo
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.Contact
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.FileElement
|
||||||
|
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.PttElement
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.VideoElement
|
||||||
|
import com.tencent.qqnt.msg.api.IMsgUtilApi
|
||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Private
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.Troop
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.TryUpPicData
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.VideoResource
|
||||||
|
import moe.fuqiuluo.shamrock.helper.Level
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
|
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
||||||
|
import moe.fuqiuluo.shamrock.helper.TransfileHelper
|
||||||
|
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
||||||
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
|
||||||
import moe.fuqiuluo.shamrock.tools.slice
|
import moe.fuqiuluo.shamrock.tools.slice
|
||||||
|
import moe.fuqiuluo.shamrock.utils.AudioUtils
|
||||||
|
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||||
|
import moe.fuqiuluo.shamrock.utils.MD5
|
||||||
|
import moe.fuqiuluo.shamrock.utils.MediaType
|
||||||
|
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
|
||||||
|
import moe.fuqiuluo.shamrock.xposed.helper.msgService
|
||||||
import moe.fuqiuluo.symbols.decodeProtobuf
|
import moe.fuqiuluo.symbols.decodeProtobuf
|
||||||
import protobuf.auto.toByteArray
|
import protobuf.auto.toByteArray
|
||||||
import protobuf.oidb.TrpcOidb
|
import protobuf.oidb.TrpcOidb
|
||||||
@ -31,13 +63,248 @@ import protobuf.oidb.cmd0x388.Cmd0x388ReqBody
|
|||||||
import protobuf.oidb.cmd0x388.Cmd0x388RspBody
|
import protobuf.oidb.cmd0x388.Cmd0x388RspBody
|
||||||
import protobuf.oidb.cmd0x388.TryUpImgReq
|
import protobuf.oidb.cmd0x388.TryUpImgReq
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.math.roundToInt
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.random.nextUInt
|
import kotlin.random.nextUInt
|
||||||
import kotlin.random.nextULong
|
import kotlin.random.nextULong
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
internal object NtV2RichMediaSvc: BaseSvc() {
|
internal object NtV2RichMediaSvc: BaseSvc() {
|
||||||
|
private const val GROUP_PIC_UPLOAD_TO = "100000000"
|
||||||
|
|
||||||
private val requestIdSeq = atomic(2L)
|
private val requestIdSeq = atomic(2L)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量上传图片
|
||||||
|
*/
|
||||||
|
suspend fun tryUploadResourceByNt(
|
||||||
|
chatType: Int,
|
||||||
|
elementType: Int,
|
||||||
|
resources: ArrayList<File>,
|
||||||
|
timeout: Duration
|
||||||
|
): Result<MutableList<CommonFileInfo>> {
|
||||||
|
require(resources.size in 1 .. 10) { "imageFiles.size() must be in 1 .. 10" }
|
||||||
|
|
||||||
|
val messages = resources.map { file ->
|
||||||
|
val elem = MsgElement()
|
||||||
|
elem.elementType = elementType
|
||||||
|
when(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
|
||||||
|
}
|
||||||
|
MsgConstant.KELEMTYPEPTT -> {
|
||||||
|
require(resources.size == 1) // 语音只能单个上传
|
||||||
|
var pttFile = file
|
||||||
|
val ptt = PttElement()
|
||||||
|
when (AudioUtils.getMediaType(pttFile)) {
|
||||||
|
MediaType.Silk -> {
|
||||||
|
ptt.formatType = MsgConstant.KPTTFORMATTYPESILK
|
||||||
|
ptt.duration = QRoute.api(IAIOPttApi::class.java)
|
||||||
|
.getPttFileDuration(pttFile.absolutePath)
|
||||||
|
}
|
||||||
|
MediaType.Amr -> {
|
||||||
|
ptt.duration = AudioUtils.getDurationSec(pttFile)
|
||||||
|
ptt.formatType = MsgConstant.KPTTFORMATTYPEAMR
|
||||||
|
}
|
||||||
|
MediaType.Pcm -> {
|
||||||
|
val result = AudioUtils.pcmToSilk(pttFile)
|
||||||
|
ptt.duration = (result.second * 0.001).roundToInt()
|
||||||
|
pttFile = result.first
|
||||||
|
ptt.formatType = MsgConstant.KPTTFORMATTYPESILK
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val result = AudioUtils.audioToSilk(pttFile)
|
||||||
|
ptt.duration = runCatching {
|
||||||
|
QRoute.api(IAIOPttApi::class.java)
|
||||||
|
.getPttFileDuration(result.second.absolutePath)
|
||||||
|
}.getOrElse {
|
||||||
|
result.first
|
||||||
|
}
|
||||||
|
pttFile = result.second
|
||||||
|
ptt.formatType = MsgConstant.KPTTFORMATTYPESILK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptt.md5HexStr = QQNTWrapperUtil.CppProxy.genFileMd5Hex(pttFile.absolutePath)
|
||||||
|
val msgService = NTServiceFetcher.kernelService.msgService!!
|
||||||
|
val originalPath = msgService.getRichMediaFilePathForMobileQQSend(
|
||||||
|
RichMediaFilePathInfo(
|
||||||
|
MsgConstant.KELEMTYPEPTT, 0, ptt.md5HexStr, file.name, 1, 0, null, "", true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(originalPath) != pttFile.length()) {
|
||||||
|
QQNTWrapperUtil.CppProxy.copyFile(pttFile.absolutePath, originalPath)
|
||||||
|
}
|
||||||
|
if (originalPath != null) {
|
||||||
|
ptt.filePath = originalPath
|
||||||
|
} else {
|
||||||
|
ptt.filePath = pttFile.absolutePath
|
||||||
|
}
|
||||||
|
ptt.canConvert2Text = true
|
||||||
|
ptt.fileId = 0
|
||||||
|
ptt.fileUuid = ""
|
||||||
|
ptt.text = ""
|
||||||
|
ptt.voiceType = MsgConstant.KPTTVOICETYPESOUNDRECORD
|
||||||
|
ptt.voiceChangeType = MsgConstant.KPTTVOICECHANGETYPENONE
|
||||||
|
elem.pttElement = ptt
|
||||||
|
}
|
||||||
|
MsgConstant.KELEMTYPEVIDEO -> {
|
||||||
|
require(resources.size == 1) // 视频只能单个上传
|
||||||
|
val video = VideoElement()
|
||||||
|
video.videoMd5 = QQNTWrapperUtil.CppProxy.genFileMd5Hex(file.absolutePath)
|
||||||
|
val msgService = NTServiceFetcher.kernelService.msgService!!
|
||||||
|
val originalPath = msgService.getRichMediaFilePathForMobileQQSend(
|
||||||
|
RichMediaFilePathInfo(
|
||||||
|
5, 2, video.videoMd5, file.name, 1, 0, null, "", true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val thumbPath = msgService.getRichMediaFilePathForMobileQQSend(
|
||||||
|
RichMediaFilePathInfo(
|
||||||
|
5, 1, video.videoMd5, file.name, 2, 0, null, "", true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (!QQNTWrapperUtil.CppProxy.fileIsExist(originalPath) || QQNTWrapperUtil.CppProxy.getFileSize(
|
||||||
|
originalPath
|
||||||
|
) != file.length()
|
||||||
|
) {
|
||||||
|
QQNTWrapperUtil.CppProxy.copyFile(file.absolutePath, originalPath)
|
||||||
|
AudioUtils.obtainVideoCover(file.absolutePath, thumbPath!!)
|
||||||
|
}
|
||||||
|
video.fileTime = AudioUtils.getVideoTime(file)
|
||||||
|
video.fileSize = file.length()
|
||||||
|
video.fileName = file.name
|
||||||
|
video.fileFormat = FileTransfer.VIDEO_FORMAT_MP4
|
||||||
|
video.thumbSize = QQNTWrapperUtil.CppProxy.getFileSize(thumbPath).toInt()
|
||||||
|
val options = BitmapFactory.Options()
|
||||||
|
BitmapFactory.decodeFile(thumbPath, options)
|
||||||
|
video.thumbWidth = options.outWidth
|
||||||
|
video.thumbHeight = options.outHeight
|
||||||
|
video.thumbMd5 = QQNTWrapperUtil.CppProxy.genFileMd5Hex(thumbPath)
|
||||||
|
video.thumbPath = hashMapOf(0 to thumbPath)
|
||||||
|
elem.videoElement = video
|
||||||
|
}
|
||||||
|
|
||||||
|
/*MsgConstant.KELEMTYPEFILE -> {
|
||||||
|
require(resources.size == 1) // 文件只能单个上传
|
||||||
|
val fileElement = FileElement()
|
||||||
|
fileElement.fileMd5 = ""
|
||||||
|
fileElement.fileName = file.name
|
||||||
|
fileElement.filePath = file.absolutePath
|
||||||
|
fileElement.fileSize = file.length()
|
||||||
|
fileElement.picWidth = 0
|
||||||
|
fileElement.picHeight = 0
|
||||||
|
fileElement.videoDuration = 0
|
||||||
|
fileElement.picThumbPath = HashMap()
|
||||||
|
fileElement.expireTime = 0L
|
||||||
|
fileElement.fileSha = ""
|
||||||
|
fileElement.fileSha3 = ""
|
||||||
|
fileElement.file10MMd5 = ""
|
||||||
|
when (TransfileHelper.getExtensionId(file.name)) {
|
||||||
|
0 -> {
|
||||||
|
val wh = QRoute.api(IMsgUtilApi::class.java)
|
||||||
|
.getPicSizeByPath(file.absolutePath)
|
||||||
|
fileElement.picWidth = wh.first
|
||||||
|
fileElement.picHeight = wh.second
|
||||||
|
fileElement.picThumbPath[750] = file.absolutePath
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
val thumbPic = FileUtils.getFileByMd5(MD5.genFileMd5Hex(file.absolutePath))
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val fileOutputStream = FileOutputStream(thumbPic)
|
||||||
|
val retriever = MediaMetadataRetriever()
|
||||||
|
retriever.setDataSource(fileElement.filePath)
|
||||||
|
retriever.frameAtTime?.compress(Bitmap.CompressFormat.JPEG, 60, fileOutputStream)
|
||||||
|
fileOutputStream.flush()
|
||||||
|
fileOutputStream.close()
|
||||||
|
}
|
||||||
|
val options = BitmapFactory.Options()
|
||||||
|
BitmapFactory.decodeFile(thumbPic.absolutePath, options)
|
||||||
|
fileElement.picHeight = options.outHeight
|
||||||
|
fileElement.picWidth = options.outWidth
|
||||||
|
fileElement.picThumbPath = hashMapOf(750 to thumbPic.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elem.fileElement = fileElement
|
||||||
|
}*/
|
||||||
|
else -> throw IllegalArgumentException("unsupported elementType: $elementType")
|
||||||
|
}
|
||||||
|
return@map elem
|
||||||
|
}
|
||||||
|
if (messages.isEmpty()) {
|
||||||
|
return Result.failure(Exception("no valid image files"))
|
||||||
|
}
|
||||||
|
val contact = when(chatType) {
|
||||||
|
MsgConstant.KCHATTYPEC2C -> MessageHelper.generateContact(chatType, TicketSvc.getUin())
|
||||||
|
else -> Contact(chatType, GROUP_PIC_UPLOAD_TO, GROUP_PIC_UPLOAD_TO)
|
||||||
|
}
|
||||||
|
val result = mutableListOf<CommonFileInfo>()
|
||||||
|
withTimeoutOrNull(timeout) {
|
||||||
|
suspendCancellableCoroutine {
|
||||||
|
val uniseq = MessageHelper.generateMsgId(chatType)
|
||||||
|
RichMediaUploadHandler.registerListener(uniseq.qqMsgId) upload@{
|
||||||
|
if (uniseq.qqMsgId == msgId) {
|
||||||
|
result.add(commonFileInfo)
|
||||||
|
}
|
||||||
|
if (result.size == resources.size) {
|
||||||
|
it.resume(true)
|
||||||
|
return@upload true
|
||||||
|
}
|
||||||
|
return@upload false
|
||||||
|
}
|
||||||
|
MessageHelper.sendMessageWithMsgId(
|
||||||
|
contact = contact,
|
||||||
|
message = ArrayList(messages),
|
||||||
|
uniseq = uniseq.qqMsgId
|
||||||
|
) { _, _ -> }
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
RichMediaUploadHandler.removeListener(uniseq.qqMsgId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.success(result)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取NT图片的RKEY
|
* 获取NT图片的RKEY
|
||||||
*/
|
*/
|
||||||
@ -173,6 +440,9 @@ internal object NtV2RichMediaSvc: BaseSvc() {
|
|||||||
LogCenter.log("requestUploadPic => rsp: $rsp")
|
LogCenter.log("requestUploadPic => rsp: $rsp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用OldBDH获取图片上传状态以及图片上传服务器
|
||||||
|
*/
|
||||||
suspend fun requestUploadGroupPic(
|
suspend fun requestUploadGroupPic(
|
||||||
groupId: ULong,
|
groupId: ULong,
|
||||||
md5: String,
|
md5: String,
|
||||||
@ -215,13 +485,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,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.service.api
|
package moe.fuqiuluo.qqinterface.servlet.transfile
|
||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo
|
import com.tencent.qqnt.kernel.nativeinterface.FileTransNotifyInfo
|
||||||
|
|
@ -1,14 +1,19 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile
|
||||||
|
|
||||||
import com.tencent.mobileqq.data.MessageForPic
|
|
||||||
import com.tencent.mobileqq.data.MessageForShortVideo
|
import com.tencent.mobileqq.data.MessageForShortVideo
|
||||||
import com.tencent.mobileqq.data.MessageRecord
|
import com.tencent.mobileqq.data.MessageRecord
|
||||||
import com.tencent.mobileqq.transfile.FileMsg
|
import com.tencent.mobileqq.transfile.FileMsg
|
||||||
import com.tencent.mobileqq.transfile.TransferRequest
|
import com.tencent.mobileqq.transfile.TransferRequest
|
||||||
import moe.fuqiuluo.shamrock.utils.MD5
|
import moe.fuqiuluo.shamrock.utils.MD5
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.ResourceType.*
|
import moe.fuqiuluo.qqinterface.servlet.transfile.data.ResourceType.*
|
||||||
import moe.fuqiuluo.shamrock.helper.TransfileHelper
|
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() {
|
internal object Transfer: FileTransfer() {
|
||||||
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
|
private val ROUTE = mapOf<ContactType, Map<ResourceType, suspend TransTarget.(Resource) -> Boolean>>(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile.data
|
||||||
|
|
||||||
import com.tencent.mobileqq.data.MessageRecord
|
import com.tencent.mobileqq.data.MessageRecord
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package moe.fuqiuluo.qqinterface.servlet.transfile
|
package moe.fuqiuluo.qqinterface.servlet.transfile.data
|
||||||
|
|
||||||
import java.io.File
|
import java.io.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,
|
||||||
|
)
|
@ -205,27 +205,24 @@ internal object MessageHelper {
|
|||||||
fun sendMessageWithMsgId(
|
fun sendMessageWithMsgId(
|
||||||
contact: Contact,
|
contact: Contact,
|
||||||
message: ArrayList<MsgElement>,
|
message: ArrayList<MsgElement>,
|
||||||
|
uniseq: Long,
|
||||||
callback: IOperateCallback
|
callback: IOperateCallback
|
||||||
): SendMsgResult {
|
): SendMsgResult {
|
||||||
val uniseq = generateMsgId(contact.chatType)
|
|
||||||
val nonMsg: Boolean = message.isEmpty()
|
val nonMsg: Boolean = message.isEmpty()
|
||||||
return if (!nonMsg) {
|
if (!nonMsg) {
|
||||||
val service = QRoute.api(IMsgService::class.java)
|
val service = QRoute.api(IMsgService::class.java)
|
||||||
if (callback is MsgSvc.MessageCallback) {
|
|
||||||
callback.msgHash = uniseq.msgHashId
|
|
||||||
}
|
|
||||||
|
|
||||||
service.sendMsg(
|
service.sendMsg(
|
||||||
contact,
|
contact,
|
||||||
uniseq.qqMsgId,
|
uniseq,
|
||||||
message,
|
message,
|
||||||
callback
|
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(
|
suspend fun sendMessageNoCb(
|
||||||
|
@ -3,7 +3,7 @@ package moe.fuqiuluo.shamrock.helper
|
|||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import kotlinx.serialization.json.Json
|
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.qqinterface.servlet.ark.ArkMsgSvc
|
||||||
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
import moe.fuqiuluo.shamrock.tools.GlobalClient
|
||||||
import moe.fuqiuluo.shamrock.tools.asInt
|
import moe.fuqiuluo.shamrock.tools.asInt
|
||||||
|
@ -49,7 +49,7 @@ internal object DownloadFile: IActionHandler() {
|
|||||||
headerMap[k] = v
|
headerMap[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return invoke(url, threadCnt, headerMap, echo)
|
return invoke(url, threadCnt, headerMap, name, echo)
|
||||||
} else if (base64 != null) {
|
} else if (base64 != null) {
|
||||||
return invoke(base64, name, echo)
|
return invoke(base64, name, echo)
|
||||||
} else {
|
} else {
|
||||||
@ -88,6 +88,7 @@ internal object DownloadFile: IActionHandler() {
|
|||||||
url: String,
|
url: String,
|
||||||
threadCnt: Int,
|
threadCnt: Int,
|
||||||
headers: Map<String, String>,
|
headers: Map<String, String>,
|
||||||
|
name: String?,
|
||||||
echo: JsonElement = EmptyJsonString
|
echo: JsonElement = EmptyJsonString
|
||||||
): String {
|
): String {
|
||||||
return kotlin.runCatching {
|
return kotlin.runCatching {
|
||||||
@ -100,7 +101,13 @@ internal object DownloadFile: IActionHandler() {
|
|||||||
)) {
|
)) {
|
||||||
return error("下载失败 (0x1)", echo)
|
return error("下载失败 (0x1)", echo)
|
||||||
}
|
}
|
||||||
tmp = FileUtils.renameByMd5(tmp)
|
tmp = if (name == null) {
|
||||||
|
FileUtils.renameByMd5(tmp)
|
||||||
|
} else {
|
||||||
|
val newFile = tmp.parentFile!!.resolve(name)
|
||||||
|
tmp.renameTo(newFile)
|
||||||
|
newFile
|
||||||
|
}
|
||||||
ok(data = DownloadResult(
|
ok(data = DownloadResult(
|
||||||
file = tmp.absolutePath,
|
file = tmp.absolutePath,
|
||||||
md5 = MD5.genFileMd5Hex(tmp.absolutePath)
|
md5 = MD5.genFileMd5Hex(tmp.absolutePath)
|
||||||
|
@ -146,11 +146,13 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
).also {
|
).also {
|
||||||
desc[++i] = record.sendMemberName.ifEmpty { record.sendNickName } + ": "
|
desc[++i] = record.sendMemberName.ifEmpty { record.sendNickName } + ": "
|
||||||
}.map {
|
}.map {
|
||||||
desc[++i] += when (it.type) {
|
desc[i] += when (it.type) {
|
||||||
"text" -> it.data["text"] as String
|
"text" -> it.data["text"] as String
|
||||||
"at" -> "@${it.data["name"] as String? ?: it.data["qq"] as String}"
|
"at" -> "@${it.data["name"] as String? ?: it.data["qq"] as String}"
|
||||||
"face" -> "[表情]"
|
"face" -> "[表情]"
|
||||||
"voice" -> "[语音]"
|
"pic", "image" -> "[图片]"
|
||||||
|
"voice", "record" -> "[语音]"
|
||||||
|
"video" -> "[视频]"
|
||||||
"node" -> "[合并转发消息]"
|
"node" -> "[合并转发消息]"
|
||||||
"markdown" -> "[Markdown消息]"
|
"markdown" -> "[Markdown消息]"
|
||||||
"button" -> "[Button类型]"
|
"button" -> "[Button类型]"
|
||||||
@ -197,7 +199,7 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
body = MsgBody(
|
body = MsgBody(
|
||||||
richText = RichText(
|
richText = RichText(
|
||||||
elements = MessageHelper.messageArrayToMessageElements(
|
elements = MessageHelper.messageArrayToMessageElements(
|
||||||
chatType = MsgConstant.KCHATTYPEGROUP,
|
chatType = chatType,
|
||||||
msgId = Random.nextLong(),
|
msgId = Random.nextLong(),
|
||||||
peerId = data["uin"]?.asString ?: TicketSvc.getUin(),
|
peerId = data["uin"]?.asString ?: TicketSvc.getUin(),
|
||||||
messageList = when (data["content"]) {
|
messageList = when (data["content"]) {
|
||||||
@ -207,7 +209,7 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
}.also {
|
}.also {
|
||||||
desc[++i] =
|
desc[++i] =
|
||||||
(data["name"].asStringOrNull ?: data["uin"].asStringOrNull
|
(data["name"].asStringOrNull ?: data["uin"].asStringOrNull
|
||||||
?: TicketSvc.getNickname() )+ ": "
|
?: TicketSvc.getNickname()) + ": "
|
||||||
}.onEach {
|
}.onEach {
|
||||||
val type = it.asJsonObject["type"].asString
|
val type = it.asJsonObject["type"].asString
|
||||||
val itData = it.asJsonObject["data"].asJsonObject
|
val itData = it.asJsonObject["data"].asJsonObject
|
||||||
@ -215,8 +217,9 @@ internal object SendForwardMessage : IActionHandler() {
|
|||||||
"text" -> itData["text"].asString
|
"text" -> itData["text"].asString
|
||||||
"at" -> "@${itData["name"].asStringOrNull ?: itData["qq"].asString}"
|
"at" -> "@${itData["name"].asStringOrNull ?: itData["qq"].asString}"
|
||||||
"face" -> "[表情]"
|
"face" -> "[表情]"
|
||||||
"image" -> "[图片]"
|
"pic", "image" -> "[图片]"
|
||||||
"voice" -> "[语音]"
|
"voice", "record" -> "[语音]"
|
||||||
|
"video" -> "[视频]"
|
||||||
"node" -> "[合并转发消息]"
|
"node" -> "[合并转发消息]"
|
||||||
"markdown" -> "[Markdown消息]"
|
"markdown" -> "[Markdown消息]"
|
||||||
"button" -> "[Button类型]"
|
"button" -> "[Button类型]"
|
||||||
|
@ -24,8 +24,7 @@ internal object SendMsgByResid : IActionHandler() {
|
|||||||
val resId = session.getString("res_id")
|
val resId = session.getString("res_id")
|
||||||
val peerId = session.getString("peer_id")
|
val peerId = session.getString("peer_id")
|
||||||
val messageType = session.getString("message_type")
|
val messageType = session.getString("message_type")
|
||||||
invoke(resId, peerId, messageType)
|
return invoke(peerId, resId, messageType, session.echo)
|
||||||
return ok("ok", session.echo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend operator fun invoke(peerId: String, resId: String, messageType: String, echo: JsonElement = EmptyJsonString): String {
|
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())
|
BaseSvc.sendBufferAW("MessageSvc.PbSendMsg", true, req.toByteArray())
|
||||||
return ok("ok", echo)
|
return ok("ok", echo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val requiredParams: Array<String> = arrayOf("res_id", "peer_id", "message_type")
|
||||||
}
|
}
|
@ -1,15 +1,18 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.action.handlers
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
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.ActionSession
|
||||||
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
|
||||||
import moe.fuqiuluo.shamrock.tools.jsonArray
|
import moe.fuqiuluo.shamrock.tools.jsonArray
|
||||||
import moe.fuqiuluo.symbols.OneBotHandler
|
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() {
|
internal object SendPrivateMessage : IActionHandler() {
|
||||||
override suspend fun internalHandle(session: ActionSession): String {
|
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 groupId = session.getLongOrNull("group_id")
|
||||||
val chatType = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP
|
val chatType = if (groupId == null) MsgConstant.KCHATTYPEC2C else MsgConstant.KCHATTYPETEMPC2CFROMGROUP
|
||||||
val retryCnt = session.getIntOrNull("retry_cnt")
|
val retryCnt = session.getIntOrNull("retry_cnt")
|
||||||
@ -19,21 +22,21 @@ internal object SendPrivateMessage : IActionHandler() {
|
|||||||
val message = session.getString("message")
|
val message = session.getString("message")
|
||||||
SendMessage(
|
SendMessage(
|
||||||
chatType = chatType,
|
chatType = chatType,
|
||||||
peerId = userId.toString(),
|
peerId = userId,
|
||||||
message = message,
|
message = message,
|
||||||
autoEscape = autoEscape,
|
autoEscape = autoEscape,
|
||||||
echo = session.echo,
|
echo = session.echo,
|
||||||
fromId = groupId?.toString() ?: userId.toString(),
|
fromId = groupId?.toString() ?: userId,
|
||||||
retryCnt = retryCnt ?: 5,
|
retryCnt = retryCnt ?: 5,
|
||||||
recallDuration = recallDuration
|
recallDuration = recallDuration
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
SendMessage(
|
SendMessage(
|
||||||
chatType = chatType,
|
chatType = chatType,
|
||||||
peerId = userId.toString(),
|
peerId = userId,
|
||||||
message = if (session.isArray("message")) session.getArray("message") else listOf(session.getObject("message")).jsonArray,
|
message = if (session.isArray("message")) session.getArray("message") else listOf(session.getObject("message")).jsonArray,
|
||||||
echo = session.echo,
|
echo = session.echo,
|
||||||
fromId = groupId?.toString() ?: userId.toString(),
|
fromId = groupId?.toString() ?: userId,
|
||||||
retryCnt = retryCnt ?: 5,
|
retryCnt = retryCnt ?: 5,
|
||||||
recallDuration = recallDuration
|
recallDuration = recallDuration
|
||||||
)
|
)
|
||||||
|
@ -17,12 +17,12 @@ 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
|
||||||
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichMediaUploadHandler
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
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
|
||||||
@ -55,6 +55,20 @@ internal object UploadGroupFile : IActionHandler() {
|
|||||||
if (!srcFile.exists()) {
|
if (!srcFile.exists()) {
|
||||||
srcFile = FileUtils.getFile(file)
|
srcFile = FileUtils.getFile(file)
|
||||||
}
|
}
|
||||||
|
if (!srcFile.exists()) {
|
||||||
|
srcFile = file.let {
|
||||||
|
val md5 = it.replace(
|
||||||
|
regex = "[{}\\-]".toRegex(),
|
||||||
|
replacement = ""
|
||||||
|
).split(".")[0].lowercase()
|
||||||
|
if (md5.length == 32) {
|
||||||
|
FileUtils.getFileByMd5(it)
|
||||||
|
} else {
|
||||||
|
FileUtils.parseAndSave(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!srcFile.exists()) {
|
if (!srcFile.exists()) {
|
||||||
return badParam("文件不存在", echo)
|
return badParam("文件不存在", echo)
|
||||||
}
|
}
|
||||||
@ -64,6 +78,7 @@ internal object UploadGroupFile : IActionHandler() {
|
|||||||
fileElement.fileName = name
|
fileElement.fileName = name
|
||||||
fileElement.filePath = srcFile.absolutePath
|
fileElement.filePath = srcFile.absolutePath
|
||||||
fileElement.fileSize = srcFile.length()
|
fileElement.fileSize = srcFile.length()
|
||||||
|
fileElement.folderId = srcFile.parent ?: ""
|
||||||
fileElement.picWidth = 0
|
fileElement.picWidth = 0
|
||||||
fileElement.picHeight = 0
|
fileElement.picHeight = 0
|
||||||
fileElement.videoDuration = 0
|
fileElement.videoDuration = 0
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
package moe.fuqiuluo.shamrock.remote.action.handlers
|
||||||
|
|
||||||
|
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
|
||||||
|
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_nt_resource", ["upload_nt_res"])
|
||||||
|
internal object UploadNtResource: IActionHandler() {
|
||||||
|
override suspend fun internalHandle(session: ActionSession): String {
|
||||||
|
val pic = session.getString("file")
|
||||||
|
val chatType = when(session.getStringOrNull("message_type")) {
|
||||||
|
"group" -> MsgConstant.KCHATTYPEGROUP
|
||||||
|
"guild" -> MsgConstant.KCHATTYPEGUILD
|
||||||
|
"private" -> MsgConstant.KCHATTYPEC2C
|
||||||
|
else -> MsgConstant.KCHATTYPEGROUP
|
||||||
|
}
|
||||||
|
val fileType = when(session.getStringOrNull("file_type")) {
|
||||||
|
"file" -> MsgConstant.KELEMTYPEFILE
|
||||||
|
"image", "pic" -> MsgConstant.KELEMTYPEPIC
|
||||||
|
"video" -> MsgConstant.KELEMTYPEVIDEO
|
||||||
|
"audio", "voice", "record" -> MsgConstant.KELEMTYPEPTT
|
||||||
|
else -> MsgConstant.KELEMTYPEFILE
|
||||||
|
}
|
||||||
|
return invoke(chatType, fileType, pic, session.echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend operator fun invoke(
|
||||||
|
chatType: Int,
|
||||||
|
fileType: Int,
|
||||||
|
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.tryUploadResourceByNt(
|
||||||
|
chatType = chatType,
|
||||||
|
elementType = fileType,
|
||||||
|
resources = 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,
|
||||||
|
sha = it.sha ?: ""
|
||||||
|
)
|
||||||
|
}), echo)
|
||||||
|
}.onFailure {
|
||||||
|
return logic("upload failed: ${it.message ?: it.toString()}", echo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return logic("upload failed", echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val requiredParams: Array<String> = arrayOf("file")
|
||||||
|
}
|
@ -14,16 +14,13 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
import moe.fuqiuluo.shamrock.helper.ContactHelper
|
|
||||||
import moe.fuqiuluo.shamrock.helper.LogCenter
|
import moe.fuqiuluo.shamrock.helper.LogCenter
|
||||||
import moe.fuqiuluo.shamrock.helper.MessageHelper
|
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.qqinterface.servlet.transfile.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
|
||||||
@ -56,6 +53,21 @@ internal object UploadPrivateFile : IActionHandler() {
|
|||||||
if (!srcFile.exists()) {
|
if (!srcFile.exists()) {
|
||||||
srcFile = FileUtils.getFile(file)
|
srcFile = FileUtils.getFile(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!srcFile.exists()) {
|
||||||
|
srcFile = file.let {
|
||||||
|
val md5 = it.replace(
|
||||||
|
regex = "[{}\\-]".toRegex(),
|
||||||
|
replacement = ""
|
||||||
|
).split(".")[0].lowercase()
|
||||||
|
if (md5.length == 32) {
|
||||||
|
FileUtils.getFileByMd5(it)
|
||||||
|
} else {
|
||||||
|
FileUtils.parseAndSave(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!srcFile.exists()) {
|
if (!srcFile.exists()) {
|
||||||
return badParam("文件不存在", echo)
|
return badParam("文件不存在", echo)
|
||||||
}
|
}
|
||||||
@ -65,6 +77,7 @@ internal object UploadPrivateFile : IActionHandler() {
|
|||||||
fileElement.fileName = name
|
fileElement.fileName = name
|
||||||
fileElement.filePath = srcFile.absolutePath
|
fileElement.filePath = srcFile.absolutePath
|
||||||
fileElement.fileSize = srcFile.length()
|
fileElement.fileSize = srcFile.length()
|
||||||
|
fileElement.folderId = srcFile.parent ?: ""
|
||||||
fileElement.picWidth = 0
|
fileElement.picWidth = 0
|
||||||
fileElement.picHeight = 0
|
fileElement.picHeight = 0
|
||||||
fileElement.videoDuration = 0
|
fileElement.videoDuration = 0
|
||||||
@ -111,9 +124,11 @@ internal object UploadPrivateFile : IActionHandler() {
|
|||||||
msgService.sendMsgWithMsgId(
|
msgService.sendMsgWithMsgId(
|
||||||
contact, msgIdPair.qqMsgId, arrayListOf(msgElement)
|
contact, msgIdPair.qqMsgId, arrayListOf(msgElement)
|
||||||
) { code, reason ->
|
) { code, reason ->
|
||||||
|
if (code != 0) {
|
||||||
LogCenter.log("私聊文件消息发送异常(code = $code, reason = $reason)")
|
LogCenter.log("私聊文件消息发送异常(code = $code, reason = $reason)")
|
||||||
it.resume(null)
|
it.resume(null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
RichMediaUploadHandler.registerListener(msgIdPair.qqMsgId) {
|
RichMediaUploadHandler.registerListener(msgIdPair.qqMsgId) {
|
||||||
it.resume(this)
|
it.resume(this)
|
||||||
return@registerListener true
|
return@registerListener true
|
||||||
|
@ -27,13 +27,18 @@ fun Routing.testAction() {
|
|||||||
val resId = fetchOrThrow("res_id")
|
val resId = fetchOrThrow("res_id")
|
||||||
val peerId = fetchOrThrow("peer_Id")
|
val peerId = fetchOrThrow("peer_Id")
|
||||||
val messageType = fetchOrThrow("message_type")
|
val messageType = fetchOrThrow("message_type")
|
||||||
call.respondText(SendMsgByResid(resId, peerId, messageType))
|
call.respondText(SendMsgByResid(peerId, resId, messageType))
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrPost("/createUidFromTinyId") {
|
getOrPost("/createUidFromTinyId") {
|
||||||
val selfId = fetchOrThrow("selfId").toLong()
|
val selfId = fetchOrThrow("selfId").toLong()
|
||||||
val peerId = fetchOrThrow("peerId")
|
val peerId = fetchOrThrow("peerId")
|
||||||
call.respondText(NTServiceFetcher.kernelService.wrapperSession.msgService.createUidFromTinyId(selfId, peerId.toLong()))
|
call.respondText(
|
||||||
|
NTServiceFetcher.kernelService.wrapperSession.msgService.createUidFromTinyId(
|
||||||
|
selfId,
|
||||||
|
peerId.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrPost("/addSendMsg") {
|
getOrPost("/addSendMsg") {
|
||||||
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.GlobalScope
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
import moe.fuqiuluo.qqinterface.servlet.MsgSvc
|
||||||
import moe.fuqiuluo.qqinterface.servlet.TicketSvc
|
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.msg.toCQCode
|
||||||
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
|
||||||
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
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.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.qqinterface.servlet.transfile.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 java.util.ArrayList
|
import java.util.ArrayList
|
||||||
@ -24,9 +25,6 @@ import java.util.Collections
|
|||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
internal object AioListener : IKernelMsgListener {
|
internal object AioListener : IKernelMsgListener {
|
||||||
// 通过MSG SEQ临时监听器
|
|
||||||
private val tempMessageListenerMap = Collections.synchronizedMap(HashMap<Long, suspend MsgRecord.() -> Unit>())
|
|
||||||
|
|
||||||
override fun onRecvMsg(msgList: ArrayList<MsgRecord>) {
|
override fun onRecvMsg(msgList: ArrayList<MsgRecord>) {
|
||||||
if (msgList.isEmpty()) return
|
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) {
|
private suspend fun handleMsg(record: MsgRecord) {
|
||||||
try {
|
try {
|
||||||
tempMessageListenerMap.firstNotNullOfOrNull {
|
if (MessageTempHandler.notify(record)) return
|
||||||
if (it.key == record.msgSeq) it else null
|
|
||||||
}?.let {
|
|
||||||
it.value(record)
|
|
||||||
tempMessageListenerMap.remove(it.key)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (record.msgSeq < 0) return
|
if (record.msgSeq < 0) return
|
||||||
|
|
||||||
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
val msgHash = MessageHelper.generateMsgIdHash(record.chatType, record.msgId)
|
||||||
@ -432,7 +412,7 @@ internal object AioListener : IKernelMsgListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onRichMediaUploadComplete(notifyInfo: FileTransNotifyInfo) {
|
override fun onRichMediaUploadComplete(notifyInfo: FileTransNotifyInfo) {
|
||||||
LogCenter.log("onRichMediaUploadComplete($notifyInfo)", Level.DEBUG)
|
LogCenter.log({ "[BDH] 资源上传完成(${notifyInfo.trasferStatus}, ${notifyInfo.fileId}, ${notifyInfo.msgId}, ${notifyInfo.commonFileInfo})" }, Level.DEBUG)
|
||||||
RichMediaUploadHandler.notify(notifyInfo)
|
RichMediaUploadHandler.notify(notifyInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user