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
This commit is contained in:
Karlatemp 2021-10-28 20:44:18 +08:00 committed by GitHub
parent e61fbbcc70
commit 217907c58a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 5 deletions

View File

@ -31,6 +31,7 @@ public sealed interface TypeSafeMap {
public val size: Int
public operator fun <T> get(key: TypeKey<T>): T
public operator fun <T> get(key: TypeKey<T>, defaultValue: T): T
public operator fun <T> contains(key: TypeKey<T>): Boolean = get(key) != null
public fun toMapBoxed(): Map<TypeKey<*>, Any?>
@ -80,7 +81,10 @@ internal open class TypeSafeMapImpl(
override operator fun <T> get(key: TypeKey<T>): T =
map[key.name]?.uncheckedCast() ?: throw NoSuchElementException(key.toString())
override operator fun <T> contains(key: TypeKey<T>): Boolean = get(key) != null
override operator fun <T> get(key: TypeKey<T>, defaultValue: T): T =
map[key.name]?.uncheckedCast() ?: defaultValue
override operator fun <T> contains(key: TypeKey<T>): Boolean = map.containsKey(key.name)
override fun toMapBoxed(): Map<TypeKey<*>, Any?> = map.mapKeys { TypeKey<Any?>(it.key) }
override fun toMap(): Map<String, Any?> = map

View File

@ -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<FriendImpl>(contact) {
override val messageSvcSendMessage: (client: QQAndroidClient, contact: FriendImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (Deferred<OnlineMessageSource.Outgoing>) -> Unit) -> List<OutgoingPacket> =
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,

View File

@ -172,6 +172,7 @@ internal class OnlineMessageSourceToGroupImpl(
coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, IntArray>(
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) {

View File

@ -141,6 +141,7 @@ internal interface NoticePipelineContext : BotAware, NewContactSupport {
val KEY_MSG_INFO = TypeKey<MsgInfo>("msgInfo")
val NoticePipelineContext.fromSync get() = attributes[KEY_FROM_SYNC]
val NoticePipelineContext.fromSyncSafely get() = attributes[KEY_FROM_SYNC, false]
/**
* 来自 [MsgInfo] 的数据, [MsgType0x210], [MsgType0x2DC] 的处理过程之中可以使用

View File

@ -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<MsgOnlinePush.PbPushMsg>(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

View File

@ -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<MsgComm.Msg>(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<MsgComm.Msg>(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
)
)
}
}