diff --git a/mirai-core/src/commonMain/kotlin/message/data/lightApp.kt b/mirai-core/src/commonMain/kotlin/message/data/lightApp.kt index 34ee0221a..2ef1a57a3 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/lightApp.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/lightApp.kt @@ -9,13 +9,16 @@ package net.mamoe.mirai.internal.message.data +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json +import kotlinx.serialization.json.* import net.mamoe.mirai.Bot +import net.mamoe.mirai.Mirai import net.mamoe.mirai.internal.message.RefinableMessage import net.mamoe.mirai.internal.message.RefineContext import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.map import net.mamoe.mirai.utils.safeCast internal data class LightAppInternal( @@ -25,7 +28,17 @@ internal data class LightAppInternal( AbstractPolymorphicMessageKey(RichMessage, { it.safeCast() }) override fun tryRefine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message { - val struct = tryDeserialize() ?: return LightApp(content) + val contentJson = runCatching { + json.parseToJsonElement(content).jsonObject + }.getOrNull() ?: return LightApp(content) + + val struct = contentJson.tryDeserialize() ?: return LightApp(content) + + return lightRefine(struct, contentJson) ?: LightApp(content) + } + + private fun lightRefine(struct: LightAppStruct, contentJson: JsonObject): Message? { + struct.run { if (meta.music != null) { MusicKind.values().find { it.appId == meta.music.appid }?.let { musicType -> @@ -43,7 +56,42 @@ internal data class LightAppInternal( } } - return LightApp(content) + return null + } + + override suspend fun refine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message? { + val contentJson = runCatching { + json.parseToJsonElement(content).jsonObject + }.getOrNull() ?: return LightApp(content) + + val struct = contentJson.tryDeserialize() ?: return LightApp(content) + + if (struct.app == "com.tencent.multimsg") { + runCatching { + json.decodeFromJsonElement( + LightAppStruct.Meta.MultiMsgDetail.serializer(), + contentJson["meta"]!!.jsonObject["detail"]!! + ) + }.onSuccess { detail -> + return MessageOrigin( + LightApp(content), + detail.resId, + MessageOriginKind.FORWARD, + ) + ForwardMessage( + preview = listOf(), // FIXME preview with LightApp + title = detail.source.trim(), + brief = struct.prompt.trim(), + source = detail.source.trim(), + summary = detail.summary.trim(), + nodeList = Mirai.downloadForwardMessage(bot, detail.resId), + ) + }.onFailure { err -> + bot.logger.warning("Exception when refining forward message", err) + } + } + + + return lightRefine(struct, contentJson) ?: LightApp(content) } } @@ -52,9 +100,9 @@ private val json = Json { isLenient = true } -internal fun LightAppInternal.tryDeserialize(): LightAppStruct? { +private fun JsonElement.tryDeserialize(): LightAppStruct? { return kotlin.runCatching { - json.decodeFromString(LightAppStruct.serializer(), this.content) + json.decodeFromJsonElement(LightAppStruct.serializer(), this) }.getOrNull() } @@ -98,6 +146,50 @@ EXAMPLE LightAppStruct for MusicShare "view": "music" } */ +/* +EXAMPLE LightAppStruct for ForwardMessage + +{ + "app": "com.tencent.multimsg", + "desc": "[聊天记录]", + "bizsrc": "", + "view": "contact", + "ver": "0.0.0.5", + "prompt": "[聊天记录]", + "appID": "", + "sourceName": "", + "actionData": "", + "actionData_A": "", + "sourceUrl": "", + "meta": { + "detail": { + "news": [ + { + "text": "纤绫·洛雨: [动画表情]" + }, + { + "text": "纤绫·洛雨: [图片]" + } + ], + "uniseq": "7238251206428406430", + "resid": "...", + "summary": "查看2条转发消息", + "source": "群聊的聊天记录" + } + }, + "config": { + "round": 1, + "forward": 1, + "autosize": 1, + "type": "normal", + "width": 300 + }, + "text": "", + "sourceAd": "", + "extra": "{\"tsum\":2,\"filename\":\"7238251206428406430\"}" +} + */ + @Serializable internal data class LightAppStruct( @@ -107,8 +199,8 @@ internal data class LightAppStruct( val config: Config = Config(), @SerialName("desc") val desc: String = "", - @SerialName("extra") - val extra: Extra = Extra(), + // @SerialName("extra") + // val extra: Extra = Extra(), @SerialName("meta") val meta: Meta = Meta(), @SerialName("prompt") @@ -121,10 +213,12 @@ internal data class LightAppStruct( @Serializable data class Config( @SerialName("autosize") + @Serializable(BadBooleanSerializer::class) val autosize: Boolean = false, @SerialName("ctime") val ctime: Long = 0, @SerialName("forward") + @Serializable(BadBooleanSerializer::class) val forward: Boolean = false, @SerialName("token") val token: String = "", @@ -176,5 +270,31 @@ internal data class LightAppStruct( @SerialName("title") val title: String = "", ) + + @Serializable + data class MultiMsgDetail( + @SerialName("uniseq") + val fileName: String, + @SerialName("resid") + val resId: String, + + @SerialName("summary") + val summary: String = "", + @SerialName("source") + val source: String = "", + ) } } + +private object BadBooleanSerializer : KSerializer by JsonPrimitive.serializer().map( + JsonPrimitive.serializer().descriptor, + deserialize = { prime -> + prime.booleanOrNull?.let { return@map it } + + prime.intOrNull?.let { return@map it != 0 } + + return@map prime.content.toBoolean() + }, + serialize = { JsonPrimitive(it) } +) +