Shamrock: Support big face and bubble face

This commit is contained in:
whitechi73 2024-01-28 23:22:06 +08:00
parent 9482641c38
commit e07e75747a
4 changed files with 90 additions and 27 deletions

View File

@ -19,4 +19,11 @@ public class QQSysFaceUtil {
public static String getFaceDescription(int localId) { public static String getFaceDescription(int localId) {
return ""; return "";
} }
public static String getPrueFaceDescription(String str) {
if (str == null) {
return null;
}
return str.startsWith("/") ? str.substring(1) : str;
}
} }

View File

@ -9,6 +9,7 @@ import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qphone.base.remote.ToServiceMsg import com.tencent.qphone.base.remote.ToServiceMsg
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
import com.tencent.qqnt.kernel.nativeinterface.ArkElement import com.tencent.qqnt.kernel.nativeinterface.ArkElement
import com.tencent.qqnt.kernel.nativeinterface.FaceBubbleElement
import com.tencent.qqnt.kernel.nativeinterface.FaceElement import com.tencent.qqnt.kernel.nativeinterface.FaceElement
import com.tencent.qqnt.kernel.nativeinterface.MarkdownElement import com.tencent.qqnt.kernel.nativeinterface.MarkdownElement
import com.tencent.qqnt.kernel.nativeinterface.MarketFaceElement import com.tencent.qqnt.kernel.nativeinterface.MarketFaceElement
@ -20,9 +21,9 @@ import com.tencent.qqnt.kernel.nativeinterface.PttElement
import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil import com.tencent.qqnt.kernel.nativeinterface.QQNTWrapperUtil
import com.tencent.qqnt.kernel.nativeinterface.ReplyElement import com.tencent.qqnt.kernel.nativeinterface.ReplyElement
import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo import com.tencent.qqnt.kernel.nativeinterface.RichMediaFilePathInfo
import com.tencent.qqnt.kernel.nativeinterface.SmallYellowFaceInfo
import com.tencent.qqnt.kernel.nativeinterface.TextElement import com.tencent.qqnt.kernel.nativeinterface.TextElement
import com.tencent.qqnt.kernel.nativeinterface.VideoElement import com.tencent.qqnt.kernel.nativeinterface.VideoElement
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -45,7 +46,9 @@ import moe.fuqiuluo.qqinterface.servlet.transfile.with
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
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LocalCacheHelper import moe.fuqiuluo.shamrock.helper.LocalCacheHelper
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.LogicException import moe.fuqiuluo.shamrock.helper.LogicException
import moe.fuqiuluo.shamrock.helper.MessageHelper import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.helper.MusicHelper import moe.fuqiuluo.shamrock.helper.MusicHelper
@ -62,8 +65,6 @@ import moe.fuqiuluo.shamrock.utils.AudioUtils
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MediaType import moe.fuqiuluo.shamrock.utils.MediaType
import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService import moe.fuqiuluo.shamrock.xposed.helper.msgService
@ -106,8 +107,32 @@ internal object MessageMaker {
"basketball" to MessageMaker::createBasketballElem, "basketball" to MessageMaker::createBasketballElem,
//"node" to MessageMaker::createNodeElem, //"node" to MessageMaker::createNodeElem,
//"multi_msg" to MessageMaker::createLongMsgStruct, //"multi_msg" to MessageMaker::createLongMsgStruct,
"bubble_face" to MessageMaker::createBubbleFaceElem,
) )
private suspend fun createBubbleFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
data.checkAndThrow("id", "count")
val faceId = data["id"].asInt
val local = QQSysFaceUtil.convertToLocal(faceId)
val name = QQSysFaceUtil.getFaceDescription(local)
val count = data["count"].asInt
val elem = MsgElement()
elem.elementType = MsgConstant.KELEMTYPEFACEBUBBLE
val face = FaceBubbleElement()
face.faceType = 13
face.faceCount = count
face.faceSummary = QQSysFaceUtil.getPrueFaceDescription(name)
val smallYellowFaceInfo = SmallYellowFaceInfo()
smallYellowFaceInfo.index = faceId
smallYellowFaceInfo.compatibleText = face.faceSummary
smallYellowFaceInfo.text = face.faceSummary
face.yellowFaceInfo = smallYellowFaceInfo
face.faceFlag = 0
face.content = data["text"].asStringOrNull ?: "[${face.faceSummary}]x$count"
elem.faceBubbleElement = face
return Result.success(elem)
}
// private suspend fun createNodeElem( // private suspend fun createNodeElem(
// chatType: Int, // chatType: Int,
// msgId: Long, // msgId: Long,
@ -475,19 +500,31 @@ internal object MessageMaker {
private suspend fun createFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> { private suspend fun createFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
data.checkAndThrow("id") data.checkAndThrow("id")
val big = data["big"].asBooleanOrNull ?: false
val elem = MsgElement() val elem = MsgElement()
elem.elementType = MsgConstant.KELEMTYPEFACE elem.elementType = MsgConstant.KELEMTYPEFACE
val face = FaceElement() val face = FaceElement()
// 1 old face
// 2 normal face
// 3 super face
// 4 is market face // 4 is market face
// 5 is vas poke // 5 is vas poke
face.faceType = 0 face.faceType = if (big) 3 else 2
val serverId = data["id"].asInt val serverId = data["id"].asInt
val localId = QQSysFaceUtil.convertToLocal(serverId)
face.faceIndex = serverId face.faceIndex = serverId
face.faceText = QQSysFaceUtil.getFaceDescription(localId) face.faceText = QQSysFaceUtil.getFaceDescription(QQSysFaceUtil.convertToLocal(serverId))
face.imageType = 0 face.imageType = 0
if (big) {
face.stickerId = 30.toString()
face.packId = "1"
face.sourceType = 1
face.stickerType = 1
face.randomType = 1
} else {
face.packId = "0" face.packId = "0"
}
elem.faceElement = face elem.faceElement = face
return Result.success(elem) return Result.success(elem)

View File

@ -4,6 +4,7 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.msg.convert.MessageElemConverter.*
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
@ -43,19 +44,20 @@ internal suspend fun List<MsgElement>.toCQCode(chatType: Int, peerId: String): S
internal object MessageConvert { internal object MessageConvert {
private val convertMap by lazy { private val convertMap by lazy {
mutableMapOf<Int, IMessageConvert>( mutableMapOf<Int, IMessageConvert>(
MsgConstant.KELEMTYPETEXT to MessageElemConverter.TextConverter, MsgConstant.KELEMTYPETEXT to TextConverter,
MsgConstant.KELEMTYPEFACE to MessageElemConverter.FaceConverter, MsgConstant.KELEMTYPEFACE to FaceConverter,
MsgConstant.KELEMTYPEPIC to MessageElemConverter.ImageConverter, MsgConstant.KELEMTYPEPIC to ImageConverter,
MsgConstant.KELEMTYPEPTT to MessageElemConverter.VoiceConverter, MsgConstant.KELEMTYPEPTT to VoiceConverter,
MsgConstant.KELEMTYPEVIDEO to MessageElemConverter.VideoConverter, MsgConstant.KELEMTYPEVIDEO to VideoConverter,
MsgConstant.KELEMTYPEMARKETFACE to MessageElemConverter.MarketFaceConverter, MsgConstant.KELEMTYPEMARKETFACE to MarketFaceConverter,
MsgConstant.KELEMTYPEARKSTRUCT to MessageElemConverter.StructJsonConverter, MsgConstant.KELEMTYPEARKSTRUCT to StructJsonConverter,
MsgConstant.KELEMTYPEREPLY to MessageElemConverter.ReplyConverter, MsgConstant.KELEMTYPEREPLY to ReplyConverter,
MsgConstant.KELEMTYPEGRAYTIP to MessageElemConverter.GrayTipsConverter, MsgConstant.KELEMTYPEGRAYTIP to GrayTipsConverter,
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter, MsgConstant.KELEMTYPEFILE to FileConverter,
MsgConstant.KELEMTYPEMARKDOWN to MessageElemConverter.MarkdownConverter, MsgConstant.KELEMTYPEMARKDOWN to MarkdownConverter,
//MsgConstant.KELEMTYPEMULTIFORWARD to MessageElemConverter.XmlMultiMsgConverter, //MsgConstant.KELEMTYPEMULTIFORWARD to XmlMultiMsgConverter,
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MessageElemConverter.XmlLongMsgConverter, //MsgConstant.KELEMTYPESTRUCTLONGMSG to XmlLongMsgConverter,
MsgConstant.KELEMTYPEFACEBUBBLE to BubbleFaceConverter,
) )
} }
@ -65,11 +67,11 @@ internal object MessageConvert {
peerId: String peerId: String
): ArrayList<MessageSegment> { ): ArrayList<MessageSegment> {
val messageData = arrayListOf<MessageSegment>() val messageData = arrayListOf<MessageSegment>()
elements.forEach { elements.forEach { msg ->
kotlin.runCatching { kotlin.runCatching {
val elementId = it.elementType val elementId = msg.elementType
val converter = convertMap[elementId] val converter = convertMap[elementId]
converter?.convert(chatType, peerId, it) converter?.convert(chatType, peerId, msg)
?: throw UnsupportedOperationException("不支持的消息element类型$elementId") ?: throw UnsupportedOperationException("不支持的消息element类型$elementId")
}.onSuccess { }.onSuccess {
messageData.add(it) messageData.add(it)
@ -77,7 +79,7 @@ internal object MessageConvert {
if (it is UnknownError) { if (it is UnknownError) {
// 不处理的消息类型抛出unknown error // 不处理的消息类型抛出unknown error
} else { } else {
LogCenter.log("消息element转换错误$it", Level.WARN) LogCenter.log("消息element转换错误$it, elementType: ${msg.elementType}", Level.WARN)
} }
} }
} }

View File

@ -45,6 +45,7 @@ internal sealed class MessageElemConverter: IMessageConvert {
data object FaceConverter: MessageElemConverter() { data object FaceConverter: MessageElemConverter() {
override suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment { override suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment {
val face = element.faceElement val face = element.faceElement
if (face.faceType == 5) { if (face.faceType == 5) {
return MessageSegment( return MessageSegment(
type = "poke", type = "poke",
@ -55,8 +56,6 @@ internal sealed class MessageElemConverter: IMessageConvert {
) )
) )
} }
when (face.faceIndex) { when (face.faceIndex) {
114 -> { 114 -> {
return MessageSegment( return MessageSegment(
@ -87,7 +86,8 @@ internal sealed class MessageElemConverter: IMessageConvert {
else -> return MessageSegment( else -> return MessageSegment(
type = "face", type = "face",
data = hashMapOf( data = hashMapOf(
"id" to face.faceIndex "id" to face.faceIndex,
"big" to (face.faceType == 3)
) )
) )
} }
@ -431,6 +431,23 @@ internal sealed class MessageElemConverter: IMessageConvert {
} }
} }
data object BubbleFaceConverter: MessageElemConverter() {
override suspend fun convert(
chatType: Int,
peerId: String,
element: MsgElement
): MessageSegment {
val bubbleElement = element.faceBubbleElement
return MessageSegment(
type = "bubble_face",
data = mapOf(
"id" to bubbleElement.yellowFaceInfo.index,
"count" to (bubbleElement.faceCount ?: 1),
)
)
}
}
protected fun unknownChatType(chatType: Int) { protected fun unknownChatType(chatType: Int) {
throw UnsupportedOperationException("Not supported chat type: $chatType") throw UnsupportedOperationException("Not supported chat type: $chatType")
} }