diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index f3fe87604..42d5dca55 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -91,6 +91,8 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve public abstract fun createImage (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image; public synthetic fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)Lkotlin/Unit; public fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)V + public fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Ljava/util/List; + public abstract fun downloadForwardMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/MessageChain; public abstract fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getBotFactory ()Lnet/mamoe/mirai/BotFactory; diff --git a/mirai-core-api/src/commonMain/kotlin/IMirai.kt b/mirai-core-api/src/commonMain/kotlin/IMirai.kt index 0a2764088..260e1350f 100644 --- a/mirai-core-api/src/commonMain/kotlin/IMirai.kt +++ b/mirai-core-api/src/commonMain/kotlin/IMirai.kt @@ -179,6 +179,15 @@ public interface IMirai : LowLevelApiAccessor { resourceId: String, ): MessageChain + /** + * @since 2.3 + */ + @JvmBlockingBridge + public suspend fun downloadForwardMessage( + bot: Bot, + resourceId: String, + ): List + /** * 通过好友验证 * diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt index 9ea0a8623..fd716b7cc 100644 --- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt @@ -965,6 +965,28 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { ) override suspend fun downloadLongMessage(bot: Bot, resourceId: String): MessageChain { + return downloadMultiMsgTransmit(bot, resourceId, ResourceKind.LONG_MESSAGE).msg + .toMessageChainNoSource(bot.id, 0, MessageSourceKind.GROUP) + } + + override suspend fun downloadForwardMessage(bot: Bot, resourceId: String): List { + return downloadMultiMsgTransmit(bot, resourceId, ResourceKind.FORWARD_MESSAGE).msg.map { msg -> + ForwardMessage.Node( + senderId = msg.msgHead.fromUin, + time = msg.msgHead.msgTime, + senderName = msg.msgHead.groupInfo?.groupCard + ?: msg.msgHead.fromNick.takeIf { it.isNotEmpty() } + ?: msg.msgHead.fromUin.toString(), + messageChain = listOf(msg).toMessageChainNoSource(bot.id, 0, MessageSourceKind.GROUP) + ) + } + } + + private suspend fun downloadMultiMsgTransmit( + bot: Bot, + resourceId: String, + resourceKind: ResourceKind, + ): MsgTransmit.PbMultiMsgTransmit { bot.asQQAndroidBot() when (val resp = MultiMsg.ApplyDown(bot.client, 2, resourceId, 1).sendAndExpect(bot)) { is MultiMsg.ApplyDown.Response.RequireDownload -> { @@ -977,7 +999,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { host = "https://ssl.htdata.qq.com", port = 443, times = 3, - resourceKind = ResourceKind.LONG_MESSAGE, + resourceKind = resourceKind, channelKind = ChannelKind.HTTP ) { host, _ -> http.get("$host${origin.thumbDownPara}") @@ -985,7 +1007,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { } else tryServersDownload( bot = bot, servers = origin.uint32DownIp.zip(origin.uint32DownPort), - resourceKind = ResourceKind.LONG_MESSAGE, + resourceKind = resourceKind, channelKind = ChannelKind.HTTP ) { ip, port -> http.get("http://$ip:$port${origin.thumbDownPara}") @@ -1011,9 +1033,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { } val content = down.msgContent.ungzip() - val transmit = content.loadAs(MsgTransmit.PbMultiMsgTransmit.serializer()) - - return transmit.msg.toMessageChainNoSource(bot.id, 0, MessageSourceKind.GROUP) + return content.loadAs(MsgTransmit.PbMultiMsgTransmit.serializer()) } MultiMsg.ApplyDown.Response.MessageTooLarge -> { error("Message is too large and cannot download") diff --git a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt index fea6bcaa1..11217fb02 100644 --- a/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt +++ b/mirai-core/src/commonMain/kotlin/message/LongMessageInternal.kt @@ -32,18 +32,57 @@ internal data class LongMessageInternal internal constructor(override val conten } // internal runtime value, not serializable -internal data class ForwardMessageInternal(override val content: String, val resId: String) : AbstractServiceMessage(), RefinableMessage { +@Suppress("RegExpRedundantEscape", "UnnecessaryVariable") +internal data class ForwardMessageInternal(override val content: String, val resId: String) : AbstractServiceMessage(), + RefinableMessage { override val serviceId: Int get() = 35 override suspend fun refine(contact: Contact, context: MessageChain): Message { - // val bot = contact.bot.asQQAndroidBot() - // TODO: 2021/2/2 Support forward message refinement - // https://github.com/mamoe/mirai/issues/623 - return this + val bot = contact.bot.asQQAndroidBot() + + val msgXml = content.substringAfter("(ServiceMessage, { it.safeCast() }) + AbstractPolymorphicMessageKey(ServiceMessage, { it.safeCast() }) { + + val SUMMARY_REGEX = """\(.*?)\<\/summary\>""".toRegex() + + @Suppress("SpellCheckingInspection") + val TITLE_REGEX = """\([\u0000-\uFFFF]*?)\<\/title\>""".toRegex() + + + fun String.findField(type: String): String { + return substringAfter("$type=\"", "") + .substringBefore("\"", "") + } + } } internal interface RefinableMessage : SingleMessage { diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index ca4dc75e3..fe9e1a8fd 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -391,11 +391,18 @@ private object ReceiveMessageTransformer { val resId = findStringProperty("m_resid") - val msg = when(findStringProperty("multiMsgFlag").toIntOrNull()) { + val msg = if (resId.isEmpty()) { + SimpleServiceMessage(35, content) + } else when (findStringProperty("multiMsgFlag").toIntOrNull()) { 1 -> LongMessageInternal(content, resId) 0 -> ForwardMessageInternal(content, resId) else -> { - SimpleServiceMessage(35, content) + // from PC QQ + if (findStringProperty("action") == "viewMultiMsg") { + ForwardMessageInternal(content, resId) + } else { + SimpleServiceMessage(35, content) + } } }