mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-24 14:30:09 +08:00
Support merged forward messages!
This commit is contained in:
parent
bb4c13d789
commit
1786c95e07
@ -550,12 +550,18 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
@JvmSynthetic
|
||||
@LowLevelAPI
|
||||
@MiraiExperimentalAPI
|
||||
internal suspend fun lowLevelSendLongGroupMessage(groupCode: Long, message: MessageChain): MessageReceipt<Group> {
|
||||
internal suspend fun lowLevelSendGroupLongOrForwardMessage(
|
||||
groupCode: Long,
|
||||
message: Collection<MessageChain>,
|
||||
isLong: Boolean
|
||||
): MessageReceipt<Group> {
|
||||
message.forEach {
|
||||
it.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||
}
|
||||
val group = getGroup(groupCode)
|
||||
|
||||
val time = currentTimeSeconds
|
||||
val sequenceId = client.atomicNextMessageSequenceId()
|
||||
message.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||
|
||||
network.run {
|
||||
val data = message.calculateValidationDataForGroup(
|
||||
@ -569,6 +575,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
|
||||
val response =
|
||||
MultiMsg.ApplyUp.createForGroupLongMessage(
|
||||
buType = if (isLong) 1 else 2,
|
||||
client = this@QQAndroidBotBase.client,
|
||||
messageData = data,
|
||||
dstUin = Group.calculateGroupUinByGroupCode(groupCode)
|
||||
@ -578,10 +585,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
when (response) {
|
||||
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
|
||||
error(
|
||||
"Internal error: message is too large, but this should be handled before sending. Message content:" +
|
||||
message.joinToString {
|
||||
"${it::class.simpleName}(l=${it.toString().length})"
|
||||
}
|
||||
"Internal error: message is too large, but this should be handled before sending. "
|
||||
)
|
||||
is MultiMsg.ApplyUp.Response.RequireUpload -> {
|
||||
resId = response.proto.msgResid
|
||||
@ -639,13 +643,27 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
}
|
||||
|
||||
return group.sendMessage(
|
||||
RichMessage.longMessage(
|
||||
brief = message.joinToString(limit = 27) { it.contentToString() },
|
||||
resId = resId,
|
||||
timeSeconds = time
|
||||
return if (isLong) {
|
||||
group.sendMessage(
|
||||
RichMessage.longMessage(
|
||||
brief = message.joinToString(limit = 27) { it.contentToString() },
|
||||
resId = resId,
|
||||
timeSeconds = time
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
group.sendMessage(
|
||||
RichMessage.forwardMessage(
|
||||
resId = resId,
|
||||
timeSeconds = time,
|
||||
preview = message.take(3).joinToString {
|
||||
"""
|
||||
<title size="26" color="#777777" maxLines="2" lineSpace="12">${it.joinToString(limit = 10)}</title>
|
||||
""".trimIndent()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -746,3 +764,26 @@ private fun RichMessage.Templates.longMessage(brief: String, resId: String, time
|
||||
|
||||
return LongMessage(template, resId)
|
||||
}
|
||||
|
||||
|
||||
private fun RichMessage.Templates.forwardMessage(
|
||||
resId: String,
|
||||
timeSeconds: Long,
|
||||
preview: String
|
||||
): ForwardMessageInternal {
|
||||
val template = """
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]"
|
||||
m_resid="$resId" m_fileName="$timeSeconds"
|
||||
tSum="3" sourceMsgId="0" url="" flag="3" adverSign="0" multiMsgFlag="0">
|
||||
<item layout="1" advertiser_id="0" aid="0">
|
||||
<title size="34" maxLines="2" lineSpace="12">群聊的聊天记录</title>
|
||||
$preview
|
||||
<hr hidden="false" style="0"/>
|
||||
<summary size="26" color="#777777">查看3条转发消息</summary>
|
||||
</item>
|
||||
<source name="聊天记录" icon="" action="" appid="-1"/>
|
||||
</msg>
|
||||
""".trimIndent()
|
||||
return ForwardMessageInternal(template)
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
|
||||
@file:Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
@file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
|
||||
|
||||
package net.mamoe.mirai.qqandroid.contact
|
||||
@ -289,10 +289,18 @@ internal class GroupImpl(
|
||||
|
||||
@OptIn(MiraiExperimentalAPI::class)
|
||||
private suspend fun sendMessageImpl(message: Message): MessageReceipt<Group> {
|
||||
if (message is MessageChain) {
|
||||
if (message.anyIsInstance<ForwardMessage>()) {
|
||||
return sendMessageImpl(message.singleOrNull() ?: error("ForwardMessage must be standalone"))
|
||||
}
|
||||
}
|
||||
if (message is ForwardMessage) {
|
||||
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, message.messageList, false)
|
||||
}
|
||||
|
||||
val msg: MessageChain
|
||||
|
||||
if (message !is LongMessage) {
|
||||
if (message !is LongMessage && message !is ForwardMessageInternal) {
|
||||
val event = GroupMessageSendEvent(this, message.asMessageChain()).broadcast()
|
||||
if (event.isCancelled) {
|
||||
throw EventCancelledException("cancelled by GroupMessageSendEvent")
|
||||
@ -314,7 +322,7 @@ internal class GroupImpl(
|
||||
}
|
||||
|
||||
if (length > 702 || imageCnt > 2)
|
||||
return bot.lowLevelSendLongGroupMessage(this.id, event.message)
|
||||
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, listOf(event.message), true)
|
||||
|
||||
msg = event.message
|
||||
} else msg = message.asMessageChain()
|
||||
@ -334,7 +342,7 @@ internal class GroupImpl(
|
||||
120 -> throw BotIsBeingMutedException(this@GroupImpl)
|
||||
34 -> {
|
||||
kotlin.runCatching { // allow retry once
|
||||
return bot.lowLevelSendLongGroupMessage(id, msg)
|
||||
return bot.lowLevelSendGroupLongOrForwardMessage(id, listOf(msg), true)
|
||||
}.getOrElse {
|
||||
throw IllegalStateException("internal error: send message failed(34)", it)
|
||||
}
|
||||
|
@ -52,6 +52,17 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
|
||||
if (it is RichMessage) {
|
||||
val content = MiraiPlatformUtils.zip(it.content.toByteArray())
|
||||
when (it) {
|
||||
is ForwardMessageInternal -> {
|
||||
elements.add(
|
||||
ImMsgBody.Elem(
|
||||
richMsg = ImMsgBody.RichMsg(
|
||||
serviceId = it.serviceId, // ok
|
||||
template1 = byteArrayOf(1) + content
|
||||
)
|
||||
)
|
||||
)
|
||||
transformOneMessage(UNSUPPORTED_MERGED_MESSAGE_PLAIN)
|
||||
}
|
||||
is LongMessage -> {
|
||||
check(longTextResId == null) { "There must be no more than one LongMessage element in the message chain" }
|
||||
elements.add(
|
||||
@ -136,6 +147,7 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
|
||||
}
|
||||
}
|
||||
}
|
||||
is ForwardMessage,
|
||||
is MessageSource, // mirai metadata only
|
||||
is RichMessage // already transformed above
|
||||
-> {
|
||||
@ -324,7 +336,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
|
||||
if (resId != null) {
|
||||
list.add(LongMessage(content, resId))
|
||||
} else {
|
||||
list.add(ForwardMessage(content))
|
||||
list.add(ForwardMessageInternal(content))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ internal class MessageValidationData @OptIn(MiraiInternalAPI::class) constructor
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
internal fun MessageChain.calculateValidationDataForGroup(
|
||||
internal fun Collection<MessageChain>.calculateValidationDataForGroup(
|
||||
sequenceId: Int,
|
||||
time: Int,
|
||||
random: UInt,
|
||||
@ -50,10 +50,9 @@ internal fun MessageChain.calculateValidationDataForGroup(
|
||||
botId: Long,
|
||||
botMemberNameCard: String
|
||||
): MessageValidationData {
|
||||
val richTextElems = this.toRichTextElems(forGroup = true, withGeneralFlags = false)
|
||||
|
||||
val msgTransmit = MsgTransmit.PbMultiMsgTransmit(
|
||||
msg = listOf(
|
||||
msg = this.map { chain ->
|
||||
MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = botId,
|
||||
@ -73,11 +72,11 @@ internal fun MessageChain.calculateValidationDataForGroup(
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = richTextElems.toMutableList()
|
||||
elems = chain.toRichTextElems(forGroup = true, withGeneralFlags = false).toMutableList()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val bytes = msgTransmit.toByteArray(MsgTransmit.PbMultiMsgTransmit.serializer())
|
||||
@ -105,6 +104,7 @@ internal class MultiMsg {
|
||||
|
||||
// captured from group
|
||||
fun createForGroupLongMessage(
|
||||
buType: Int,
|
||||
client: QQAndroidClient,
|
||||
messageData: MessageValidationData,
|
||||
dstUin: Long // group uin
|
||||
@ -112,7 +112,7 @@ internal class MultiMsg {
|
||||
writeProtoBuf(
|
||||
MultiMsg.ReqBody.serializer(),
|
||||
MultiMsg.ReqBody(
|
||||
buType = 1,
|
||||
buType = buType, // 1: long, 2: 合并转发
|
||||
buildVer = "8.2.0.1296",
|
||||
multimsgApplyupReq = listOf(
|
||||
MultiMsg.MultiMsgApplyUpReq(
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
|
||||
|
||||
/**
|
||||
* 合并转发
|
||||
*/
|
||||
@SinceMirai("0.39.0")
|
||||
class ForwardMessage(
|
||||
val messageList: Collection<MessageChain>
|
||||
) : MessageContent {
|
||||
companion object Key : Message.Key<ForwardMessage> {
|
||||
override val typeName: String get() = "ForwardMessage"
|
||||
}
|
||||
|
||||
override fun toString(): String = "[mirai:forward:$messageList]"
|
||||
|
||||
|
||||
private val contentToString: String by lazy {
|
||||
messageList.joinToString("\n")
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI
|
||||
override fun contentToString(): String = contentToString
|
||||
|
||||
override val length: Int
|
||||
get() = contentToString.length
|
||||
|
||||
override fun get(index: Int): Char = contentToString[length]
|
||||
|
||||
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
|
||||
contentToString.subSequence(startIndex, endIndex)
|
||||
|
||||
override fun compareTo(other: String): Int = contentToString.compareTo(other)
|
||||
}
|
@ -155,22 +155,18 @@ constructor(serviceId: Int = 60, content: String) : ServiceMessage(serviceId, co
|
||||
@SinceMirai("0.31.0")
|
||||
@MiraiExperimentalAPI
|
||||
class LongMessage internal constructor(content: String, val resId: String) : ServiceMessage(35, content) {
|
||||
companion object Key : Message.Key<XmlMessage> {
|
||||
companion object Key : Message.Key<LongMessage> {
|
||||
override val typeName: String get() = "LongMessage"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并转发消息
|
||||
* @suppress 此 API 不稳定
|
||||
* @suppress 此 API 非常不稳定
|
||||
*/
|
||||
@SinceMirai("0.36.0")
|
||||
@MiraiExperimentalAPI
|
||||
class ForwardMessage(content: String) : ServiceMessage(35, content) {
|
||||
companion object Key : Message.Key<XmlMessage> {
|
||||
override val typeName: String get() = "ForwardMessage"
|
||||
}
|
||||
}
|
||||
@SinceMirai("0.39.0")
|
||||
@MiraiExperimentalAPI("此 API 非常不稳定")
|
||||
internal class ForwardMessageInternal(content: String) : ServiceMessage(35, content)
|
||||
|
||||
/*
|
||||
commonElem=CommonElem#750141174 {
|
||||
|
Loading…
Reference in New Issue
Block a user