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) {
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.qqnt.aio.adapter.api.IAIOPttApi
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.MarkdownElement
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.ReplyElement
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.VideoElement
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
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.ContactHelper
import moe.fuqiuluo.shamrock.helper.IllegalParamsException
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LocalCacheHelper
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.LogicException
import moe.fuqiuluo.shamrock.helper.MessageHelper
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.MediaType
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.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService
@ -106,8 +107,32 @@ internal object MessageMaker {
"basketball" to MessageMaker::createBasketballElem,
//"node" to MessageMaker::createNodeElem,
//"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(
// chatType: Int,
// msgId: Long,
@ -475,19 +500,31 @@ internal object MessageMaker {
private suspend fun createFaceElem(chatType: Int, msgId: Long, peerId: String, data: JsonObject): Result<MsgElement> {
data.checkAndThrow("id")
val big = data["big"].asBooleanOrNull ?: false
val elem = MsgElement()
elem.elementType = MsgConstant.KELEMTYPEFACE
val face = FaceElement()
// 1 old face
// 2 normal face
// 3 super face
// 4 is market face
// 5 is vas poke
face.faceType = 0
face.faceType = if (big) 3 else 2
val serverId = data["id"].asInt
val localId = QQSysFaceUtil.convertToLocal(serverId)
face.faceIndex = serverId
face.faceText = QQSysFaceUtil.getFaceDescription(localId)
face.faceText = QQSysFaceUtil.getFaceDescription(QQSysFaceUtil.convertToLocal(serverId))
face.imageType = 0
face.packId = "0"
if (big) {
face.stickerId = 30.toString()
face.packId = "1"
face.sourceType = 1
face.stickerType = 1
face.randomType = 1
} else {
face.packId = "0"
}
elem.faceElement = face
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.MsgRecord
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.msg.convert.MessageElemConverter.*
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.helper.MessageHelper
@ -43,19 +44,20 @@ internal suspend fun List<MsgElement>.toCQCode(chatType: Int, peerId: String): S
internal object MessageConvert {
private val convertMap by lazy {
mutableMapOf<Int, IMessageConvert>(
MsgConstant.KELEMTYPETEXT to MessageElemConverter.TextConverter,
MsgConstant.KELEMTYPEFACE to MessageElemConverter.FaceConverter,
MsgConstant.KELEMTYPEPIC to MessageElemConverter.ImageConverter,
MsgConstant.KELEMTYPEPTT to MessageElemConverter.VoiceConverter,
MsgConstant.KELEMTYPEVIDEO to MessageElemConverter.VideoConverter,
MsgConstant.KELEMTYPEMARKETFACE to MessageElemConverter.MarketFaceConverter,
MsgConstant.KELEMTYPEARKSTRUCT to MessageElemConverter.StructJsonConverter,
MsgConstant.KELEMTYPEREPLY to MessageElemConverter.ReplyConverter,
MsgConstant.KELEMTYPEGRAYTIP to MessageElemConverter.GrayTipsConverter,
MsgConstant.KELEMTYPEFILE to MessageElemConverter.FileConverter,
MsgConstant.KELEMTYPEMARKDOWN to MessageElemConverter.MarkdownConverter,
//MsgConstant.KELEMTYPEMULTIFORWARD to MessageElemConverter.XmlMultiMsgConverter,
//MsgConstant.KELEMTYPESTRUCTLONGMSG to MessageElemConverter.XmlLongMsgConverter,
MsgConstant.KELEMTYPETEXT to TextConverter,
MsgConstant.KELEMTYPEFACE to FaceConverter,
MsgConstant.KELEMTYPEPIC to ImageConverter,
MsgConstant.KELEMTYPEPTT to VoiceConverter,
MsgConstant.KELEMTYPEVIDEO to VideoConverter,
MsgConstant.KELEMTYPEMARKETFACE to MarketFaceConverter,
MsgConstant.KELEMTYPEARKSTRUCT to StructJsonConverter,
MsgConstant.KELEMTYPEREPLY to ReplyConverter,
MsgConstant.KELEMTYPEGRAYTIP to GrayTipsConverter,
MsgConstant.KELEMTYPEFILE to FileConverter,
MsgConstant.KELEMTYPEMARKDOWN to MarkdownConverter,
//MsgConstant.KELEMTYPEMULTIFORWARD to XmlMultiMsgConverter,
//MsgConstant.KELEMTYPESTRUCTLONGMSG to XmlLongMsgConverter,
MsgConstant.KELEMTYPEFACEBUBBLE to BubbleFaceConverter,
)
}
@ -65,11 +67,11 @@ internal object MessageConvert {
peerId: String
): ArrayList<MessageSegment> {
val messageData = arrayListOf<MessageSegment>()
elements.forEach {
elements.forEach { msg ->
kotlin.runCatching {
val elementId = it.elementType
val elementId = msg.elementType
val converter = convertMap[elementId]
converter?.convert(chatType, peerId, it)
converter?.convert(chatType, peerId, msg)
?: throw UnsupportedOperationException("不支持的消息element类型$elementId")
}.onSuccess {
messageData.add(it)
@ -77,7 +79,7 @@ internal object MessageConvert {
if (it is UnknownError) {
// 不处理的消息类型抛出unknown error
} 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() {
override suspend fun convert(chatType: Int, peerId: String, element: MsgElement): MessageSegment {
val face = element.faceElement
if (face.faceType == 5) {
return MessageSegment(
type = "poke",
@ -55,8 +56,6 @@ internal sealed class MessageElemConverter: IMessageConvert {
)
)
}
when (face.faceIndex) {
114 -> {
return MessageSegment(
@ -87,7 +86,8 @@ internal sealed class MessageElemConverter: IMessageConvert {
else -> return MessageSegment(
type = "face",
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) {
throw UnsupportedOperationException("Not supported chat type: $chatType")
}