Commit 1b4e1475 authored by Him188's avatar Him188

Support ForwardMessage DSL

parent 6734403f
......@@ -552,12 +552,14 @@ internal abstract class QQAndroidBotBase constructor(
@MiraiExperimentalAPI
internal suspend fun lowLevelSendGroupLongOrForwardMessage(
groupCode: Long,
message: Collection<MessageChain>,
isLong: Boolean
message: Collection<ForwardMessage.INode>,
isLong: Boolean,
forwardMessage: ForwardMessage?
): MessageReceipt<Group> {
message.forEach {
it.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
it.message.ensureSequenceIdAvailable()
}
val group = getGroup(groupCode)
val time = currentTimeSeconds
......@@ -566,10 +568,8 @@ internal abstract class QQAndroidBotBase constructor(
network.run {
val data = message.calculateValidationDataForGroup(
sequenceId = sequenceId,
time = time.toInt(),
random = Random.nextInt().absoluteValue.toUInt(),
groupCode = groupCode,
botId = this@QQAndroidBotBase.id,
botMemberNameCard = group.botAsMember.nameCardOrNick
)
......@@ -646,21 +646,30 @@ internal abstract class QQAndroidBotBase constructor(
return if (isLong) {
group.sendMessage(
RichMessage.longMessage(
brief = message.joinToString(limit = 27) { it.contentToString() },
brief = message.joinToString(limit = 27) { it.message.contentToString() },
resId = resId,
timeSeconds = time
)
)
} else {
checkNotNull(forwardMessage) { "Internal error: forwardMessage is null when sending forward" }
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()
}
// preview = message.take(5).joinToString {
// """
// <title size="26" color="#777777" maxLines="2" lineSpace="12">${it.message.asMessageChain().joinToString(limit = 10)}</title>
// """.trimIndent()
// },
preview = forwardMessage.displayStrategy.generatePreview(forwardMessage).take(4)
.map {
"""<title size="26" color="#777777" maxLines="2" lineSpace="12">$it</title>"""
}.joinToString(""),
title = forwardMessage.displayStrategy.generateTitle(forwardMessage),
brief = forwardMessage.displayStrategy.generateBrief(forwardMessage),
source = forwardMessage.displayStrategy.generateSource(forwardMessage),
summary = forwardMessage.displayStrategy.generateSummary(forwardMessage)
)
)
}
......@@ -769,21 +778,25 @@ private fun RichMessage.Templates.longMessage(brief: String, resId: String, time
private fun RichMessage.Templates.forwardMessage(
resId: String,
timeSeconds: Long,
preview: String
preview: String,
title: String,
brief: String,
source: String,
summary: String
): ForwardMessageInternal {
val template = """
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<msg serviceID="35" templateID="1" action="viewMultiMsg" brief="[聊天记录]"
<msg serviceID="35" templateID="1" action="viewMultiMsg" brief="$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>
<title size="34" maxLines="2" lineSpace="12">$title</title>
$preview
<hr hidden="false" style="0"/>
<summary size="26" color="#777777">查看3条转发消息</summary>
<summary size="26" color="#777777">$summary</summary>
</item>
<source name="聊天记录" icon="" action="" appid="-1"/>
<source name="$source" icon="" action="" appid="-1"/>
</msg>
""".trimIndent()
""".trimIndent().replace("\n", " ")
return ForwardMessageInternal(template)
}
\ No newline at end of file
......@@ -295,7 +295,13 @@ internal class GroupImpl(
}
}
if (message is ForwardMessage) {
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, message.messageList, false)
check(message.nodeList.size < 200) {
throw MessageTooLargeException(
this, message, message,
"ForwardMessage allows up to 200 nodes, but found ${message.nodeList.size}")
}
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, message.nodeList, false, message)
}
val msg: MessageChain
......@@ -321,8 +327,16 @@ internal class GroupImpl(
)
}
if (length > 702 || imageCnt > 2)
return bot.lowLevelSendGroupLongOrForwardMessage(this.id, listOf(event.message), true)
if (length > 702 || imageCnt > 2) {
return bot.lowLevelSendGroupLongOrForwardMessage(this.id,
listOf(ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds.toInt(),
message = event.message,
senderName = bot.nick)
),
true, null)
}
msg = event.message
} else msg = message.asMessageChain()
......@@ -343,7 +357,15 @@ internal class GroupImpl(
120 -> throw BotIsBeingMutedException(this@GroupImpl)
34 -> {
kotlin.runCatching { // allow retry once
return bot.lowLevelSendGroupLongOrForwardMessage(id, listOf(msg), true)
return bot.lowLevelSendGroupLongOrForwardMessage(
id, listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds.toInt(),
message = msg,
senderName = bot.nick
)
), true, null)
}.getOrElse {
throw IllegalStateException("internal error: send message failed(34)", it)
}
......
......@@ -15,6 +15,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.OnlineMessageSource
......@@ -47,6 +48,18 @@ internal suspend inline fun MessageSource.ensureSequenceIdAvailable() {
}*/
}
@Suppress("RedundantSuspendModifier", "unused")
internal suspend inline fun Message.ensureSequenceIdAvailable() {
// no suspend.
// obsolete but keep for future
return
/*
if (this is MessageSourceToGroupImpl) {
this.ensureSequenceIdAvailable()
}*/
}
internal class MessageSourceFromFriendImpl(
override val bot: Bot,
val msg: MsgComm.Msg
......
......@@ -9,32 +9,32 @@ import kotlin.jvm.JvmField
@Serializable
internal class MultiMsg : ProtoBuf {
@Serializable
internal class ExternMsg(
internal class ExternMsg(
@ProtoId(1) @JvmField val channelType: Int = 0
) : ProtoBuf
@Serializable
internal class MultiMsgApplyDownReq(
internal class MultiMsgApplyDownReq(
@ProtoId(1) @JvmField val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(2) @JvmField val msgType: Int = 0,
@ProtoId(3) @JvmField val srcUin: Long = 0L
) : ProtoBuf
@Serializable
internal class MultiMsgApplyDownRsp(
internal class MultiMsgApplyDownRsp(
@ProtoId(1) @JvmField val result: Int = 0,
@ProtoId(2) @JvmField val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) @JvmField val msgKey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) @JvmField val uint32DownIp: List<Int>? = null,
@ProtoId(5) @JvmField val uint32DownPort: List<Int>? = null,
@ProtoId(6) @JvmField val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(7) @JvmField val msgExternInfo: MultiMsg.ExternMsg? = null,
@ProtoId(7) @JvmField val msgExternInfo: ExternMsg? = null,
@ProtoId(8) @JvmField val bytesDownIpV6: List<ByteArray>? = null,
@ProtoId(9) @JvmField val uint32DownV6Port: List<Int>? = null
) : ProtoBuf
@Serializable
internal class MultiMsgApplyUpReq(
internal class MultiMsgApplyUpReq(
@ProtoId(1) @JvmField val dstUin: Long = 0L,
@ProtoId(2) @JvmField val msgSize: Long = 0L,
@ProtoId(3) @JvmField val msgMd5: ByteArray = EMPTY_BYTE_ARRAY,
......@@ -43,24 +43,24 @@ internal class MultiMsgApplyUpReq(
) : ProtoBuf
@Serializable
internal class MultiMsgApplyUpRsp(
internal class MultiMsgApplyUpRsp(
@ProtoId(1) @JvmField val result: Int = 0,
@ProtoId(2) @JvmField val msgResid: String = "",
@ProtoId(3) @JvmField val msgUkey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) @JvmField val uint32UpIp: List<Int>,
@ProtoId(5) @JvmField val uint32UpPort: List<Int>,
@ProtoId(4) @JvmField val uint32UpIp: List<Int> = listOf(),
@ProtoId(5) @JvmField val uint32UpPort: List<Int> = listOf(),
@ProtoId(6) @JvmField val blockSize: Long = 0L,
@ProtoId(7) @JvmField val upOffset: Long = 0L,
@ProtoId(8) @JvmField val applyId: Int = 0,
@ProtoId(9) @JvmField val msgKey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(10) @JvmField val msgSig: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(11) @JvmField val msgExternInfo: MultiMsg.ExternMsg? = null,
@ProtoId(11) @JvmField val msgExternInfo: ExternMsg? = null,
@ProtoId(12) @JvmField val bytesUpIpV6: List<ByteArray>? = null,
@ProtoId(13) @JvmField val uint32UpV6Port: List<Int>? = null
) : ProtoBuf
@Serializable
internal class ReqBody(
internal class ReqBody(
@ProtoId(1) @JvmField val subcmd: Int = 0,
@ProtoId(2) @JvmField val termType: Int = 0,
@ProtoId(3) @JvmField val platformType: Int = 0,
......@@ -73,7 +73,7 @@ internal class ReqBody(
) : ProtoBuf
@Serializable
internal class RspBody(
internal class RspBody(
@ProtoId(1) @JvmField val subcmd: Int = 0,
@ProtoId(2) @JvmField val multimsgApplyupRsp: List<MultiMsg.MultiMsgApplyUpRsp>? = null,
@ProtoId(3) @JvmField val multimsgApplydownRsp: List<MultiMsg.MultiMsgApplyDownRsp>? = null
......
......@@ -12,7 +12,8 @@
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.ForwardMessage
import net.mamoe.mirai.message.data.asMessageChain
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.message.toRichTextElems
import net.mamoe.mirai.qqandroid.network.Packet
......@@ -42,12 +43,10 @@ internal class MessageValidationData @OptIn(MiraiInternalAPI::class) constructor
}
@OptIn(MiraiInternalAPI::class)
internal fun Collection<MessageChain>.calculateValidationDataForGroup(
internal fun Collection<ForwardMessage.INode>.calculateValidationDataForGroup(
sequenceId: Int,
time: Int,
random: UInt,
groupCode: Long,
botId: Long,
botMemberNameCard: String
): MessageValidationData {
......@@ -55,9 +54,9 @@ internal fun Collection<MessageChain>.calculateValidationDataForGroup(
msg = this.map { chain ->
MsgComm.Msg(
msgHead = MsgComm.MsgHead(
fromUin = botId,
fromUin = chain.senderId,
msgSeq = sequenceId,
msgTime = time,
msgTime = chain.time,
msgUid = 0x01000000000000000L or random.toLong(),
mutiltransHead = MsgComm.MutilTransHead(
status = 0,
......@@ -66,13 +65,14 @@ internal fun Collection<MessageChain>.calculateValidationDataForGroup(
msgType = 82, // troop
groupInfo = MsgComm.GroupInfo(
groupCode = groupCode,
groupCard = botMemberNameCard // Cinnamon
groupCard = chain.senderName // Cinnamon
),
isSrcMsg = false
),
msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText(
elems = chain.toRichTextElems(forGroup = true, withGeneralFlags = false).toMutableList()
elems = chain.message.asMessageChain()
.toRichTextElems(forGroup = true, withGeneralFlags = false).toMutableList()
)
)
)
......
......@@ -358,6 +358,7 @@ interface ConstrainSingle<out M : Message> : MessageMetadata {
* @see Image 图片
* @see RichMessage 富文本
* @see Face 原生表情
* @see ForwardMessage 合并转发
*/
interface MessageContent : SingleMessage
......
......@@ -13,6 +13,7 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
import kotlin.annotation.AnnotationTarget.*
......@@ -151,6 +152,8 @@ constructor(serviceId: Int = 60, content: String) : ServiceMessage(serviceId, co
/**
* 长消息.
*
* 不需要手动区分长消息和普通消息, 在 [Contact.sendMessage] 时会自动判断.
*/
@SinceMirai("0.31.0")
@MiraiExperimentalAPI
......@@ -164,8 +167,8 @@ class LongMessage internal constructor(content: String, val resId: String) : Ser
* 合并转发消息
* @suppress 此 API 非常不稳定
*/
@OptIn(MiraiExperimentalAPI::class)
@SinceMirai("0.39.0")
@MiraiExperimentalAPI("此 API 非常不稳定")
internal class ForwardMessageInternal(content: String) : ServiceMessage(35, content)
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment