From 217907c58ab0a36b58bb97e422b9a618ca0a00f0 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Thu, 28 Oct 2021 20:44:18 +0800 Subject: [PATCH] Support sending `MusicShare` to friends. (#1615) * Support sending `MusicShare` to friends. - Also fix a potential data out-of-bounds issues * `TypeSafeMap.get` with default values * Fix tests --- .../src/commonMain/kotlin/TypeSafeMap.kt | 6 ++- .../kotlin/contact/SendMessageHandler.kt | 25 ++++++++++- .../kotlin/message/outgoingSourceImpl.kt | 1 + .../components/NoticeProcessorPipeline.kt | 1 + .../notice/group/GroupMessageProcessor.kt | 6 ++- .../notice/priv/PrivateMessageProcessor.kt | 44 +++++++++++++++++++ 6 files changed, 78 insertions(+), 5 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt b/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt index 50d15e708..d13313152 100644 --- a/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt +++ b/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt @@ -31,6 +31,7 @@ public sealed interface TypeSafeMap { public val size: Int public operator fun get(key: TypeKey): T + public operator fun get(key: TypeKey, defaultValue: T): T public operator fun contains(key: TypeKey): Boolean = get(key) != null public fun toMapBoxed(): Map, Any?> @@ -80,7 +81,10 @@ internal open class TypeSafeMapImpl( override operator fun get(key: TypeKey): T = map[key.name]?.uncheckedCast() ?: throw NoSuchElementException(key.toString()) - override operator fun contains(key: TypeKey): Boolean = get(key) != null + override operator fun get(key: TypeKey, defaultValue: T): T = + map[key.name]?.uncheckedCast() ?: defaultValue + + override operator fun contains(key: TypeKey): Boolean = map.containsKey(key.name) override fun toMapBoxed(): Map, Any?> = map.mapKeys { TypeKey(it.key) } override fun toMap(): Map = map diff --git a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt index 1cfe4d919..f65a2c1fc 100644 --- a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt @@ -23,6 +23,7 @@ import net.mamoe.mirai.internal.network.components.ClockHolder.Companion.clock import net.mamoe.mirai.internal.network.components.MessageSvcSyncer import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.SendGroupMessageReceipt +import net.mamoe.mirai.internal.network.notice.priv.PrivateMessageProcessor.SendPrivateMessageReceipt import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement @@ -357,6 +358,25 @@ internal class FriendSendMessageHandler( ) : UserSendMessageHandler(contact) { override val messageSvcSendMessage: (client: QQAndroidClient, contact: FriendImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (Deferred) -> Unit) -> List = MessageSvcPbSendMsg::createToFriend + + override suspend fun constructSourceForSpecialMessage( + finalMessage: MessageChain, + fromAppId: Int + ): OnlineMessageSource.Outgoing { + + val receipt: SendPrivateMessageReceipt = nextEventOrNull(3000) { + it.bot === bot && it.fromAppId == fromAppId + } ?: SendPrivateMessageReceipt.EMPTY + + return OnlineMessageSourceToFriendImpl( + internalIds = intArrayOf(receipt.messageRandom), + sequenceIds = intArrayOf(receipt.sequenceId), + sender = bot, + target = contact, + time = bot.clock.server.currentTimeSeconds().toInt(), + originalMessage = finalMessage + ) + } } internal class StrangerSendMessageHandler( @@ -399,8 +419,9 @@ internal open class GroupSendMessageHandler( fromAppId: Int, ): OnlineMessageSource.Outgoing { - val receipt: SendGroupMessageReceipt = - nextEventOrNull(3000) { it.fromAppId == fromAppId } ?: SendGroupMessageReceipt.EMPTY + val receipt: SendGroupMessageReceipt = nextEventOrNull(3000) { + it.bot === bot && it.fromAppId == fromAppId + } ?: SendGroupMessageReceipt.EMPTY return OnlineMessageSourceToGroupImpl( contact, diff --git a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt index 47704bd77..42f2ebb4c 100644 --- a/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt @@ -172,6 +172,7 @@ internal class OnlineMessageSourceToGroupImpl( coroutineScope.asyncFromEventOrNull( timeoutMillis = 3000L * this@OnlineMessageSourceToGroupImpl.internalIds.size ) { + if (it.bot !== this.bot) return@asyncFromEventOrNull null if (it.messageRandom in this@OnlineMessageSourceToGroupImpl.internalIds) { multi[it.messageRandom] = it.sequenceId if (multi.size == this@OnlineMessageSourceToGroupImpl.internalIds.size) { diff --git a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt index 5da5ab64c..e1292665d 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt @@ -141,6 +141,7 @@ internal interface NoticePipelineContext : BotAware, NewContactSupport { val KEY_MSG_INFO = TypeKey("msgInfo") val NoticePipelineContext.fromSync get() = attributes[KEY_FROM_SYNC] + val NoticePipelineContext.fromSyncSafely get() = attributes[KEY_FROM_SYNC, false] /** * 来自 [MsgInfo] 的数据, 即 [MsgType0x210], [MsgType0x2DC] 的处理过程之中可以使用 diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt index a515416c1..32f929f34 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.network.notice.group +import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Member import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.event.Event @@ -44,6 +45,7 @@ internal class GroupMessageProcessor( private val logger: MiraiLogger, ) : SimpleNoticeProcessor(type()) { internal data class SendGroupMessageReceipt( + val bot: Bot?, val messageRandom: Int, val sequenceId: Int, val fromAppId: Int, @@ -53,7 +55,7 @@ internal class GroupMessageProcessor( } companion object { - val EMPTY = SendGroupMessageReceipt(0, 0, 0) + val EMPTY = SendGroupMessageReceipt(null, 0, 0, 0) } } @@ -81,7 +83,7 @@ internal class GroupMessageProcessor( // 3116=group music share // 2021=group file // message sent by bot - collect(SendGroupMessageReceipt(messageRandom, msgHead.msgSeq, msgHead.fromAppid)) + collect(SendGroupMessageReceipt(bot, messageRandom, msgHead.msgSeq, msgHead.fromAppid)) return } // else: sync form other device diff --git a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt index 603d5244c..b24dd7515 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt @@ -9,12 +9,17 @@ package net.mamoe.mirai.internal.network.notice.priv +import net.mamoe.mirai.Bot +import net.mamoe.mirai.event.AbstractEvent +import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.events.* import net.mamoe.mirai.internal.contact.* import net.mamoe.mirai.internal.getGroupByUinOrCode import net.mamoe.mirai.internal.message.toMessageChainOnline +import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.components.NoticePipelineContext import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.fromSync +import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.fromSyncSafely import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor import net.mamoe.mirai.internal.network.components.SsoProcessor import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor @@ -36,8 +41,35 @@ import net.mamoe.mirai.utils.context * @see GroupTempMessageSyncEvent */ internal class PrivateMessageProcessor : SimpleNoticeProcessor(type()) { + + internal data class SendPrivateMessageReceipt( + val bot: Bot?, + val messageRandom: Int, + val sequenceId: Int, + val fromAppId: Int, + ) : Packet, Event, Packet.NoLog, AbstractEvent() { + override fun toString(): String { + return "OnlinePush.PbC2CMsgSync.SendPrivateMessageReceipt(messageRandom=$messageRandom, sequenceId=$sequenceId)" + } + + companion object { + val EMPTY = SendPrivateMessageReceipt(null, 0, 0, 0) + } + } + override suspend fun NoticePipelineContext.processImpl(data: MsgComm.Msg) = data.context { markAsConsumed() + + if (fromSyncSafely) { + val msgFromAppid = msgHead.fromAppid + // 3116 = music share + // message sent by bot + if (msgFromAppid == 3116) { + handleSpecialMessageSendingResponse(data, msgFromAppid) + return + } + } + if (msgHead.fromUin == bot.id && fromSync) { // Bot send message to himself? or from other client? I am not the implementer. bot.client.sendFriendMessageSeq.updateIfSmallerThan(msgHead.msgSeq) @@ -107,4 +139,16 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor(type } } } + + private fun NoticePipelineContext.handleSpecialMessageSendingResponse( + data: MsgComm.Msg, + fromAppId: Int, + ) = data.context { + val messageRandom = data.msgBody.richText.attr?.random ?: return + collect( + SendPrivateMessageReceipt( + bot, messageRandom, data.msgHead.msgSeq, fromAppId + ) + ) + } } \ No newline at end of file