Kaynağa Gözat

Fix ambiguous message event names. Fix #299.

Binary compatibility until 1.2.0.

Migrations:
- `MessagePacket` deprecated in favor of `MessageEvent`
- `MessagePacketBase` deprecated in favor of `MessageEvent`
- `ContactMessage` -> `MessageEvent`
- `FriendMessage` -> `FriendMessageEvent`
- `GroupMessage` -> `GroupMessageEvent`
- `TempMessage` -> `TempMessageEvent`
Him188 5 yıl önce
ebeveyn
işleme
4ee27f2069
18 değiştirilmiş dosya ile 585 ekleme ve 483 silme
  1. 4 4
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
  2. 2 2
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
  3. 4 4
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
  4. 2 2
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
  5. 2 2
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt
  6. 18 17
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt
  7. 8 8
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt
  8. 8 8
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/select.kt
  9. 9 9
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt
  10. 0 383
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt
  11. 9 5
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessageEvent.kt
  12. 9 7
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessageEvent.kt
  13. 221 0
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageEvent.kt
  14. 8 4
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/TempMessageEvent.kt
  15. 8 5
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/ForwardMessage.kt
  16. 3 3
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
  17. 243 0
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/utils.kt
  18. 27 20
      mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageEventPlatform.kt

+ 4 - 4
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt

@@ -59,17 +59,17 @@ internal fun Contact.logMessageSent(message: Message) {
 }
 
 @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
-internal fun ContactMessage.logMessageReceived() {
+internal fun MessageEvent.logMessageReceived() {
     when (this) {
-        is GroupMessage -> bot.logger.verbose {
+        is GroupMessageEvent -> bot.logger.verbose {
             "[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(${sender.id}) -> ${message.toString()
                 .singleLine()}"
         }
-        is TempMessage -> bot.logger.verbose {
+        is TempMessageEvent -> bot.logger.verbose {
             "[${group.name.singleLine()}(${group.id})] ${senderName.singleLine()}(Temp ${sender.id}) -> ${message.toString()
                 .singleLine()}"
         }
-        is FriendMessage -> bot.logger.verbose {
+        is FriendMessageEvent -> bot.logger.verbose {
             "${sender.nick.singleLine()}(${sender.id}) -> ${message.toString().singleLine()}"
         }
     }

+ 2 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt

@@ -22,7 +22,7 @@ import kotlinx.io.core.use
 import net.mamoe.mirai.event.*
 import net.mamoe.mirai.event.events.BotOfflineEvent
 import net.mamoe.mirai.event.events.BotOnlineEvent
-import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.message.MessageEvent
 import net.mamoe.mirai.network.BotNetworkHandler
 import net.mamoe.mirai.network.UnsupportedSMSLoginException
 import net.mamoe.mirai.network.WrongPasswordException
@@ -495,7 +495,7 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
                 is Packet.NoLog -> {
                     // nothing to do
                 }
-                is ContactMessage -> packet.logMessageReceived()
+                is MessageEvent -> packet.logMessageReceived()
                 is Event -> bot.logger.verbose { "Event: ${packet.toString().singleLine()}" }
                 else -> logger.verbose { "Packet: ${packet.toString().singleLine()}" }
             }

+ 4 - 4
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt

@@ -29,8 +29,8 @@ import net.mamoe.mirai.event.events.BotJoinGroupEvent
 import net.mamoe.mirai.event.events.BotOfflineEvent
 import net.mamoe.mirai.event.events.MemberJoinEvent
 import net.mamoe.mirai.getFriendOrNull
-import net.mamoe.mirai.message.FriendMessage
-import net.mamoe.mirai.message.TempMessage
+import net.mamoe.mirai.message.FriendMessageEvent
+import net.mamoe.mirai.message.TempMessageEvent
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.contact.GroupImpl
@@ -274,7 +274,7 @@ internal class MessageSvc {
                             friend.lastMessageSequence.loop { instant ->
                                 if (msg.msgHead.msgSeq > instant) {
                                     if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
-                                        return@mapNotNull FriendMessage(
+                                        return@mapNotNull FriendMessageEvent(
                                             friend,
                                             msg.toMessageChain(bot, groupIdOrZero = 0, onlineSource = true),
                                             msg.msgHead.msgTime
@@ -297,7 +297,7 @@ internal class MessageSvc {
                             member.lastMessageSequence.loop { instant ->
                                 if (msg.msgHead.msgSeq > instant) {
                                     if (member.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
-                                        return@mapNotNull TempMessage(
+                                        return@mapNotNull TempMessageEvent(
                                             member,
                                             msg.toMessageChain(
                                                 bot,

+ 2 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt

@@ -23,7 +23,7 @@ import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.getFriendOrNull
 import net.mamoe.mirai.getGroupOrNull
-import net.mamoe.mirai.message.GroupMessage
+import net.mamoe.mirai.message.GroupMessageEvent
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.contact.*
 import net.mamoe.mirai.qqandroid.message.contextualBugReportException
@@ -109,7 +109,7 @@ internal class OnlinePush {
             }
 
             val flags = extraInfo?.flags ?: 0
-            return GroupMessage(
+            return GroupMessageEvent(
                 senderName = name.also {
                     if (it != sender.nameCard) {
                         val origin = sender._nameCard

+ 2 - 2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt

@@ -16,7 +16,7 @@ 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.message.FriendMessage
+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.toMessage
@@ -29,7 +29,7 @@ import kotlin.jvm.JvmSynthetic
  * 对于同一个 [Bot], 任何一个人的 [Friend] 实例都是单一的.
  * [Friend] 无法通过任何方式直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
  *
- * @see FriendMessage
+ * @see FriendMessageEvent
  */
 @Suppress("DEPRECATION_ERROR")
 abstract class Friend : User(), CoroutineScope {

+ 18 - 17
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt

@@ -17,10 +17,10 @@ package net.mamoe.mirai.event
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.event.internal.*
-import net.mamoe.mirai.message.ContactMessage
-import net.mamoe.mirai.message.FriendMessage
-import net.mamoe.mirai.message.GroupMessage
-import net.mamoe.mirai.message.TempMessage
+import net.mamoe.mirai.message.FriendMessageEvent
+import net.mamoe.mirai.message.GroupMessageEvent
+import net.mamoe.mirai.message.MessageEvent
+import net.mamoe.mirai.message.TempMessageEvent
 import net.mamoe.mirai.message.data.*
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmOverloads
@@ -31,7 +31,7 @@ import kotlin.jvm.JvmSynthetic
  * 消息事件的处理器.
  *
  * 注:
- * 接受者 T 为 [ContactMessage]
+ * 接受者 T 为 [MessageEvent]
  * 参数 String 为 转为字符串了的消息 ([Message.toString])
  */
 typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
@@ -47,7 +47,7 @@ typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
  * @see subscribeFriendMessages
  */
 @MessageDsl
-open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
+open class MessageSubscribersBuilder<M : MessageEvent, out Ret, R : RR, RR>(
     /**
      * 用于 [MessageListener] 无返回值的替代.
      */
@@ -231,7 +231,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
 
     /** 如果是这个人发的消息. 消息目前只会是群消息 */
     @MessageDsl
-    fun sentBy(name: String): ListeningFilter = content { this is GroupMessage && this.senderName == name }
+    fun sentBy(name: String): ListeningFilter = content { this is GroupMessageEvent && this.senderName == name }
 
     /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
     @MessageDsl
@@ -247,36 +247,37 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
 
     /** 如果是好友发来的消息 */
     @MessageDsl
-    fun sentByFriend(onEvent: MessageListener<FriendMessage, R>): Ret =
-        content({ this is FriendMessage }) { onEvent(this as FriendMessage, it) }
+    fun sentByFriend(onEvent: MessageListener<FriendMessageEvent, R>): Ret =
+        content({ this is FriendMessageEvent }) { onEvent(this as FriendMessageEvent, it) }
 
     /** 如果是好友发来的消息 */
     @MessageDsl
-    fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage }
+    fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessageEvent }
 
-    /** 如果是好友发来的消息 */
+    /** 如果是群临时会话消息 */
     @MessageDsl
-    fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessage }
+    fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessageEvent }
 
     /** 如果是管理员或群主发的消息 */
     @MessageDsl
-    fun sentByOperator(): ListeningFilter = content { this is GroupMessage && sender.permission.isOperator() }
+    fun sentByOperator(): ListeningFilter = content { this is GroupMessageEvent && sender.permission.isOperator() }
 
     /** 如果是管理员发的消息 */
     @MessageDsl
-    fun sentByAdministrator(): ListeningFilter = content { this is GroupMessage && sender.permission.isAdministrator() }
+    fun sentByAdministrator(): ListeningFilter =
+        content { this is GroupMessageEvent && sender.permission.isAdministrator() }
 
     /** 如果是群主发的消息 */
     @MessageDsl
-    fun sentByOwner(): ListeningFilter = content { this is GroupMessage && sender.isOwner() }
+    fun sentByOwner(): ListeningFilter = content { this is GroupMessageEvent && sender.isOwner() }
 
     /** 如果是来自这个群的消息 */
     @MessageDsl
-    fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessage && group.id == groupId }
+    fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessageEvent && group.id == groupId }
 
     /** 如果是来自这个群的消息 */
     @MessageDsl
-    fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessage && group.id == group.id }
+    fun sentFrom(group: Group): ListeningFilter = content { this is GroupMessageEvent && group.id == group.id }
 
     /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At] */
     @MessageDsl

+ 8 - 8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt

@@ -12,7 +12,7 @@ package net.mamoe.mirai.event.internal
 import net.mamoe.mirai.event.MessageDsl
 import net.mamoe.mirai.event.MessageListener
 import net.mamoe.mirai.event.MessageSubscribersBuilder
-import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.message.MessageEvent
 
 
 /*
@@ -20,13 +20,13 @@ import net.mamoe.mirai.message.ContactMessage
  */
 
 @MessageDsl
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
     filter: M.(String) -> Boolean,
     onEvent: MessageListener<M, RR>
 ): Ret =
     subscriber(filter) { onEvent(this, it) }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
     suffix: String,
     removeSuffix: Boolean = true,
     trim: Boolean = true,
@@ -46,7 +46,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
     }
 }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
     prefix: String,
     removePrefix: Boolean = true,
     trim: Boolean = true,
@@ -64,7 +64,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
     }
 }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
     sub: Array<out String>,
     ignoreCase: Boolean = false,
     trim: Boolean = true
@@ -76,7 +76,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
         content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
     }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
     vararg sub: String,
     ignoreCase: Boolean = false,
     trim: Boolean = true
@@ -86,7 +86,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
         content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
     } else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
     equals: String,
     ignoreCase: Boolean = false,
     trim: Boolean = true
@@ -99,7 +99,7 @@ internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M,
     }
 }
 
-internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
+internal fun <M : MessageEvent, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
     sub: String,
     ignoreCase: Boolean = false,
     trim: Boolean = true,

+ 8 - 8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/select.kt

@@ -12,7 +12,7 @@
 package net.mamoe.mirai.event
 
 import kotlinx.coroutines.*
-import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.message.MessageEvent
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.PlainText
 import net.mamoe.mirai.message.isContextIdenticalWith
@@ -61,7 +61,7 @@ import kotlin.jvm.JvmSynthetic
  */
 @SinceMirai("0.29.0")
 @Suppress("unused")
-suspend inline fun <reified T : ContactMessage> T.whileSelectMessages(
+suspend inline fun <reified T : MessageEvent> T.whileSelectMessages(
     timeoutMillis: Long = -1,
     filterContext: Boolean = true,
     crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
@@ -74,7 +74,7 @@ suspend inline fun <reified T : ContactMessage> T.whileSelectMessages(
 @MiraiExperimentalAPI
 @SinceMirai("0.29.0")
 @JvmName("selectMessages1")
-suspend inline fun <reified T : ContactMessage> T.selectMessagesUnit(
+suspend inline fun <reified T : MessageEvent> T.selectMessagesUnit(
     timeoutMillis: Long = -1,
     filterContext: Boolean = true,
     crossinline selectBuilder: @MessageDsl MessageSelectBuilderUnit<T, Unit>.() -> Unit
@@ -104,7 +104,7 @@ suspend inline fun <reified T : ContactMessage> T.selectMessagesUnit(
 @SinceMirai("0.29.0")
 @Suppress("unused") // false positive
 // @BuilderInference // https://youtrack.jetbrains.com/issue/KT-37716
-suspend inline fun <reified T : ContactMessage, R> T.selectMessages(
+suspend inline fun <reified T : MessageEvent, R> T.selectMessages(
     timeoutMillis: Long = -1,
     filterContext: Boolean = true,
     // @BuilderInference
@@ -121,7 +121,7 @@ suspend inline fun <reified T : ContactMessage, R> T.selectMessages(
  */
 @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
 @SinceMirai("0.29.0")
-abstract class MessageSelectBuilder<M : ContactMessage, R> @PublishedApi internal constructor(
+abstract class MessageSelectBuilder<M : MessageEvent, R> @PublishedApi internal constructor(
     ownerMessagePacket: M,
     stub: Any?,
     subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@@ -236,7 +236,7 @@ abstract class MessageSelectBuilder<M : ContactMessage, R> @PublishedApi interna
  * @see MessageSubscribersBuilder 查看上层 API
  */
 @SinceMirai("0.29.0")
-abstract class MessageSelectBuilderUnit<M : ContactMessage, R> @PublishedApi internal constructor(
+abstract class MessageSelectBuilderUnit<M : MessageEvent, R> @PublishedApi internal constructor(
     private val ownerMessagePacket: M,
     stub: Any?,
     subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
@@ -480,7 +480,7 @@ internal val ExceptionHandlerIgnoringCancellationException = CoroutineExceptionH
 @PublishedApi
 @BuilderInference
 @OptIn(ExperimentalTypeInference::class)
-internal suspend inline fun <reified T : ContactMessage, R> T.selectMessagesImpl(
+internal suspend inline fun <reified T : MessageEvent, R> T.selectMessagesImpl(
     timeoutMillis: Long = -1,
     isUnit: Boolean,
     filterContext: Boolean = true,
@@ -579,7 +579,7 @@ internal suspend inline fun <reified T : ContactMessage, R> T.selectMessagesImpl
 
 @Suppress("unused")
 @PublishedApi
-internal suspend inline fun <reified T : ContactMessage> T.whileSelectMessagesImpl(
+internal suspend inline fun <reified T : MessageEvent> T.whileSelectMessagesImpl(
     timeoutMillis: Long = -1,
     filterContext: Boolean = true,
     crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit

+ 9 - 9
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt

@@ -17,10 +17,10 @@ import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.ReceiveChannel
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.event.events.BotEvent
-import net.mamoe.mirai.message.ContactMessage
-import net.mamoe.mirai.message.FriendMessage
-import net.mamoe.mirai.message.GroupMessage
-import net.mamoe.mirai.message.TempMessage
+import net.mamoe.mirai.message.FriendMessageEvent
+import net.mamoe.mirai.message.GroupMessageEvent
+import net.mamoe.mirai.message.MessageEvent
+import net.mamoe.mirai.message.TempMessageEvent
 import net.mamoe.mirai.utils.SinceMirai
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
@@ -28,7 +28,7 @@ import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
-typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<ContactMessage, Listener<ContactMessage>, Unit, Unit>
+typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<MessageEvent, Listener<MessageEvent>, Unit, Unit>
 
 /**
  * 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
@@ -47,7 +47,7 @@ fun <R> CoroutineScope.subscribeMessages(
     }
 
     return MessagePacketSubscribersBuilder(Unit)
-    { filter, messageListener: MessageListener<ContactMessage, Unit> ->
+    { filter, messageListener: MessageListener<MessageEvent, Unit> ->
         // subscribeAlways 即注册一个监听器. 这个监听器收到消息后就传递给 [messageListener]
         // messageListener 即为 DSL 里 `contains(...) { }`, `startsWith(...) { }` 的代码块.
         subscribeAlways(coroutineContext, concurrencyKind) {
@@ -59,7 +59,7 @@ fun <R> CoroutineScope.subscribeMessages(
     }.run(listeners)
 }
 
-typealias GroupMessageSubscribersBuilder = MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>
+typealias GroupMessageSubscribersBuilder = MessageSubscribersBuilder<GroupMessageEvent, Listener<GroupMessageEvent>, Unit, Unit>
 
 /**
  * 订阅来自所有 [Bot] 的所有群消息事件
@@ -84,7 +84,7 @@ fun <R> CoroutineScope.subscribeGroupMessages(
     }.run(listeners)
 }
 
-typealias FriendMessageSubscribersBuilder = MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>
+typealias FriendMessageSubscribersBuilder = MessageSubscribersBuilder<FriendMessageEvent, Listener<FriendMessageEvent>, Unit, Unit>
 
 /**
  * 订阅来自所有 [Bot] 的所有好友消息事件
@@ -109,7 +109,7 @@ fun <R> CoroutineScope.subscribeFriendMessages(
     }.run(listeners)
 }
 
-typealias TempMessageSubscribersBuilder = MessageSubscribersBuilder<TempMessage, Listener<TempMessage>, Unit, Unit>
+typealias TempMessageSubscribersBuilder = MessageSubscribersBuilder<TempMessageEvent, Listener<TempMessageEvent>, Unit, Unit>
 
 /**
  * 订阅来自所有 [Bot] 的所有临时会话消息事件

+ 0 - 383
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt

@@ -1,383 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-@file:Suppress(
-    "EXPERIMENTAL_UNSIGNED_LITERALS",
-    "EXPERIMENTAL_API_USAGE",
-    "unused",
-    "INVISIBLE_REFERENCE",
-    "INVISIBLE_MEMBER"
-)
-@file:OptIn(MiraiInternalAPI::class)
-
-package net.mamoe.mirai.message
-
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.TimeoutCancellationException
-import kotlinx.coroutines.async
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.event.*
-import net.mamoe.mirai.event.events.BotEvent
-import net.mamoe.mirai.message.data.*
-import net.mamoe.mirai.qqandroid.network.Packet
-import net.mamoe.mirai.utils.*
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmSynthetic
-
-/**
- * 一个消息事件.
- *
- * 它是一个 [BotEvent], 因此可以被 [监听][Bot.subscribe]
- *
- * 支持的消息类型:
- * - [群消息事件][GroupMessage]
- * - [好友消息事件][FriendMessage]
- * - [临时会话消息事件][TempMessage]
- *
- * @see isContextIdenticalWith 判断语境是否相同
- */
-@Suppress("DEPRECATION")
-@SinceMirai("0.32.0")
-abstract class ContactMessage : MessagePacket<User, Contact>(), BotEvent
-
-/**
- * 一条从服务器接收到的消息事件.
- * 请查看各平台的 `actual` 实现的说明.
- */
-@Suppress("DEPRECATION")
-@Deprecated(
-    message = "use ContactMessage",
-    replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
-)
-expect abstract class MessagePacket<TSender : User, TSubject : Contact> constructor() :
-    MessagePacketBase<TSender, TSubject>
-
-/**
- * 仅内部使用, 请使用 [ContactMessage]
- */ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构
-@Deprecated(
-    message = "use ContactMessage",
-    replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
-)
-@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
-abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent, AbstractEvent() {
-    /**
-     * 接受到这条消息的
-     */
-    abstract override val bot: Bot
-
-    /**
-     * 消息事件主体.
-     *
-     * - 对于好友消息, 这个属性为 [Friend] 的实例, 与 [sender] 引用相同;
-     * - 对于临时会话消息, 这个属性为 [Member] 的实例, 与 [sender] 引用相同;
-     * - 对于群消息, 这个属性为 [Group] 的实例, 与 [GroupMessage.group] 引用相同
-     *
-     * 在回复消息时, 可通过 [subject] 作为回复对象
-     */
-    abstract val subject: TSubject
-
-    /**
-     * 发送人.
-     *
-     * 在好友消息时为 [Friend] 的实例, 在群消息时为 [Member] 的实例
-     */
-    abstract val sender: TSender
-
-    abstract val senderName: String
-
-    /**
-     * 消息内容
-     */
-    abstract val message: MessageChain
-
-    /**
-     * 消息发送时间 (由服务器提供)
-     */
-    @SinceMirai("0.39.0")
-    abstract val time: Int
-
-    /**
-     * 消息源
-     */
-    open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
-
-    // region 发送 Message
-
-    /**
-     * 给这个消息事件的主体发送消息
-     * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
-     * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
-     */
-    suspend inline fun reply(message: Message): MessageReceipt<TSubject> =
-        subject.sendMessage(message.asMessageChain()) as MessageReceipt<TSubject>
-
-    suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
-        subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
-    // endregion
-
-    // region 图片
-    suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
-
-    suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
-
-    suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
-    suspend inline fun Message.send(): MessageReceipt<TSubject> = this.sendTo(subject)
-    suspend inline fun String.send(): MessageReceipt<TSubject> = this.toMessage().sendTo(subject)
-    // endregion
-
-
-    // region 引用回复
-    /**
-     * 给这个消息事件的主体发送引用回复消息
-     * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
-     * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
-     */
-    suspend inline fun quoteReply(message: MessageChain): MessageReceipt<TSubject> =
-        reply(this.message.quote() + message)
-
-    suspend inline fun quoteReply(message: Message): MessageReceipt<TSubject> = reply(this.message.quote() + message)
-    suspend inline fun quoteReply(plain: String): MessageReceipt<TSubject> = reply(this.message.quote() + plain)
-
-    @JvmName("reply2")
-    suspend inline fun String.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
-
-    @JvmName("reply2")
-    suspend inline fun Message.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
-
-    @JvmName("reply2")
-    suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
-
-    // endregion
-
-    inline operator fun <M : Message> get(at: Message.Key<M>): M {
-        return this.message[at]
-    }
-
-    inline fun At.isBot(): Boolean = target == bot.id
-
-    // endregion
-
-    // region 下载图片
-
-
-    /**
-     * 获取图片下载链接
-     *
-     * @return "http://gchat.qpic.cn/gchatpic_new/..."
-     */
-    suspend inline fun Image.url(): String = bot.queryImageUrl(this@url)
-
-    // endregion
-}
-
-/**
- * 判断两个 [MessagePacket] 的 [MessagePacket.sender] 和 [MessagePacket.subject] 是否相同
- */
-@SinceMirai("0.29.0")
-fun ContactMessage.isContextIdenticalWith(another: ContactMessage): Boolean {
-    return this.sender == another.sender && this.subject == another.subject && this.bot == another.bot
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同且通过 [筛选][filter] 的 [MessagePacket]
- *
- * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
- *
- * @see syncFromEvent
- */
-@JvmSynthetic
-suspend inline fun <reified P : ContactMessage> P.nextMessage(
-    timeoutMillis: Long = -1,
-    crossinline filter: suspend P.(P) -> Boolean
-): MessageChain {
-    return syncFromEvent<P, P>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) }
-    }.message
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同且通过 [筛选][filter] 的 [MessagePacket]
- *
- * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
- * @return 消息链. 超时时返回 `null`
- *
- * @see syncFromEventOrNull
- */
-@JvmSynthetic
-suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
-    timeoutMillis: Long = -1,
-    crossinline filter: suspend P.(P) -> Boolean
-): MessageChain? {
-    return syncFromEventOrNull<P, P>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }?.takeIf { filter(it, it) }
-    }?.message
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同的 [MessagePacket]
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- *
- * @throws TimeoutCancellationException
- *
- * @see syncFromEvent
- */
-@JvmSynthetic
-suspend inline fun <reified P : ContactMessage> P.nextMessage(
-    timeoutMillis: Long = -1
-): MessageChain {
-    return syncFromEvent<P, P>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessage) }
-    }.message
-}
-
-/**
- * @see nextMessage
- * @throws TimeoutCancellationException
- */
-@JvmSynthetic
-inline fun <reified P : ContactMessage> P.nextMessageAsync(
-    timeoutMillis: Long = -1,
-    coroutineContext: CoroutineContext = EmptyCoroutineContext
-): Deferred<MessageChain> {
-    return this.bot.async(coroutineContext) {
-        syncFromEvent<P, P>(timeoutMillis) {
-            takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
-        }.message
-    }
-}
-
-/**
- * @see nextMessage
- */
-@JvmSynthetic
-inline fun <reified P : ContactMessage> P.nextMessageAsync(
-    timeoutMillis: Long = -1,
-    coroutineContext: CoroutineContext = EmptyCoroutineContext,
-    crossinline filter: suspend P.(P) -> Boolean
-): Deferred<MessageChain> {
-    return this.bot.async(coroutineContext) {
-        syncFromEvent<P, P>(timeoutMillis) {
-            takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
-                .takeIf { filter(this, this) }
-        }.message
-    }
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同的 [MessagePacket]
- *
- * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- * @return 消息链. 超时时返回 `null`
- *
- * @see syncFromEventOrNull
- */
-@JvmSynthetic
-suspend inline fun <reified P : ContactMessage> P.nextMessageOrNull(
-    timeoutMillis: Long = -1
-): MessageChain? {
-    return syncFromEventOrNull<P, P>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }
-    }?.message
-}
-
-/**
- * @see nextMessageOrNull
- */
-@JvmSynthetic
-inline fun <reified P : ContactMessage> P.nextMessageOrNullAsync(
-    timeoutMillis: Long = -1,
-    coroutineContext: CoroutineContext = EmptyCoroutineContext
-): Deferred<MessageChain?> {
-    return this.bot.async(coroutineContext) {
-        syncFromEventOrNull<P, P>(timeoutMillis) {
-            takeIf { this.isContextIdenticalWith(this@nextMessageOrNullAsync) }
-        }?.message
-    }
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同的 [MessagePacket]
- *
- * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- *
- * @see syncFromEvent
- * @see whileSelectMessages
- * @see selectMessages
- */
-@JvmSynthetic
-suspend inline fun <reified M : Message> ContactMessage.nextMessageContaining(
-    timeoutMillis: Long = -1
-): M {
-    return syncFromEvent<ContactMessage, ContactMessage>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessageContaining) }
-            .takeIf { this.message.anyIsInstance<M>() }
-    }.message.firstIsInstance()
-}
-
-@JvmSynthetic
-inline fun <reified M : Message> ContactMessage.nextMessageContainingAsync(
-    timeoutMillis: Long = -1,
-    coroutineContext: CoroutineContext = EmptyCoroutineContext
-): Deferred<M> {
-    return this.bot.async(coroutineContext) {
-        @Suppress("RemoveExplicitTypeArguments")
-        syncFromEvent<ContactMessage, ContactMessage>(timeoutMillis) {
-            takeIf { this.isContextIdenticalWith(this@nextMessageContainingAsync) }
-                .takeIf { this.message.anyIsInstance<M>() }
-        }.message.firstIsInstance<M>()
-    }
-}
-
-/**
- * 挂起当前协程, 等待下一条 [MessagePacket.sender] 和 [MessagePacket.subject] 与 [this] 相同并含有 [M] 类型的消息的 [MessagePacket]
- *
- * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
- *
- * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
- * @return 指定类型的消息. 超时时返回 `null`
- *
- * @see syncFromEventOrNull
- */
-@JvmSynthetic
-suspend inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNull(
-    timeoutMillis: Long = -1
-): M? {
-    return syncFromEventOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
-        takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNull) }
-            .takeIf { this.message.anyIsInstance<M>() }
-    }?.message?.firstIsInstance()
-}
-
-@JvmSynthetic
-inline fun <reified M : Message> ContactMessage.nextMessageContainingOrNullAsync(
-    timeoutMillis: Long = -1,
-    coroutineContext: CoroutineContext = EmptyCoroutineContext
-): Deferred<M?> {
-    return this.bot.async(coroutineContext) {
-        syncFromEventOrNull<ContactMessage, ContactMessage>(timeoutMillis) {
-            takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNullAsync) }
-                .takeIf { this.message.anyIsInstance<M>() }
-        }?.message?.firstIsInstance<M>()
-    }
-}

+ 9 - 5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt → mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessageEvent.kt

@@ -18,17 +18,21 @@ import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
 import net.mamoe.mirai.message.data.OnlineMessageSource
 import net.mamoe.mirai.message.data.source
+import net.mamoe.mirai.utils.PlannedRemoval
 
 /**
- * 好友消息事件
+ * 机器人收到的好友消息的事件
+ *
+ * @see MessageEvent
  */
-class FriendMessage constructor(
+class FriendMessageEvent constructor(
     override val sender: Friend,
     override val message: MessageChain,
     override val time: Int
-) : ContactMessage(), BroadcastControllable {
+) : @PlannedRemoval("1.2.0") FriendMessage(), BroadcastControllable {
     init {
-        val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
+        val source =
+            message.getOrNull(MessageSource) ?: throw IllegalArgumentException("Cannot find MessageSource from message")
         check(source is OnlineMessageSource.Incoming.FromFriend) { "source provided to a FriendMessage must be an instance of OnlineMessageSource.Incoming.FromFriend" }
     }
 
@@ -37,5 +41,5 @@ class FriendMessage constructor(
     override val senderName: String get() = sender.nick
     override val source: OnlineMessageSource.Incoming.FromFriend get() = message.source as OnlineMessageSource.Incoming.FromFriend
 
-    override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)"
+    override fun toString(): String = "FriendMessageEvent(sender=${sender.id}, message=$message)"
 }

+ 9 - 7
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt → mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessageEvent.kt

@@ -7,6 +7,8 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("DEPRECATION_ERROR", "unused", "NOTHING_TO_INLINE")
+
 package net.mamoe.mirai.message
 
 import net.mamoe.mirai.Bot
@@ -15,14 +17,14 @@ import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.utils.PlannedRemoval
 
 /**
- * 群消息事件
+ * 机器人收到的群消息事件
  *
- * @see ContactMessage
+ * @see MessageEvent
  */
-@Suppress("unused", "NOTHING_TO_INLINE")
-class GroupMessage(
+class GroupMessageEvent(
     override val senderName: String,
     /**
      * 发送方权限.
@@ -31,13 +33,13 @@ class GroupMessage(
     override val sender: Member,
     override val message: MessageChain,
     override val time: Int
-) : ContactMessage(), Event {
+) : @PlannedRemoval("1.2.0") GroupMessage(), Event {
     init {
         val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
         check(source is OnlineMessageSource.Incoming.FromGroup) { "source provided to a GroupMessage must be an instance of OnlineMessageSource.Incoming.FromGroup" }
     }
 
-    val group: Group get() = sender.group
+    inline val group: Group get() = sender.group
     override val bot: Bot get() = sender.bot
 
     override val subject: Group get() = group
@@ -47,5 +49,5 @@ class GroupMessage(
     inline fun At.asMember(): Member = group[this.target]
 
     override fun toString(): String =
-        "GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
+        "GroupMessageEvent(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
 }

+ 221 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageEvent.kt

@@ -0,0 +1,221 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+@file:Suppress(
+    "EXPERIMENTAL_UNSIGNED_LITERALS",
+    "EXPERIMENTAL_API_USAGE",
+    "unused",
+    "DECLARATION_CANT_BE_INLINED", "UNCHECKED_CAST", "NOTHING_TO_INLINE"
+)
+
+@file:OptIn(MiraiInternalAPI::class)
+@file:JvmMultifileClass
+@file:JvmName("MessageEventKt")
+
+package net.mamoe.mirai.message
+
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.*
+import net.mamoe.mirai.event.AbstractEvent
+import net.mamoe.mirai.event.events.BotEvent
+import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.qqandroid.network.Packet
+import net.mamoe.mirai.utils.*
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+import kotlin.jvm.JvmSynthetic
+
+/**
+ * 一个 (收到的) 消息事件.
+ *
+ * 它是一个 [BotEvent], 因此可以被 [监听][Bot.subscribe]
+ *
+ * 支持的消息类型:
+ * - [群消息事件][GroupMessageEvent]
+ * - [好友消息事件][FriendMessageEvent]
+ * - [临时会话消息事件][TempMessageEvent]
+ *
+ * @see isContextIdenticalWith 判断语境是否相同
+ */
+@Suppress("DEPRECATION_ERROR")
+@SinceMirai("0.32.0")
+abstract class MessageEvent : @PlannedRemoval("1.2.0") ContactMessage(),
+    BotEvent, MessageEventExtensions<User, Contact> {
+
+    /**
+     * 与这个消息事件相关的 [Bot]
+     */
+    abstract override val bot: Bot
+
+    /**
+     * 消息事件主体.
+     *
+     * - 对于好友消息, 这个属性为 [Friend] 的实例, 与 [sender] 引用相同;
+     * - 对于临时会话消息, 这个属性为 [Member] 的实例, 与 [sender] 引用相同;
+     * - 对于群消息, 这个属性为 [Group] 的实例, 与 [GroupMessageEvent.group] 引用相同
+     *
+     * 在回复消息时, 可通过 [subject] 作为回复对象
+     */
+    abstract override val subject: Contact
+
+    /**
+     * 发送人.
+     *
+     * 在好友消息时为 [Friend] 的实例, 在群消息时为 [Member] 的实例
+     */
+    abstract override val sender: User
+
+    abstract val senderName: String
+
+    /** 消息内容 */
+    abstract override val message: MessageChain
+
+    /** 消息发送时间 (由服务器提供) */
+    @SinceMirai("0.39.0")
+    abstract val time: Int
+
+    /** 消息源 */
+    open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
+}
+
+/** 消息事件的扩展函数 */
+@Suppress("EXPOSED_SUPER_INTERFACE") // Functions are visible
+interface MessageEventExtensions<out TSender : User, out TSubject : Contact> :
+    MessageEventPlatformExtensions<TSender, TSubject> {
+
+    // region 发送 Message
+
+    /**
+     * 给这个消息事件的主体发送消息
+     * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
+     * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
+     */
+    @JvmSynthetic
+    suspend inline fun reply(message: Message): MessageReceipt<TSubject> =
+        subject.sendMessage(message.asMessageChain()) as MessageReceipt<TSubject>
+
+    @JvmSynthetic
+    suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
+        subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
+
+    // endregion
+
+    @JvmSynthetic
+    suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
+
+    @JvmSynthetic
+    suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
+
+    @JvmSynthetic
+    suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
+
+    @JvmSynthetic
+    suspend inline fun Message.send(): MessageReceipt<TSubject> = this.sendTo(subject)
+
+    @JvmSynthetic
+    suspend inline fun String.send(): MessageReceipt<TSubject> = this.toMessage().sendTo(subject)
+
+    // region 引用回复
+    /**
+     * 给这个消息事件的主体发送引用回复消息
+     * 对于好友消息事件, 这个方法将会给好友 ([subject]) 发送消息
+     * 对于群消息事件, 这个方法将会给群 ([subject]) 发送消息
+     */
+    @JvmSynthetic
+    suspend inline fun quoteReply(message: MessageChain): MessageReceipt<TSubject> =
+        reply(this.message.quote() + message)
+
+    @JvmSynthetic
+    suspend inline fun quoteReply(message: Message): MessageReceipt<TSubject> = reply(this.message.quote() + message)
+
+    @JvmSynthetic
+    suspend inline fun quoteReply(plain: String): MessageReceipt<TSubject> = reply(this.message.quote() + plain)
+
+    @JvmSynthetic
+    inline operator fun <M : Message> get(at: Message.Key<M>): M {
+        return this.message[at]
+    }
+
+    @JvmSynthetic
+    inline fun At.isBot(): Boolean = target == bot.id
+
+
+    /**
+     * 获取图片下载链接
+     * @return "http://gchat.qpic.cn/gchatpic_new/..."
+     */
+    @JvmSynthetic
+    suspend inline fun Image.url(): String = bot.queryImageUrl(this@url)
+}
+
+/** 一个消息事件在各平台的相关扩展. 请使用 [MessageEventExtensions] */
+internal expect interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
+    val subject: TSubject
+    val sender: TSender
+    val message: MessageChain
+    val bot: Bot
+}
+
+
+/**
+ * 已废弃, 请使用 [MessageEvent]
+ */
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "use MessageEvent",
+    replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent, AbstractEvent()
+
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "Ambiguous name. Use MessageEvent instead",
+    replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DEPRECATION_ERROR")
+abstract class MessagePacket : MessagePacketBase<User, Contact>(),
+    BotEvent, MessageEventExtensions<User, Contact>
+
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "Ambiguous name. Use MessageEvent instead",
+    replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DEPRECATION_ERROR")
+abstract class ContactMessage : MessagePacket(),
+    BotEvent, MessageEventExtensions<User, Contact>
+
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "Ambiguous name. Use FriendMessageEvent instead",
+    replaceWith = ReplaceWith("FriendMessageEvent", "net.mamoe.mirai.message.FriendMessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DEPRECATION_ERROR")
+abstract class FriendMessage : MessageEvent()
+
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "Ambiguous name. Use GroupMessageEvent instead",
+    replaceWith = ReplaceWith("GroupMessageEvent", "net.mamoe.mirai.message.GroupMessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DEPRECATION_ERROR")
+abstract class GroupMessage : MessageEvent()
+
+@PlannedRemoval("1.2.0")
+@Deprecated(
+    message = "Ambiguous name. Use TempMessageEvent instead",
+    replaceWith = ReplaceWith("TempMessageEvent", "net.mamoe.mirai.message.TempMessageEvent"),
+    level = DeprecationLevel.ERROR
+)
+abstract class TempMessage : MessageEvent()

+ 8 - 4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/TempMessage.kt → mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/TempMessageEvent.kt

@@ -1,3 +1,5 @@
+@file:Suppress("DEPRECATION_ERROR", "unused", "NOTHING_TO_INLINE")
+
 package net.mamoe.mirai.message
 
 import net.mamoe.mirai.Bot
@@ -12,14 +14,16 @@ import net.mamoe.mirai.message.data.source
 import net.mamoe.mirai.utils.SinceMirai
 
 /**
- * 临时会话消息
+ * 机器人收到的群临时会话消息的事件
+ *
+ * @see MessageEvent
  */
 @SinceMirai("0.35.0")
-class TempMessage(
+class TempMessageEvent(
     override val sender: Member,
     override val message: MessageChain,
     override val time: Int
-) : ContactMessage(), BroadcastControllable {
+) : TempMessage(), BroadcastControllable {
     init {
         val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
         check(source is OnlineMessageSource.Incoming.FromTemp) { "source provided to a TempMessage must be an instance of OnlineMessageSource.Incoming.FromTemp" }
@@ -32,5 +36,5 @@ class TempMessage(
     override val source: OnlineMessageSource.Incoming.FromTemp get() = message.source as OnlineMessageSource.Incoming.FromTemp
 
     override fun toString(): String =
-        "TempMessage(sender=${sender.id} from group(${sender.group.id}), message=$message)"
+        "TempMessageEvent(sender=${sender.id} from group(${sender.group.id}), message=$message)"
 }

+ 8 - 5
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/ForwardMessage.kt

@@ -12,8 +12,11 @@
 package net.mamoe.mirai.message.data
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.contact.Group
+import net.mamoe.mirai.contact.User
+import net.mamoe.mirai.contact.nameCardOrNick
+import net.mamoe.mirai.message.MessageEvent
 import net.mamoe.mirai.message.data.ForwardMessage.DisplayStrategy
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.SinceMirai
@@ -72,7 +75,7 @@ import kotlin.jvm.JvmSynthetic
  *
  * ### 构造
  * - 使用 [DSL][buildForwardMessage]
- * - 通过 [ContactMessage] 集合转换: [toForwardMessage]
+ * - 通过 [MessageEvent] 集合转换: [toForwardMessage]
  *
  * @see buildForwardMessage
  */
@@ -186,7 +189,7 @@ class ForwardMessage @JvmOverloads constructor(
  */
 @SinceMirai("0.39.0")
 @JvmOverloads
-fun Iterable<ContactMessage>.toForwardMessage(displayStrategy: DisplayStrategy = DisplayStrategy): ForwardMessage {
+fun Iterable<MessageEvent>.toForwardMessage(displayStrategy: DisplayStrategy = DisplayStrategy): ForwardMessage {
     val iterator = this.iterator()
     if (!iterator.hasNext()) return ForwardMessage(emptyList(), displayStrategy)
     return ForwardMessage(
@@ -236,7 +239,7 @@ inline fun buildForwardMessage(
  */
 @SinceMirai("0.39.0")
 @JvmSynthetic
-inline fun ContactMessage.buildForwardMessage(
+inline fun MessageEvent.buildForwardMessage(
     context: Contact = this.subject,
     displayStrategy: DisplayStrategy = DisplayStrategy,
     block: ForwardMessageBuilder.() -> Unit

+ 3 - 3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt

@@ -16,7 +16,7 @@ package net.mamoe.mirai.message.data
 import kotlinx.coroutines.Job
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.message.MessageEvent
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.recallIn
 import net.mamoe.mirai.utils.LazyProperty
@@ -159,7 +159,7 @@ sealed class MessageSource : Message, MessageMetadata, ConstrainSingle<MessageSo
  * 此回执的 [消息源][MessageReceipt.source] 即为一个 [外向消息源][OnlineMessageSource.Outgoing], 代表着刚刚发出的那条消息的来源.
  *
  * #### 机器人接受消息
- * 当机器人接收一条消息 [ContactMessage], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
+ * 当机器人接收一条消息 [MessageEvent], 这条消息包含一个 [内向消息源][OnlineMessageSource.Incoming], 代表着接收到的这条消息的来源.
  *
  *
  * ### 实现
@@ -356,7 +356,7 @@ fun MessageSource.quote(): QuoteReply {
 }
 
 /**
- * 引用这条消息. 仅从服务器接收的消息 (即来自 [ContactMessage]) 才可以通过这个方式被引用.
+ * 引用这条消息. 仅从服务器接收的消息 (即来自 [MessageEvent]) 才可以通过这个方式被引用.
  * @see QuoteReply
  */
 fun MessageChain.quote(): QuoteReply {

+ 243 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/utils.kt

@@ -0,0 +1,243 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+@file:JvmMultifileClass
+@file:JvmName("MessageEventKt")
+@file:Suppress("unused")
+
+package net.mamoe.mirai.message
+
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.async
+import net.mamoe.mirai.event.selectMessages
+import net.mamoe.mirai.event.syncFromEvent
+import net.mamoe.mirai.event.syncFromEventOrNull
+import net.mamoe.mirai.event.whileSelectMessages
+import net.mamoe.mirai.message.data.Message
+import net.mamoe.mirai.message.data.MessageChain
+import net.mamoe.mirai.message.data.anyIsInstance
+import net.mamoe.mirai.message.data.firstIsInstance
+import net.mamoe.mirai.utils.PlannedRemoval
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+import kotlin.jvm.JvmSynthetic
+
+
+/**
+ * 判断两个 [MessageEvent] 的 [MessageEvent.sender] 和 [MessageEvent.subject] 是否相同
+ */
+fun MessageEvent.isContextIdenticalWith(another: MessageEvent): Boolean {
+    return this.sender == another.sender && this.subject == another.subject
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同且通过 [筛选][filter] 的 [MessageEvent]
+ *
+ * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
+ *
+ * @see syncFromEvent
+ */
+@JvmSynthetic
+suspend inline fun <reified P : MessageEvent> P.nextMessage(
+    timeoutMillis: Long = -1,
+    crossinline filter: suspend P.(P) -> Boolean
+): MessageChain {
+    return syncFromEvent<P, P>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessage) }?.takeIf { filter(it, it) }
+    }.message
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同且通过 [筛选][filter] 的 [MessageEvent]
+ *
+ * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ * @param filter 过滤器. 返回非 null 则代表得到了需要的值. [syncFromEvent] 会返回这个值
+ * @return 消息链. 超时时返回 `null`
+ *
+ * @see syncFromEventOrNull
+ */
+@JvmSynthetic
+suspend inline fun <reified P : MessageEvent> P.nextMessageOrNull(
+    timeoutMillis: Long = -1,
+    crossinline filter: suspend P.(P) -> Boolean
+): MessageChain? {
+    return syncFromEventOrNull<P, P>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }?.takeIf { filter(it, it) }
+    }?.message
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同的 [MessageEvent]
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ *
+ * @throws TimeoutCancellationException
+ *
+ * @see syncFromEvent
+ */
+@JvmSynthetic
+suspend inline fun <reified P : MessageEvent> P.nextMessage(
+    timeoutMillis: Long = -1
+): MessageChain {
+    return syncFromEvent<P, P>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessage) }
+    }.message
+}
+
+/**
+ * @see nextMessage
+ * @throws TimeoutCancellationException
+ */
+@JvmSynthetic
+inline fun <reified P : MessageEvent> P.nextMessageAsync(
+    timeoutMillis: Long = -1,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext
+): Deferred<MessageChain> {
+    return this.bot.async(coroutineContext) {
+        syncFromEvent<P, P>(timeoutMillis) {
+            takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
+        }.message
+    }
+}
+
+/**
+ * @see nextMessage
+ */
+@JvmSynthetic
+inline fun <reified P : MessageEvent> P.nextMessageAsync(
+    timeoutMillis: Long = -1,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext,
+    crossinline filter: suspend P.(P) -> Boolean
+): Deferred<MessageChain> {
+    return this.bot.async(coroutineContext) {
+        syncFromEvent<P, P>(timeoutMillis) {
+            takeIf { this.isContextIdenticalWith(this@nextMessageAsync) }
+                .takeIf { filter(this, this) }
+        }.message
+    }
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同的 [MessageEvent]
+ *
+ * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ * @return 消息链. 超时时返回 `null`
+ *
+ * @see syncFromEventOrNull
+ */
+@JvmSynthetic
+suspend inline fun <reified P : MessageEvent> P.nextMessageOrNull(
+    timeoutMillis: Long = -1
+): MessageChain? {
+    return syncFromEventOrNull<P, P>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessageOrNull) }
+    }?.message
+}
+
+/**
+ * @see nextMessageOrNull
+ */
+@JvmSynthetic
+inline fun <reified P : MessageEvent> P.nextMessageOrNullAsync(
+    timeoutMillis: Long = -1,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext
+): Deferred<MessageChain?> {
+    return this.bot.async(coroutineContext) {
+        syncFromEventOrNull<P, P>(timeoutMillis) {
+            takeIf { this.isContextIdenticalWith(this@nextMessageOrNullAsync) }
+        }?.message
+    }
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同的 [MessageEvent]
+ *
+ * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ *
+ * @see syncFromEvent
+ * @see whileSelectMessages
+ * @see selectMessages
+ */
+@JvmSynthetic
+suspend inline fun <reified M : Message> MessageEvent.nextMessageContaining(
+    timeoutMillis: Long = -1
+): M {
+    return syncFromEvent<MessageEvent, MessageEvent>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessageContaining) }
+            .takeIf { this.message.anyIsInstance<M>() }
+    }.message.firstIsInstance()
+}
+
+@JvmSynthetic
+inline fun <reified M : Message> MessageEvent.nextMessageContainingAsync(
+    timeoutMillis: Long = -1,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext
+): Deferred<M> {
+    return this.bot.async(coroutineContext) {
+        @Suppress("RemoveExplicitTypeArguments")
+        syncFromEvent<MessageEvent, MessageEvent>(timeoutMillis) {
+            takeIf { this.isContextIdenticalWith(this@nextMessageContainingAsync) }
+                .takeIf { this.message.anyIsInstance<M>() }
+        }.message.firstIsInstance<M>()
+    }
+}
+
+/**
+ * 挂起当前协程, 等待下一条 [MessageEvent.sender] 和 [MessageEvent.subject] 与 [this] 相同并含有 [M] 类型的消息的 [MessageEvent]
+ *
+ * 若 [filter] 抛出了一个异常, 本函数会立即抛出这个异常.
+ *
+ * @param timeoutMillis 超时. 单位为毫秒. `-1` 为不限制
+ * @return 指定类型的消息. 超时时返回 `null`
+ *
+ * @see syncFromEventOrNull
+ */
+@JvmSynthetic
+suspend inline fun <reified M : Message> MessageEvent.nextMessageContainingOrNull(
+    timeoutMillis: Long = -1
+): M? {
+    return syncFromEventOrNull<MessageEvent, MessageEvent>(timeoutMillis) {
+        takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNull) }
+            .takeIf { this.message.anyIsInstance<M>() }
+    }?.message?.firstIsInstance()
+}
+
+@JvmSynthetic
+inline fun <reified M : Message> MessageEvent.nextMessageContainingOrNullAsync(
+    timeoutMillis: Long = -1,
+    coroutineContext: CoroutineContext = EmptyCoroutineContext
+): Deferred<M?> {
+    return this.bot.async(coroutineContext) {
+        syncFromEventOrNull<MessageEvent, MessageEvent>(timeoutMillis) {
+            takeIf { this.isContextIdenticalWith(this@nextMessageContainingOrNullAsync) }
+                .takeIf { this.message.anyIsInstance<M>() }
+        }?.message?.firstIsInstance<M>()
+    }
+}
+
+
+@PlannedRemoval("1.2.0")
+@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+@Suppress("DEPRECATION_ERROR")
+@JvmSynthetic
+fun ContactMessage.isContextIdenticalWith(another: ContactMessage): Boolean {
+    return this.sender == another.sender && this.subject == another.subject && this.bot == another.bot
+}

+ 27 - 20
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt → mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageEventPlatform.kt

@@ -7,50 +7,45 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-@file:Suppress("unused")
+@file:Suppress("unused", "DECLARATION_CANT_BE_INLINED")
 
 package net.mamoe.mirai.message
 
-import kotlinx.coroutines.io.ByteWriteChannel
 import kotlinx.io.core.Input
-import kotlinx.io.core.Output
-import kotlinx.io.core.use
+import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.message.data.Image
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
-import net.mamoe.mirai.utils.MiraiInternalAPI
-import net.mamoe.mirai.utils.copyAndClose
-import net.mamoe.mirai.utils.copyTo
+import net.mamoe.mirai.message.data.MessageChain
 import java.awt.image.BufferedImage
 import java.io.File
 import java.io.InputStream
-import java.io.OutputStream
 import java.net.URL
 
 /**
- * 一条从服务器接收到的消息事件.
- * JVM 平台相关扩展
+ * 消息事件在 JVM 平台的扩展
+ * @see MessageEventExtensions
  */
-@Suppress("DEPRECATION")
-@Deprecated(
-    message = "use ContactMessage",
-    replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
-)
-@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
-actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual constructor() :
-    MessagePacketBase<TSender, TSubject>() {
+internal actual interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
+    actual val subject: TSubject
+    actual val sender: TSender
+    actual val message: MessageChain
+    actual val bot: Bot
+
     // region 上传图片
+
     @JvmSynthetic
     suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
 
     @JvmSynthetic
     suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
+
     @JvmSynthetic
     suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
+
     @JvmSynthetic
     suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
+
     @JvmSynthetic
     suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
     // endregion
@@ -58,12 +53,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
     // region 发送图片
     @JvmSynthetic
     suspend inline fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
+
     @JvmSynthetic
     suspend inline fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
+
     @JvmSynthetic
     suspend inline fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
+
     @JvmSynthetic
     suspend inline fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
+
     @JvmSynthetic
     suspend inline fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
     // endregion
@@ -71,12 +70,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
     // region 上传图片 (扩展)
     @JvmSynthetic
     suspend inline fun BufferedImage.upload(): Image = upload(subject)
+
     @JvmSynthetic
     suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
+
     @JvmSynthetic
     suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
+
     @JvmSynthetic
     suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
+
     @JvmSynthetic
     suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
     // endregion 上传图片 (扩展)
@@ -84,12 +87,16 @@ actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual c
     // region 发送图片 (扩展)
     @JvmSynthetic
     suspend inline fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
+
     @JvmSynthetic
     suspend inline fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+
     @JvmSynthetic
     suspend inline fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+
     @JvmSynthetic
     suspend inline fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
+
     @JvmSynthetic
     suspend inline fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
     // endregion 发送图片 (扩展)