소스 검색

Plan for redesigning of MessageSource

Him188 6 년 전
부모
커밋
b6c24e008d
19개의 변경된 파일90개의 추가작업 그리고 15개의 파일을 삭제
  1. 4 2
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
  2. 1 1
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
  3. 2 0
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
  4. 1 0
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
  5. 3 1
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt
  6. 1 0
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt
  7. 1 1
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/MultiMsg.kt
  8. 2 2
      mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt
  9. 2 0
      mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt
  10. 4 0
      mirai-core/src/androidMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt
  11. 7 2
      mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt
  12. 11 0
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
  13. 15 1
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt
  14. 1 0
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
  15. 20 2
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
  16. 4 2
      mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt
  17. 2 0
      mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt
  18. 4 0
      mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt
  19. 5 1
      mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt

+ 4 - 2
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt

@@ -163,6 +163,7 @@ internal abstract class QQAndroidBotBase constructor(
         TODO("not implemented")
     }
 
+    @ExperimentalMessageSource
     override suspend fun recall(source: MessageSource) {
         if (source.senderId != uin && source.groupId != 0L) {
             getGroup(source.groupId).checkBotPermissionOperator()
@@ -382,8 +383,9 @@ internal abstract class QQAndroidBotBase constructor(
             val data = chain.calculateValidationDataForGroup(
                 sequenceId = client.atomicNextMessageSequenceId(),
                 time = time.toInt(),
-                random = Random.nextInt().absoluteValue.toULong().toLong(),
-                group
+                random = Random.nextInt().absoluteValue.toUInt(),
+                groupCode,
+                group.botAsMember.nameCardOrNick
             )
 
             val response =

+ 1 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt

@@ -278,7 +278,7 @@ internal class GroupImpl(
         check(!isBotMuted) { "bot is muted. Remaining seconds=$botMuteRemaining" }
         val event = GroupMessageSendEvent(this, message.asMessageChain()).broadcast()
         if (event.isCancelled) {
-            throw EventCancelledException("cancelled by FriendMessageSendEvent")
+            throw EventCancelledException("cancelled by GroupMessageSendEvent")
         }
         lateinit var source: MessageSourceFromSendGroup
         bot.network.run {

+ 2 - 0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt

@@ -7,6 +7,8 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
 package net.mamoe.mirai.qqandroid.contact
 
 import kotlinx.coroutines.launch

+ 1 - 0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt

@@ -8,6 +8,7 @@
  */
 
 @file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
+@file:Suppress("EXPERIMENTAL_API_USAGE")
 
 package net.mamoe.mirai.qqandroid.contact
 

+ 3 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt

@@ -7,6 +7,8 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_OVERRIDE")
+
 package net.mamoe.mirai.qqandroid.message
 
 import kotlinx.coroutines.CoroutineScope
@@ -146,7 +148,7 @@ internal abstract class MessageSourceFromSend : MessageSource {
     }
 
     private val elems by lazy {
-        originalMessage.toRichTextElems(groupId != 0L)
+        originalMessage.toRichTextElems(groupId != 0L, true)
     }
 
     private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {

+ 1 - 0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt

@@ -7,6 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 @file: OptIn(MiraiExperimentalAPI::class, MiraiInternalAPI::class, LowLevelAPI::class, ExperimentalUnsignedTypes::class)
+@file:Suppress("EXPERIMENTAL_API_USAGE")
 
 package net.mamoe.mirai.qqandroid.message
 

+ 1 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/MultiMsg.kt

@@ -52,7 +52,7 @@ internal fun MessageChain.calculateValidationDataForGroup(
     groupCode: Long,
     botMemberNameCard: String
 ): MessageValidationData {
-    val richTextElems = this.toRichTextElems(true)
+    val richTextElems = this.toRichTextElems(true, false)
 
     val msgTransmit = MsgTransmit.PbMultiMsgTransmit(
         msg = listOf(

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

@@ -319,7 +319,7 @@ internal class MessageSvc {
                     contentHead = MsgComm.ContentHead(pkgNum = 1),
                     msgBody = ImMsgBody.MsgBody(
                         richText = ImMsgBody.RichText(
-                            elems = message.toRichTextElems(false)
+                            elems = message.toRichTextElems(false, true)
                         )
                     ),
                     msgSeq = source.sequenceId,
@@ -372,7 +372,7 @@ internal class MessageSvc {
                     contentHead = MsgComm.ContentHead(pkgNum = 1),
                     msgBody = ImMsgBody.MsgBody(
                         richText = ImMsgBody.RichText(
-                            elems = message.toRichTextElems(true)
+                            elems = message.toRichTextElems(true, true)
                         )
                     ),
                     msgSeq = client.atomicNextMessageSequenceId(),

+ 2 - 0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/Bot.kt

@@ -7,6 +7,7 @@ import kotlinx.coroutines.io.ByteReadChannel
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.ExperimentalMessageSource
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -152,6 +153,7 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
      * @see _lowLevelRecallFriendMessage 低级 API
      * @see _lowLevelRecallGroupMessage 低级 API
      */
+    @ExperimentalMessageSource
     @JvmSynthetic
     actual abstract suspend fun recall(source: MessageSource)
 

+ 4 - 0
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt

@@ -5,6 +5,7 @@ import net.mamoe.mirai.contact.PermissionDeniedException
 import net.mamoe.mirai.contact.recall
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.ExperimentalMessageSource
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -61,6 +62,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
      *
      * @see Bot.recall (扩展函数) 接受参数 [MessageChain]
      */
+    @ExperimentalMessageSource
     @JvmName("recall")
     fun __recallBlockingForJava__(source: MessageSource) {
         runBlocking { recall(source) }
@@ -88,6 +90,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
      * @param millis 延迟的时间, 单位为毫秒
      * @see recall
      */
+    @ExperimentalMessageSource
     @JvmName("recallIn")
     fun __recallIn_MemberForJava__(source: MessageSource, millis: Long) {
         runBlocking { recallIn(source, millis) }
@@ -148,6 +151,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
     /**
      * 异步调用 [__recallBlockingForJava__]
      */
+    @ExperimentalMessageSource
     @JvmName("recallAsync")
     fun __recallAsyncForJava__(source: MessageSource): Future<Unit> {
         return future { recall(source) }

+ 7 - 2
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt

@@ -29,7 +29,8 @@ import net.mamoe.mirai.utils.unsafeWeakRef
  */
 @Suppress("FunctionName")
 @OptIn(MiraiInternalAPI::class)
-actual open class MessageReceipt<C : Contact> actual constructor(
+actual open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class)
+actual constructor(
     actual val source: MessageSource,
     target: C,
     private val botAsMember: Member?
@@ -56,6 +57,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      * @see Bot.recall
      * @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
      */
+    @OptIn(ExperimentalMessageSource::class)
     actual suspend fun recall() {
         @Suppress("BooleanLiteralArgument")
         if (_isRecalled.compareAndSet(false, true)) {
@@ -82,7 +84,8 @@ actual open class MessageReceipt<C : Contact> actual constructor(
         if (_isRecalled.compareAndSet(false, true)) {
             return when (val contact = target) {
                 is QQ,
-                is Group -> contact.bot.recallIn(source, millis)
+                is Group,
+                -> contact.bot.recallIn(source, millis)
                 else -> error("Unknown contact type")
             }
         } else error("message is already or planned to be recalled")
@@ -92,6 +95,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      * [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
      * @see MessageChain.quote 引用一条消息
      */
+    @OptIn(ExperimentalMessageSource::class)
     actual open suspend fun quote(): QuoteReplyToSend {
         this.source.ensureSequenceIdAvailable()
         @OptIn(LowLevelAPI::class)
@@ -105,6 +109,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      *
      * @see MessageChain.quote 引用一条消息
      */
+    @OptIn(ExperimentalMessageSource::class)
     @LowLevelAPI
     @Suppress("FunctionName")
     actual fun _unsafeQuote(): QuoteReplyToSend {

+ 11 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt

@@ -19,6 +19,7 @@ import kotlinx.coroutines.launch
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.ExperimentalMessageSource
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -33,6 +34,7 @@ import kotlin.jvm.JvmSynthetic
 /**
  * 登录, 返回 [this]
  */
+@JvmSynthetic
 suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() }
 // 任何人都能看到这个方法
 
@@ -167,6 +169,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
      * @see _lowLevelRecallFriendMessage 低级 API
      * @see _lowLevelRecallGroupMessage 低级 API
      */
+    @ExperimentalMessageSource
     @JvmSynthetic
     abstract suspend fun recall(source: MessageSource)
 
@@ -223,6 +226,7 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
  * @throws PermissionDeniedException 当 [Bot] 无权限操作时
  * @see Bot.recall
  */
+@JvmSynthetic
 suspend inline fun Bot.recall(message: MessageChain) = this.recall(message[MessageSource])
 
 /**
@@ -233,6 +237,7 @@ suspend inline fun Bot.recall(message: MessageChain) = this.recall(message[Messa
  * @param coroutineContext 额外的 [CoroutineContext]
  * @see recall
  */
+@JvmSynthetic
 inline fun Bot.recallIn(
     source: MessageSource,
     millis: Long,
@@ -249,6 +254,7 @@ inline fun Bot.recallIn(
  * @param coroutineContext 额外的 [CoroutineContext]
  * @see recall
  */
+@JvmSynthetic
 inline fun Bot.recallIn(
     message: MessageChain,
     millis: Long,
@@ -265,15 +271,20 @@ inline fun Bot.recallIn(
  *
  * @param cause 原因. 为 null 时视为正常关闭, 非 null 时视为异常关闭
  */
+@JvmSynthetic
 suspend inline fun Bot.closeAndJoin(cause: Throwable? = null) {
     close(cause)
     coroutineContext[Job]?.join()
 }
 
+@JvmSynthetic
 inline fun Bot.containsFriend(id: Long): Boolean = this.friends.contains(id)
 
+@JvmSynthetic
 inline fun Bot.containsGroup(id: Long): Boolean = this.groups.contains(id)
 
+@JvmSynthetic
 inline fun Bot.getFriendOrNull(id: Long): QQ? = this.friends.getOrNull(id)
 
+@JvmSynthetic
 inline fun Bot.getGroupOrNull(id: Long): Group? = this.groups.getOrNull(id)

+ 15 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt

@@ -15,12 +15,16 @@ import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.recallIn
+import kotlin.jvm.JvmSynthetic
 
 /**
  * 发送消息后得到的回执. 可用于撤回.
  *
  * 此对象持有 [Contact] 的弱引用, [Bot] 离线后将会释放引用, 届时 [target] 将无法访问.
  *
+ * @param source 指代发送出去的消息
+ * @param target 消息发送对象
+ *
  * @see Group.sendMessage 发送群消息, 返回回执(此对象)
  * @see QQ.sendMessage 发送群消息, 返回回执(此对象)
  *
@@ -28,11 +32,15 @@ import net.mamoe.mirai.recallIn
  * @see MessageReceipt.sourceSequenceId 源序列号
  * @see MessageReceipt.sourceTime 源时间
  */
-expect open class MessageReceipt<C : Contact>(
+expect open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class) constructor(
     source: MessageSource,
     target: C,
     botAsMember: Member?
 ) {
+    /**
+     * 指代发送出去的消息
+     */
+    @ExperimentalMessageSource
     val source: MessageSource
 
     /**
@@ -90,6 +98,8 @@ expect open class MessageReceipt<C : Contact>(
  *
  * @see MessageSource.id
  */
+@get:JvmSynthetic
+@ExperimentalMessageSource
 inline val MessageReceipt<*>.sourceId: Long get() = this.source.id
 
 /**
@@ -97,6 +107,8 @@ inline val MessageReceipt<*>.sourceId: Long get() = this.source.id
  *
  * @see MessageSource.sequenceId
  */
+@get:JvmSynthetic
+@ExperimentalMessageSource
 inline val MessageReceipt<*>.sourceSequenceId: Int get() = this.source.sequenceId
 
 /**
@@ -104,6 +116,8 @@ inline val MessageReceipt<*>.sourceSequenceId: Int get() = this.source.sequenceI
  *
  * @see MessageSource.time
  */
+@get:JvmSynthetic
+@ExperimentalMessageSource
 inline val MessageReceipt<*>.sourceTime: Long get() = this.source.time
 
 suspend inline fun MessageReceipt<out Contact>.quoteReply(message: Message) {

+ 1 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt

@@ -127,6 +127,7 @@ inline fun <reified M : Message> MessageChain.any(): Boolean = this.any { it is
 /**
  * 获取第一个 [M] 类型的 [Message] 实例
  */
+@OptIn(ExperimentalMessageSource::class)
 @JvmSynthetic
 @Suppress("UNCHECKED_CAST")
 fun <M : Message> MessageChain.firstOrNull(key: Message.Key<M>): M? = when (key) {

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

@@ -19,6 +19,12 @@ import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmSynthetic
 
+/**
+ * MessageSource 正计划于 0.32 或 0.33 或之后进行 API 不兼容的重写.
+ */
+@RequiresOptIn(message = "MessageSource 正计划于 0.32 或 0.33 或之后进行 API 不兼容的重写", level = RequiresOptIn.Level.WARNING)
+annotation class ExperimentalMessageSource
+
 /**
  * 消息源, 它存在于 [MessageChain] 中, 用于表示这个消息的来源.
  *
@@ -29,6 +35,7 @@ import kotlin.jvm.JvmSynthetic
  * @see Bot.recall 撤回一条消息
  * @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
  */
+@ExperimentalMessageSource
 interface MessageSource : Message, MessageMetadata {
     companion object Key : Message.Key<MessageSource>
 
@@ -82,6 +89,7 @@ interface MessageSource : Message, MessageMetadata {
  * 序列号. 若是机器人发出去的消息, 请先 [确保 sequenceId 可用][MessageSource.ensureSequenceIdAvailable]
  * @see MessageSource.id
  */
+@ExperimentalMessageSource
 @get:JvmSynthetic
 inline val MessageSource.sequenceId: Int
     get() = (this.id shr 32).toInt()
@@ -90,6 +98,7 @@ inline val MessageSource.sequenceId: Int
  * 消息随机数. 由服务器或客户端指定后不能更改. 它是消息 id 的一部分.
  * @see MessageSource.id
  */
+@ExperimentalMessageSource
 @get:JvmSynthetic
 inline val MessageSource.messageRandom: Int
     get() = this.id.toInt()
@@ -98,24 +107,33 @@ inline val MessageSource.messageRandom: Int
 
 /**
  * 消息 id.
+ *
+ * 仅接收到的消息才可以获取这个 id.
+ *
  * @see MessageSource.id
  */
+@ExperimentalMessageSource
 @get:JvmSynthetic
 inline val MessageChain.id: Long
     get() = this[MessageSource].id
 
 /**
  * 消息序列号, 可能来自服务器也可以发送时赋值, 不唯一.
+ *
+ * 仅接收到的消息才可以获取这个序列号.
+ *
  * @see MessageSource.id
  */
+@ExperimentalMessageSource
 @get:JvmSynthetic
 inline val MessageChain.sequenceId: Int
-    get() = this[MessageSource].sequenceId
+    get() = this.getOrNull(MessageSource)?.sequenceId ?: error("Only MessageChain from server has sequenceId")
 
 /**
  * 消息随机数. 由服务器或客户端指定后不能更改. 它是消息 id 的一部分.
  * @see MessageSource.id
  */
+@ExperimentalMessageSource
 @get:JvmSynthetic
 inline val MessageChain.messageRandom: Int
-    get() = this[MessageSource].messageRandom
+    get() = this.getOrNull(MessageSource)?.messageRandom ?: error("Only MessageChain from server has sequenceId")

+ 4 - 2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt

@@ -29,6 +29,7 @@ import kotlin.jvm.JvmName
  * 总是使用 [quote] 来构造这个实例.
  */
 open class QuoteReply
+@OptIn(ExperimentalMessageSource::class)
 @MiraiInternalAPI constructor(val source: MessageSource) : Message, MessageMetadata {
     companion object Key : Message.Key<QuoteReply>
 
@@ -39,7 +40,7 @@ open class QuoteReply
  * 用于发送的引用回复.
  * 总是使用 [quote] 来构造实例.
  */
-@OptIn(MiraiInternalAPI::class)
+@OptIn(MiraiInternalAPI::class, ExperimentalMessageSource::class)
 sealed class QuoteReplyToSend
 @MiraiInternalAPI constructor(source: MessageSource) : QuoteReply(source) {
     class ToGroup(source: MessageSource, val sender: QQ) : QuoteReplyToSend(source) {
@@ -53,7 +54,7 @@ sealed class QuoteReplyToSend
  * 引用这条消息.
  * @see sender 消息发送人.
  */
-@OptIn(MiraiInternalAPI::class)
+@OptIn(MiraiInternalAPI::class, ExperimentalMessageSource::class)
 fun MessageChain.quote(sender: QQ?): QuoteReplyToSend {
     this.firstOrNull<MessageSource>()?.let {
         return it.quote(sender)
@@ -65,6 +66,7 @@ fun MessageChain.quote(sender: QQ?): QuoteReplyToSend {
  * 引用这条消息.
  * @see from 消息来源. 若是好友发送
  */
+@ExperimentalMessageSource
 @OptIn(MiraiInternalAPI::class)
 fun MessageSource.quote(from: QQ?): QuoteReplyToSend {
     return if (this.groupId != 0L) {

+ 2 - 0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/Bot.kt

@@ -7,6 +7,7 @@ import kotlinx.coroutines.io.ByteReadChannel
 import net.mamoe.mirai.contact.*
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.ExperimentalMessageSource
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -162,6 +163,7 @@ actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIA
      * @see _lowLevelRecallFriendMessage 低级 API
      * @see _lowLevelRecallGroupMessage 低级 API
      */
+    @ExperimentalMessageSource
     @JvmSynthetic
     actual abstract suspend fun recall(source: MessageSource)
 

+ 4 - 0
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt

@@ -5,6 +5,7 @@ import net.mamoe.mirai.contact.PermissionDeniedException
 import net.mamoe.mirai.contact.recall
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.ExperimentalMessageSource
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -61,6 +62,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
      *
      * @see Bot.recall (扩展函数) 接受参数 [MessageChain]
      */
+    @ExperimentalMessageSource
     @JvmName("recall")
     fun __recallBlockingForJava__(source: MessageSource) {
         runBlocking { recall(source) }
@@ -88,6 +90,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
      * @param millis 延迟的时间, 单位为毫秒
      * @see recall
      */
+    @ExperimentalMessageSource
     @JvmName("recallIn")
     fun __recallIn_MemberForJava__(source: MessageSource, millis: Long) {
         runBlocking { recallIn(source, millis) }
@@ -148,6 +151,7 @@ actual abstract class BotJavaFriendlyAPI actual constructor() {
     /**
      * 异步调用 [__recallBlockingForJava__]
      */
+    @ExperimentalMessageSource
     @JvmName("recallAsync")
     fun __recallAsyncForJava__(source: MessageSource): Future<Unit> {
         return future { recall(source) }

+ 5 - 1
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt

@@ -29,7 +29,8 @@ import net.mamoe.mirai.utils.unsafeWeakRef
  */
 @Suppress("FunctionName")
 @OptIn(MiraiInternalAPI::class)
-actual open class MessageReceipt<C : Contact> actual constructor(
+actual open class MessageReceipt<C : Contact> @OptIn(ExperimentalMessageSource::class)
+actual constructor(
     actual val source: MessageSource,
     target: C,
     private val botAsMember: Member?
@@ -56,6 +57,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      * @see Bot.recall
      * @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
      */
+    @OptIn(ExperimentalMessageSource::class)
     actual suspend fun recall() {
         @Suppress("BooleanLiteralArgument")
         if (_isRecalled.compareAndSet(false, true)) {
@@ -84,6 +86,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      * [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
      * @see MessageChain.quote 引用一条消息
      */
+    @OptIn(ExperimentalMessageSource::class)
     actual open suspend fun quote(): QuoteReplyToSend {
         this.source.ensureSequenceIdAvailable()
         @OptIn(LowLevelAPI::class)
@@ -97,6 +100,7 @@ actual open class MessageReceipt<C : Contact> actual constructor(
      *
      * @see MessageChain.quote 引用一条消息
      */
+    @OptIn(ExperimentalMessageSource::class)
     @LowLevelAPI
     @Suppress("FunctionName")
     actual fun _unsafeQuote(): QuoteReplyToSend {