Commit 1b4e1475 authored by Him188's avatar Him188

Support ForwardMessage DSL

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