From 6f0853e6e40be9ca2eed171ddc35a28aaf2d1694 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 14 Jun 2020 18:43:21 +0800 Subject: [PATCH] Implement `MessagePreSendEvent` and `MessagePostSendEvent`. Deprecate `MessageSendEvent` Fix #339 --- .../mirai/qqandroid/contact/FriendImpl.kt | 102 +++--- .../mirai/qqandroid/contact/GroupImpl.kt | 116 ++++--- .../mirai/qqandroid/contact/MemberImpl.kt | 64 ++-- .../net/mamoe/mirai/qqandroid/contact/util.kt | 72 ++-- .../chat/receive/MessageSvc.PbSendMsg.kt | 166 +++++----- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 14 +- .../kotlin/net.mamoe.mirai/contact/Friend.kt | 12 +- .../kotlin/net.mamoe.mirai/contact/Group.kt | 10 +- .../kotlin/net.mamoe.mirai/contact/Member.kt | 14 +- .../kotlin/net.mamoe.mirai/contact/User.kt | 12 +- .../net.mamoe.mirai/event/events/message.kt | 312 ++++++++++++++++-- 11 files changed, 623 insertions(+), 271 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt index 589aa159a..154c4a828 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt @@ -33,6 +33,7 @@ import net.mamoe.mirai.event.events.ImageUploadEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.OfflineFriendImage import net.mamoe.mirai.message.data.isContentNotEmpty import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.network.highway.postImage @@ -87,7 +88,11 @@ internal class FriendImpl( @Suppress("DuplicatedCode") override suspend fun sendMessage(message: Message): MessageReceipt { require(message.isContentNotEmpty()) { "message is empty" } - return sendMessageImpl(this, message).also { + return sendMessageImpl( + message, + friendReceiptConstructor = { MessageReceipt(it, this, null) }, + tReceiptConstructor = { MessageReceipt(it, this, null) } + ).also { logMessageSent(message) } } @@ -101,64 +106,63 @@ internal class FriendImpl( if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) { throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup") } - bot.network.run { - val response = LongConn.OffPicUp( + val response = bot.network.run { + LongConn.OffPicUp( bot.client, Cmd0x352.TryUpImgReq( srcUin = bot.id.toInt(), dstUin = id.toInt(), fileId = 0, - fileMd5 = @Suppress("INVISIBLE_MEMBER") image.md5, - fileSize = @Suppress("INVISIBLE_MEMBER") - image.input.size.toInt(), - fileName = @Suppress("INVISIBLE_MEMBER") image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName, + fileMd5 = image.md5, + fileSize = image.input.size.toInt(), + fileName = image.md5.toUHexString("") + "." + ExternalImage.defaultFormatName, imgOriginal = 1 ) ).sendAndExpect() + } - @Suppress("UNCHECKED_CAST", "DEPRECATION", "INVISIBLE_MEMBER") - return when (response) { - is LongConn.OffPicUp.Response.FileExists -> net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId) - .also { - ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() - } - is LongConn.OffPicUp.Response.RequireUpload -> { - bot.network.logger.verbose { - "[Http] Uploading friend image, size=${image.input.size.sizeToString()}" - } - - val time = measureTime { - MiraiPlatformUtils.Http.postImage( - "0x6ff0070", - bot.id, - null, - imageInput = image.input, - uKeyHex = response.uKey.toUHexString("") - ) - } - - bot.network.logger.verbose { - "[Http] Uploading friend image: succeed at ${(image.input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" - } - - /* - HighwayHelper.uploadImageToServers( - bot, - response.serverIp.zip(response.serverPort), - response.uKey, - image, - kind = "friend", - commandId = 1 - )*/ - // 为什么不能 ?? - - return net.mamoe.mirai.message.data.OfflineFriendImage(response.resourceId).also { - ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() - } + @Suppress("UNCHECKED_CAST", "DEPRECATION") + when (response) { + is LongConn.OffPicUp.Response.FileExists -> OfflineFriendImage(response.resourceId) + .also { + ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() } - is LongConn.OffPicUp.Response.Failed -> { - ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast() - error(response.message) + is LongConn.OffPicUp.Response.RequireUpload -> { + bot.network.logger.verbose { + "[Http] Uploading friend image, size=${image.input.size.sizeToString()}" } + + val time = measureTime { + MiraiPlatformUtils.Http.postImage( + "0x6ff0070", + bot.id, + null, + imageInput = image.input, + uKeyHex = response.uKey.toUHexString("") + ) + } + + bot.network.logger.verbose { + "[Http] Uploading friend image: succeed at ${(image.input.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" + } + + /* + HighwayHelper.uploadImageToServers( + bot, + response.serverIp.zip(response.serverPort), + response.uKey, + image, + kind = "friend", + commandId = 1 + )*/ + // 为什么不能 ?? + + OfflineFriendImage(response.resourceId).also { + ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast() + } + } + is LongConn.OffPicUp.Response.Failed -> { + ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast() + error(response.message) } } } finally { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt index ee6a6bd26..5bf804262 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt @@ -22,7 +22,6 @@ import net.mamoe.mirai.data.GroupInfo import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.* -import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.QQAndroidBot @@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToGroup import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService import net.mamoe.mirai.qqandroid.utils.estimateLength import net.mamoe.mirai.utils.* @@ -304,94 +304,92 @@ internal class GroupImpl( check(message.nodeList.size < 200) { throw MessageTooLargeException( this, message, message, - "ForwardMessage allows up to 200 nodes, but found ${message.nodeList.size}") + "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 = if (message !is LongMessage && message !is ForwardMessageInternal) { + val chain = kotlin.runCatching { + GroupMessagePreSendEvent(this, message).broadcast() + }.onSuccess { + check(!it.isCancelled) { + throw EventCancelledException("cancelled by GroupMessagePreSendEvent") + } + }.getOrElse { + throw EventCancelledException("exception thrown when broadcasting GroupMessagePreSendEvent", it) + }.message.asMessageChain() - if (message !is LongMessage && message !is ForwardMessageInternal) { - val event = GroupMessageSendEvent(this, message.asMessageChain()).broadcast() - if (event.isCancelled) { - throw EventCancelledException("cancelled by GroupMessageSendEvent") - } - - val length = event.message.estimateLength(703) // 阈值为700左右,限制到3的倍数 + val length = chain.estimateLength(703) // 阈值为700左右,限制到3的倍数 var imageCnt = 0 // 通过下方逻辑短路延迟计算 - if (length > 5000 || event.message.count { it is Image }.apply { imageCnt = this } > 50) { + if (length > 5000 || chain.count { it is Image }.apply { imageCnt = this } > 50) { throw MessageTooLargeException( - this, - message, - event.message, - "message(${event.message.joinToString( - "", - limit = 10 - )}) is too large. Allow up to 50 images or 5000 chars" + this, message, chain, + "message(${chain.joinToString("", limit = 10)}) is too large. Allow up to 50 images or 5000 chars" ) } 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) + return bot.lowLevelSendGroupLongOrForwardMessage( + this.id, + listOf( + ForwardMessage.Node( + senderId = bot.id, + time = currentTimeSeconds.toInt(), + message = chain, + senderName = bot.nick + ) ), - true, null) + true, null + ) } + chain + } else message.asMessageChain() - msg = event.message - } else msg = message.asMessageChain() msg.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() - lateinit var source: MessageSourceToGroupImpl - bot.network.run { - val response: MessageSvcPbSendMsg.Response = MessageSvcPbSendMsg.createToGroup( + + val result = bot.network.runCatching { + val source: MessageSourceToGroupImpl + MessageSvcPbSendMsg.createToGroup( bot.client, this@GroupImpl, msg, isForward ) { source = it - }.sendAndExpect() - if (response is MessageSvcPbSendMsg.Response.Failed) { - when (response.resultType) { - 120 -> throw BotIsBeingMutedException(this@GroupImpl) - 34 -> { - kotlin.runCatching { // allow retry once - 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) - } - } - else -> error("send message failed: $response") + }.sendAndExpect().let { + check(it is MessageSvcPbSendMsg.Response.SUCCESS) { + "Send temp message failed: $it" } } - } - try { - source.ensureSequenceIdAvailable() - } catch (e: Exception) { - bot.network.logger.warning { - "Timeout awaiting sequenceId for group message(${message.contentToString() - .take(10)}). Some features may not work properly" + try { + source.ensureSequenceIdAvailable() + } catch (e: Exception) { + bot.network.logger.warning { + "Timeout awaiting sequenceId for group message(${message.contentToString() + .take(10)}). Some features may not work properly" + } + bot.network.logger.warning(e) } - bot.network.logger.warning(e) + + MessageReceipt(source, this@GroupImpl, botAsMember) } - return MessageReceipt(source, this, botAsMember) + result.fold( + onSuccess = { + GroupMessagePostSendEvent(this, msg, null, it) + }, + onFailure = { + GroupMessagePostSendEvent(this, msg, it, null) + } + ).broadcast() + + return result.getOrThrow() } @Suppress("DEPRECATION") diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt index 80f43ab23..18eba1b7d 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt @@ -28,6 +28,7 @@ import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToTemp import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.getValue @@ -54,34 +55,59 @@ internal class MemberImpl constructor( override val id: Long = qq.id override val nick: String = qq.nick + @Suppress("UNCHECKED_CAST") @JvmSynthetic override suspend fun sendMessage(message: Message): MessageReceipt { require(message.isContentNotEmpty()) { "message is empty" } - return (this.asFriendOrNull()?.sendMessageImpl(this, message) ?: sendMessageImpl(message)) - .also { logMessageSent(message) } + val asFriend = this.asFriendOrNull() + + return (asFriend?.sendMessageImpl( + message, + friendReceiptConstructor = { MessageReceipt(it, asFriend, null) }, + tReceiptConstructor = { MessageReceipt(it, this, null) } + ) ?: sendMessageImpl(message)).also { logMessageSent(message) } } private suspend fun sendMessageImpl(message: Message): MessageReceipt { - val event = MessageSendEvent.TempMessageSendEvent(this, message.asMessageChain()).broadcast() - if (event.isCancelled) { - throw EventCancelledException("cancelled by TempMessageSendEvent") - } - event.message.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() + val chain = kotlin.runCatching { + TempMessagePreSendEvent(this, message).broadcast() + }.onSuccess { + check(!it.isCancelled) { + throw EventCancelledException("cancelled by TempMessagePreSendEvent") + } + }.getOrElse { + throw EventCancelledException("exception thrown when broadcasting TempMessagePreSendEvent", it) + }.message.asMessageChain() - lateinit var source: MessageSourceToTempImpl - bot.network.run { - check( - MessageSvcPbSendMsg.createToTemp( - bot.client, - this@MemberImpl, - message.asMessageChain() - ) { - source = it - }.sendAndExpect() is MessageSvcPbSendMsg.Response.SUCCESS - ) { "send message failed" } + chain.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() + + val result = bot.network.runCatching { + val source: MessageSourceToTempImpl + MessageSvcPbSendMsg.createToTemp( + bot.client, + this@MemberImpl, + chain + ) { + source = it + }.sendAndExpect().let { + check(it is MessageSvcPbSendMsg.Response.SUCCESS) { + "Send temp message failed: $it" + } + } + MessageReceipt(source, this@MemberImpl, null) } - return MessageReceipt(source, this, null) + + result.fold( + onSuccess = { + TempMessagePostSendEvent(this, chain, null, it) + }, + onFailure = { + TempMessagePostSendEvent(this, chain, it, null) + } + ).broadcast() + + return result.getOrThrow() } @JvmSynthetic diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt index ac3129560..920f62b31 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt @@ -12,40 +12,72 @@ package net.mamoe.mirai.qqandroid.contact import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Friend +import net.mamoe.mirai.contact.User import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.events.EventCancelledException -import net.mamoe.mirai.event.events.MessageSendEvent +import net.mamoe.mirai.event.events.FriendMessagePostSendEvent +import net.mamoe.mirai.event.events.FriendMessagePreSendEvent import net.mamoe.mirai.message.* import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.QuoteReply import net.mamoe.mirai.message.data.asMessageChain +import net.mamoe.mirai.message.data.firstIsInstanceOrNull import net.mamoe.mirai.qqandroid.asQQAndroidBot import net.mamoe.mirai.qqandroid.message.MessageSourceToFriendImpl import net.mamoe.mirai.qqandroid.message.ensureSequenceIdAvailable -import net.mamoe.mirai.qqandroid.message.firstIsInstanceOrNull import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToFriend import net.mamoe.mirai.utils.verbose +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract -internal suspend fun Friend.sendMessageImpl(generic: T, message: Message): MessageReceipt { - val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast() - if (event.isCancelled) { - throw EventCancelledException("cancelled by FriendMessageSendEvent") - } - event.message.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() - lateinit var source: MessageSourceToFriendImpl +internal suspend fun Friend.sendMessageImpl( + message: Message, + friendReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt, + tReceiptConstructor: (MessageSourceToFriendImpl) -> MessageReceipt +): MessageReceipt { + contract { callsInPlace(friendReceiptConstructor, InvocationKind.EXACTLY_ONCE) } val bot = bot.asQQAndroidBot() - bot.network.run { - check( - MessageSvcPbSendMsg.createToFriend( - bot.asQQAndroidBot().client, - this@sendMessageImpl, - event.message - ) { - source = it - }.sendAndExpect() is MessageSvcPbSendMsg.Response.SUCCESS - ) { "send message failed" } + + val chain = kotlin.runCatching { + FriendMessagePreSendEvent(this, message).broadcast() + }.onSuccess { + check(!it.isCancelled) { + throw EventCancelledException("cancelled by FriendMessagePreSendEvent") + } + }.getOrElse { + throw EventCancelledException("exception thrown when broadcasting FriendMessagePreSendEvent", it) + }.message.asMessageChain() + + chain.firstIsInstanceOrNull()?.source?.ensureSequenceIdAvailable() + + lateinit var source: MessageSourceToFriendImpl + val result = bot.network.runCatching { + MessageSvcPbSendMsg.createToFriend( + bot.client, + this@sendMessageImpl, + chain + ) { + source = it + }.sendAndExpect().let { + check(it is MessageSvcPbSendMsg.Response.SUCCESS) { + "Send temp message failed: $it" + } + } + friendReceiptConstructor(source) } - return MessageReceipt(source, generic, null) + + result.fold( + onSuccess = { + FriendMessagePostSendEvent(this, chain, null, it) + }, + onFailure = { + FriendMessagePostSendEvent(this, chain, it, null) + } + ).broadcast() + + result.getOrThrow() + return tReceiptConstructor(source) } internal fun Contact.logMessageSent(message: Message) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt index 63d71552d..956725041 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt @@ -34,6 +34,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.math.absoluteValue import kotlin.random.Random @@ -52,34 +54,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit - ): OutgoingPacket { - val rand = Random.nextInt().absoluteValue - val source = MessageSourceToFriendImpl( - internalId = rand, - sender = client.bot, - target = qq, - time = currentTimeSeconds.toInt(), - sequenceId = client.nextFriendSeq(), - originalMessage = message - ) - sourceCallback(source) - return createToFriend( - client, - qq.id, - message, - source) - } - /** * 发送好友消息 */ @Suppress("FunctionName") - private fun createToFriend( + internal fun createToFriendImpl( client: QQAndroidClient, toUin: Long, message: MessageChain, @@ -106,33 +85,10 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit - ): OutgoingPacket { - val source = MessageSourceToTempImpl( - internalId = Random.nextInt().absoluteValue, - sender = client.bot, - target = member, - time = currentTimeSeconds.toInt(), - sequenceId = client.atomicNextMessageSequenceId(), - originalMessage = message - ) - sourceCallback(source) - return createToTemp( - client, - (member.group as GroupImpl).uin, - member.id, - message, - source) - } - /** * 发送临时消息 */ - private fun createToTemp( + internal fun createToTempImpl( client: QQAndroidClient, groupUin: Long, toUin: Long, @@ -158,37 +114,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit - ): OutgoingPacket { - - val source = MessageSourceToGroupImpl( - group, - internalId = Random.nextInt().absoluteValue, - sender = client.bot, - target = group, - time = currentTimeSeconds.toInt(), - originalMessage = message//, - // sourceMessage = message - ) - sourceCallback(source) - return createToGroup( - client, - group.id, - message, - isForward, - source) - } - /** * 发送群消息 */ @Suppress("FunctionName") - private fun createToGroup( + internal fun createToGroupImpl( client: QQAndroidClient, groupCode: Long, message: MessageChain, @@ -231,7 +161,91 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit +): OutgoingPacket { + contract { + callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) + } + val source = MessageSourceToTempImpl( + internalId = Random.nextInt().absoluteValue, + sender = client.bot, + target = member, + time = currentTimeSeconds.toInt(), + sequenceId = client.atomicNextMessageSequenceId(), + originalMessage = message + ) + sourceCallback(source) + return createToTempImpl( + client, + (member.group as GroupImpl).uin, + member.id, + message, + source + ) +} + +internal inline fun MessageSvcPbSendMsg.createToFriend( + client: QQAndroidClient, + qq: Friend, + message: MessageChain, + crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit +): OutgoingPacket { + contract { + callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) + } + val rand = Random.nextInt().absoluteValue + val source = MessageSourceToFriendImpl( + internalId = rand, + sender = client.bot, + target = qq, + time = currentTimeSeconds.toInt(), + sequenceId = client.nextFriendSeq(), + originalMessage = message + ) + sourceCallback(source) + return createToFriendImpl( + client, + qq.id, + message, + source + ) +} + +internal inline fun MessageSvcPbSendMsg.createToGroup( + client: QQAndroidClient, + group: Group, + message: MessageChain, + isForward: Boolean, + crossinline sourceCallback: (MessageSourceToGroupImpl) -> Unit +): OutgoingPacket { + contract { + callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE) + } + val source = MessageSourceToGroupImpl( + group, + internalId = Random.nextInt().absoluteValue, + sender = client.bot, + target = group, + time = currentTimeSeconds.toInt(), + originalMessage = message//, + // sourceMessage = message + ) + sourceCallback(source) + return createToGroupImpl( + client, + group.id, + message, + isForward, + source + ) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index be172a567..590e41104 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -16,13 +16,11 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import net.mamoe.mirai.Bot import net.mamoe.mirai.JavaFriendlyAPI -import net.mamoe.mirai.event.events.BeforeImageUploadEvent -import net.mamoe.mirai.event.events.EventCancelledException -import net.mamoe.mirai.event.events.ImageUploadEvent -import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent -import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.message.quote +import net.mamoe.mirai.message.recall import net.mamoe.mirai.recall import net.mamoe.mirai.recallIn import net.mamoe.mirai.utils.ExternalImage @@ -57,8 +55,8 @@ abstract class Contact : ContactOrBot, CoroutineScope, ContactJavaFriendlyAPI { * * 单条消息最大可发送 4500 字符或 50 张图片. * - * @see FriendMessageSendEvent 发送好友信息事件, cancellable - * @see GroupMessageSendEvent 发送群消息事件. cancellable + * @see MessagePreSendEvent 发送消息前事件 + * @see MessagePostSendEvent 发送消息后事件 * * @throws EventCancelledException 当发送消息事件被取消时抛出 * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 @@ -71,7 +69,7 @@ abstract class Contact : ContactOrBot, CoroutineScope, ContactJavaFriendlyAPI { abstract suspend fun sendMessage(message: Message): MessageReceipt @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE") - @kotlin.internal.InlineOnly // purely virtual + @kotlin.internal.InlineOnly @JvmSynthetic suspend inline fun sendMessage(message: String): MessageReceipt { return sendMessage(message.toMessage()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt index 23dc1d031..77c7e6b38 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt @@ -14,12 +14,14 @@ package net.mamoe.mirai.contact import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.Bot import net.mamoe.mirai.event.events.EventCancelledException -import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent -import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.event.events.FriendMessagePostSendEvent +import net.mamoe.mirai.event.events.FriendMessagePreSendEvent import net.mamoe.mirai.message.FriendMessageEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.isContentEmpty import net.mamoe.mirai.message.data.toMessage +import net.mamoe.mirai.message.recall import kotlin.jvm.JvmSynthetic /** @@ -55,8 +57,8 @@ abstract class Friend : User(), CoroutineScope { * * 单条消息最大可发送 4500 字符或 50 张图片. * - * @see FriendMessageSendEvent 发送好友信息事件, cancellable - * @see GroupMessageSendEvent 发送群消息事件. cancellable + * @see FriendMessagePreSendEvent 发送消息前事件 + * @see FriendMessagePostSendEvent 发送消息后事件 * * @throws EventCancelledException 当发送消息事件被取消时抛出 * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 @@ -69,7 +71,7 @@ abstract class Friend : User(), CoroutineScope { abstract override suspend fun sendMessage(message: Message): MessageReceipt @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE") - @kotlin.internal.InlineOnly // purely virtual + @kotlin.internal.InlineOnly @JvmSynthetic suspend inline fun sendMessage(message: String): MessageReceipt { return sendMessage(message.toMessage()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt index 489eb7c9d..1f5d1887e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt @@ -17,12 +17,12 @@ import net.mamoe.mirai.JavaFriendlyAPI import net.mamoe.mirai.LowLevelAPI import net.mamoe.mirai.data.MemberInfo import net.mamoe.mirai.event.events.* -import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent -import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.isContentEmpty import net.mamoe.mirai.message.data.toMessage +import net.mamoe.mirai.message.recall import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.internal.runBlocking import kotlin.jvm.JvmName @@ -137,8 +137,8 @@ abstract class Group : Contact(), CoroutineScope { * * 单条消息最大可发送 4500 字符或 50 张图片. * - * @see FriendMessageSendEvent 发送好友信息事件, cancellable - * @see GroupMessageSendEvent 发送群消息事件. cancellable + * @see GroupMessagePreSendEvent 发送消息前事件 + * @see GroupMessagePostSendEvent 发送消息后事件 * * @throws EventCancelledException 当发送消息事件被取消时抛出 * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 @@ -154,7 +154,7 @@ abstract class Group : Contact(), CoroutineScope { * @see sendMessage */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE") - @kotlin.internal.InlineOnly // purely virtual + @kotlin.internal.InlineOnly @JvmSynthetic suspend inline fun sendMessage(message: String): MessageReceipt { return sendMessage(message.toMessage()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt index 3dfe6473d..c5d4beb53 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt @@ -17,7 +17,12 @@ import net.mamoe.mirai.event.events.* import net.mamoe.mirai.getFriendOrNull import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.isContentEmpty import net.mamoe.mirai.message.data.toMessage +import net.mamoe.mirai.message.recall +import net.mamoe.mirai.utils.hoursToSeconds +import net.mamoe.mirai.utils.daysToSeconds +import net.mamoe.mirai.utils.minutesToSeconds import net.mamoe.mirai.utils.WeakRefProperty import kotlin.jvm.JvmSynthetic import kotlin.time.Duration @@ -139,8 +144,11 @@ abstract class Member : MemberJavaFriendlyAPI, User() { * * 单条消息最大可发送 4500 字符或 50 张图片. * - * @see MessageSendEvent.FriendMessageSendEvent 发送好友信息事件, cancellable - * @see MessageSendEvent.GroupMessageSendEvent 发送群消息事件. cancellable + * @see FriendMessagePreSendEvent 当此成员是好友时发送消息前事件 + * @see FriendMessagePostSendEvent 当此成员是好友时发送消息后事件 + * + * @see TempMessagePreSendEvent 当此成员不是好友时发送消息前事件 + * @see TempMessagePostSendEvent 当此成员不是好友时发送消息后事件 * * @throws EventCancelledException 当发送消息事件被取消时抛出 * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 @@ -156,7 +164,7 @@ abstract class Member : MemberJavaFriendlyAPI, User() { * @see sendMessage */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE") - @kotlin.internal.InlineOnly // purely virtual + @kotlin.internal.InlineOnly @JvmSynthetic suspend inline fun sendMessage(message: String): MessageReceipt { return sendMessage(message.toMessage()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt index 4e19287f9..662e795f1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt @@ -13,11 +13,7 @@ package net.mamoe.mirai.contact import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.Bot -import net.mamoe.mirai.event.events.BeforeImageUploadEvent -import net.mamoe.mirai.event.events.EventCancelledException -import net.mamoe.mirai.event.events.ImageUploadEvent -import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent -import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent +import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Message @@ -58,8 +54,8 @@ abstract class User : Contact(), CoroutineScope { * * 单条消息最大可发送 4500 字符或 50 张图片. * - * @see FriendMessageSendEvent 发送好友信息事件, cancellable - * @see GroupMessageSendEvent 发送群消息事件. cancellable + * @see UserMessagePreSendEvent 发送消息前事件 + * @see UserMessagePostSendEvent 发送消息后事件 * * @throws EventCancelledException 当发送消息事件被取消时抛出 * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 @@ -75,7 +71,7 @@ abstract class User : Contact(), CoroutineScope { * @see sendMessage */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE") - @kotlin.internal.InlineOnly // purely virtual + @kotlin.internal.InlineOnly @JvmSynthetic suspend inline fun sendMessage(message: String): MessageReceipt { return sendMessage(message.toMessage()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/message.kt index d39413381..862e33129 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/message.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/message.kt @@ -9,7 +9,7 @@ @file:JvmMultifileClass @file:JvmName("BotEventsKt") -@file:Suppress("unused") +@file:Suppress("unused", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE") package net.mamoe.mirai.event.events @@ -19,41 +19,253 @@ import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.event.CancellableEvent import net.mamoe.mirai.event.events.ImageUploadEvent.Failed import net.mamoe.mirai.event.events.ImageUploadEvent.Succeed +import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Image +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.qqandroid.network.Packet import net.mamoe.mirai.utils.ExternalImage +import net.mamoe.mirai.utils.PlannedRemoval +import net.mamoe.mirai.utils.SinceMirai +import kotlin.internal.InlineOnly import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic +// region MessagePreSendEvent + /** - * 主动发送消息 + * 在发送消息前广播的事件. 可被 [取消][CancellableEvent.cancel]. + * + * 此事件总是在 [MessagePostSendEvent] 之前广播. + * + * 当 [MessagePreSendEvent] 被 [取消][CancellableEvent.cancel] 后: + * - [MessagePostSendEvent] 不会广播 + * - 消息不会发送. + * - [Contact.sendMessage] 会抛出异常 [EventCancelledException] * * @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径 */ -sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractEvent() { +@SinceMirai("1.1.0") +sealed class MessagePreSendEvent : BotEvent, BotActiveEvent, AbstractEvent(), CancellableEvent { + /** 发信目标. */ abstract val target: Contact - final override val bot: Bot - get() = target.bot + final override val bot: Bot get() = target.bot - data class GroupMessageSendEvent internal constructor( - override val target: Group, - var message: MessageChain - ) : MessageSendEvent(), CancellableEvent - - data class FriendMessageSendEvent internal constructor( - override val target: Friend, - var message: MessageChain - ) : MessageSendEvent(), CancellableEvent - - data class TempMessageSendEvent internal constructor( - override val target: Member, - var message: MessageChain - ) : MessageSendEvent(), CancellableEvent + /** 待发送的消息. 修改后将会同时应用于发送. */ + abstract var message: Message } +/** + * 在发送群消息前广播的事件. + * @see MessagePreSendEvent + */ +@SinceMirai("1.1.0") +data class GroupMessagePreSendEvent internal constructor( + /** 发信目标. */ + override val target: Group, + /** 待发送的消息. 修改后将会同时应用于发送. */ + override var message: Message +) : MessagePreSendEvent() + +/** + * 在发送好友或群临时会话消息前广播的事件. + * @see MessagePreSendEvent + */ +@SinceMirai("1.1.0") +sealed class UserMessagePreSendEvent : MessagePreSendEvent() { + /** 发信目标. */ + abstract override val target: User +} + +/** + * 在发送好友消息前广播的事件. + * @see MessagePreSendEvent + */ +@SinceMirai("1.1.0") +data class FriendMessagePreSendEvent internal constructor( + /** 发信目标. */ + override val target: Friend, + /** 待发送的消息. 修改后将会同时应用于发送. */ + override var message: Message +) : UserMessagePreSendEvent() + +/** + * 在发送群临时会话消息前广播的事件. + * @see MessagePreSendEvent + */ +@SinceMirai("1.1.0") +data class TempMessagePreSendEvent internal constructor( + /** 发信目标. */ + override val target: Member, + /** 待发送的消息. 修改后将会同时应用于发送. */ + override var message: Message +) : UserMessagePreSendEvent() { + val group get() = target.group +} + + +// endregion + +// region MessagePostSendEvent + +/** + * 在发送消息后广播的事件, 总是在 [MessagePreSendEvent] 之后广播. + * + * 只要 [MessagePreSendEvent] 未被 [取消][CancellableEvent.cancel], [MessagePostSendEvent] 就一定会被广播, 并携带 [发送时产生的异常][MessagePostSendEvent.exception] (如果有). + * + * 在此事件广播前, 消息一定已经发送成功, 或产生一个异常. + * + * @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径 + * @see MessagePreSendEvent + */ +@SinceMirai("1.1.0") +sealed class MessagePostSendEvent : BotEvent, BotActiveEvent, AbstractEvent() { + /** 发信目标. */ + abstract val target: C + final override val bot: Bot get() = target.bot + + /** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */ + abstract val message: MessageChain + + /** + * 发送消息时抛出的异常. `null` 表示消息成功发送. + * @see result + */ + abstract val exception: Throwable? + + /** + * 发送消息成功时的回执. `null` 表示消息发送失败. + * @see result + */ + abstract val receipt: MessageReceipt? +} + +/** + * 获取指代这条已经发送的消息的 [MessageSource]. 若消息发送失败, 返回 `null` + * @see MessagePostSendEvent.sourceResult + */ +@get:JvmSynthetic +@SinceMirai("1.1.0") +inline val MessagePostSendEvent<*>.source: MessageSource? + get() = receipt?.source + +/** + * 获取指代这条已经发送的消息的 [MessageSource], 并包装为 [kotlin.Result] + * @see MessagePostSendEvent.result + */ +@get:JvmSynthetic +@SinceMirai("1.1.0") +inline val MessagePostSendEvent<*>.sourceResult: Result + get() = result.map { it.source } + +/** + * 在此消息发送成功时返回 `true`. + * @see MessagePostSendEvent.exception + * @see MessagePostSendEvent.result + */ +@get:JvmSynthetic +@SinceMirai("1.1.0") +inline val MessagePostSendEvent<*>.isSuccess: Boolean + get() = exception == null + +/** + * 在此消息发送失败时返回 `true`. + * @see MessagePostSendEvent.exception + * @see MessagePostSendEvent.result + */ +@get:JvmSynthetic +@SinceMirai("1.1.0") +inline val MessagePostSendEvent<*>.isFailure: Boolean + get() = exception != null + +/** + * 将 [MessagePostSendEvent.exception] 与 [MessagePostSendEvent.receipt] 表示为 [Result] + */ +@InlineOnly +@SinceMirai("1.1.0") +inline val MessagePostSendEvent.result: Result> + get() = exception.let { exception -> if (exception != null) Result.failure(exception) else Result.success(receipt!!) } + +/** + * 在群消息发送后广播的事件. + * @see MessagePostSendEvent + */ +@SinceMirai("1.1.0") +data class GroupMessagePostSendEvent internal constructor( + /** 发信目标. */ + override val target: Group, + /** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */ + override val message: MessageChain, + /** + * 发送消息时抛出的异常. `null` 表示消息成功发送. + * @see result + */ + override val exception: Throwable?, + /** + * 发送消息成功时的回执. `null` 表示消息发送失败. + * @see result + */ + override val receipt: MessageReceipt? +) : MessagePostSendEvent() + +/** + * 在好友或群临时会话消息发送后广播的事件. + * @see MessagePostSendEvent + */ +@SinceMirai("1.1.0") +sealed class UserMessagePostSendEvent : MessagePostSendEvent() + +/** + * 在好友消息发送后广播的事件. + * @see MessagePostSendEvent + */ +@SinceMirai("1.1.0") +data class FriendMessagePostSendEvent internal constructor( + /** 发信目标. */ + override val target: Friend, + /** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */ + override val message: MessageChain, + /** + * 发送消息时抛出的异常. `null` 表示消息成功发送. + * @see result + */ + override val exception: Throwable?, + /** + * 发送消息成功时的回执. `null` 表示消息发送失败. + * @see result + */ + override val receipt: MessageReceipt? +) : UserMessagePostSendEvent() + +/** + * 在群临时会话消息发送后广播的事件. + * @see MessagePostSendEvent + */ +@SinceMirai("1.1.0") +data class TempMessagePostSendEvent internal constructor( + /** 发信目标. */ + override val target: Member, + /** 待发送的消息. 此为 [MessagePreSendEvent.message] 的最终值. */ + override val message: MessageChain, + /** + * 发送消息时抛出的异常. `null` 表示消息成功发送. + * @see result + */ + override val exception: Throwable?, + /** + * 发送消息成功时的回执. `null` 表示消息发送失败. + * @see result + */ + override val receipt: MessageReceipt? +) : UserMessagePostSendEvent() { + val group get() = target.group +} + +// endregion + /** * 消息撤回事件. 可是任意消息被任意人撤回. * @@ -177,3 +389,65 @@ sealed class ImageUploadEvent : BotEvent, BotActiveEvent, AbstractEvent() { ) : ImageUploadEvent() } + +// region deprecated + +/** + * 主动发送消息 + * + * @see Contact.sendMessage 发送消息. 为广播这个事件的唯一途径 + */ +@Suppress("DEPRECATION") +@PlannedRemoval("1.3.0") // arise deprecation level to ERROR in 1.2.0. +@Deprecated( + message = """ + 以 MessagePreSendEvent 和 MessagePostSendEvent 替换. + """, + replaceWith = ReplaceWith("MessagePreSendEvent", "net.mamoe.mirai.event.events.MessagePreSendEvent"), + level = DeprecationLevel.WARNING +) +sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractEvent() { + abstract val target: Contact + final override val bot: Bot + get() = target.bot + + @Deprecated( + message = """ + 以 GroupMessagePreSendEvent 和 GroupMessagePostSendEvent 替换. + """, + replaceWith = ReplaceWith("GroupMessagePreSendEvent", "net.mamoe.mirai.event.events.GroupMessagePreSendEvent"), + level = DeprecationLevel.WARNING + ) + data class GroupMessageSendEvent internal constructor( + override val target: Group, + var message: MessageChain + ) : MessageSendEvent(), CancellableEvent + + @Deprecated( + message = """ + 以 FriendMessagePreSendEvent 和 FriendMessagePostSendEvent 替换. + """, + replaceWith = ReplaceWith( + "FriendMessagePreSendEvent", + "net.mamoe.mirai.event.events.FriendMessagePreSendEvent" + ), + level = DeprecationLevel.WARNING + ) + data class FriendMessageSendEvent internal constructor( + override val target: Friend, + var message: MessageChain + ) : MessageSendEvent(), CancellableEvent + + @Deprecated( + message = """ + 以 TempMessagePreSendEvent 和 TempMessagePostSendEvent 替换. + """, + replaceWith = ReplaceWith("TempMessagePreSendEvent", "net.mamoe.mirai.event.events.TempMessagePreSendEvent"), + level = DeprecationLevel.WARNING + ) + data class TempMessageSendEvent internal constructor( + override val target: Member, + var message: MessageChain + ) : MessageSendEvent(), CancellableEvent +} +// endregion \ No newline at end of file