Him188 6 лет назад
Родитель
Сommit
864cde060e

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

@@ -216,23 +216,51 @@ internal abstract class QQAndroidBotBase constructor(
                     ).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
                 }
             }
-            is OfflineMessageSource,
             is MessageSourceFromFriendImpl,
             is MessageSourceToFriendImpl
             -> network.run {
+                check(source.fromId == [email protected]) {
+                    "can only recall a message sent by bot"
+                }
                 PbMessageSvc.PbMsgWithDraw.createForFriendMessage(
                     bot.client,
-                    source.fromId,
+                    source.targetId,
                     source.sequenceId,
                     source.id,
                     source.time
                 ).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
             }
+            is OfflineMessageSource -> network.run {
+                when (source.kind) {
+                    OfflineMessageSource.Kind.FRIEND -> {
+                        check(source.fromId == [email protected]) {
+                            "can only recall a message sent by bot"
+                        }
+                        PbMessageSvc.PbMsgWithDraw.createForFriendMessage(
+                            bot.client,
+                            source.targetId,
+                            source.sequenceId,
+                            source.id,
+                            source.time
+                        ).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
+                    }
+                    OfflineMessageSource.Kind.GROUP -> {
+                        PbMessageSvc.PbMsgWithDraw.createForGroupMessage(
+                            bot.client,
+                            source.targetId,
+                            source.sequenceId,
+                            source.id
+                        ).sendAndExpect<PbMessageSvc.PbMsgWithDraw.Response>()
+                    }
+                }
+            }
             else -> error("stub!")
         }
 
 
         // 1001: No message meets the requirements (实际上是没权限, 管理员在尝试撤回群主的消息)
+        // 154: timeout
+        // 3: <no message>
         check(response is PbMessageSvc.PbMsgWithDraw.Response.Success) { "Failed to recall message #${source.id}: $response" }
     }
 

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

@@ -70,6 +70,7 @@ internal class QQImpl(
     override val id: Long,
     private val friendInfo: FriendInfo
 ) : QQ() {
+    @Suppress("unused") // bug
     val lastMessageSequence: AtomicInt = atomic(-1)
 
     override val bot: QQAndroidBot by bot.unsafeWeakRef()

+ 28 - 9
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/MessageSourceImpl.kt

@@ -60,7 +60,7 @@ internal class MessageSourceFromFriendImpl(
         }
     override val id: Int get() = msg.msgBody.richText.attr!!.random
     override val time: Int get() = msg.msgHead.msgTime
-    override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, isGroup = false, addSource = false) }
+    override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, groupIdOrZero = 0, addSource = false) }
     override val target: Bot get() = bot
     override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin)
 
@@ -117,7 +117,13 @@ internal class MessageSourceFromGroupImpl(
     override val sequenceId: Int get() = msg.msgHead.msgSeq
     override val id: Int get() = msg.msgBody.richText.attr!!.random
     override val time: Int get() = msg.msgHead.msgTime
-    override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, isGroup = true, addSource = false) }
+    override val originalMessage: MessageChain by lazy {
+        msg.toMessageChain(
+            bot,
+            groupIdOrZero = group.id,
+            addSource = false
+        )
+    }
     override val target: Bot get() = bot
     override val sender: Member
         get() = bot.getGroup(
@@ -143,12 +149,11 @@ internal class MessageSourceFromGroupImpl(
 }
 
 internal class OfflineMessageSourceImpl( // from others' quotation
-    val delegate: ImMsgBody.SourceMsg, override val bot: Bot
+    val delegate: ImMsgBody.SourceMsg,
+    override val bot: Bot,
+    groupIdOrZero: Long
 ) : OfflineMessageSource(), MessageSourceImpl {
-
-    init {
-        println(delegate._miraiContentToString())
-    }
+    override val kind: Kind get() = if (delegate.srcMsg == null) Kind.GROUP else Kind.FRIEND
 
     private val isRecalled: AtomicBoolean = atomic(false)
     override var isRecalledOrPlanned: Boolean
@@ -159,7 +164,7 @@ internal class OfflineMessageSourceImpl( // from others' quotation
     override val sequenceId: Int
         get() = delegate.origSeqs?.first() ?: error("cannot find sequenceId")
     override val time: Int get() = delegate.time
-    override val originalMessage: MessageChain by lazy { delegate.toMessageChain(bot) }
+    override val originalMessage: MessageChain by lazy { delegate.toMessageChain(bot, groupIdOrZero, false) }
     /*
     override val id: Long
         get() = (delegate.origSeqs?.firstOrNull()
@@ -172,7 +177,21 @@ internal class OfflineMessageSourceImpl( // from others' quotation
 
     // override val sourceMessage: MessageChain get() = delegate.toMessageChain()
     override val fromId: Long get() = delegate.senderUin
-    override val targetId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin)
+    override val targetId: Long by lazy {
+        when {
+            groupIdOrZero != 0L -> groupIdOrZero
+            delegate.toUin != 0L -> delegate.toUin
+            delegate.srcMsg != null -> delegate.srcMsg.loadAs(MsgComm.Msg.serializer()).msgHead.toUin
+            else -> 0/*error("cannot find targetId. delegate=${delegate._miraiContentToString()}, delegate.srcMsg=${
+            kotlin.runCatching { delegate.srcMsg?.loadAs(MsgComm.Msg.serializer())?._miraiContentToString() }
+                .fold(
+                    onFailure = { "<error: ${it.message}>" },
+                    onSuccess = { it }
+                )
+            }"
+            )*/
+        }
+    }
 }
 
 internal class MessageSourceToFriendImpl(

+ 10 - 8
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/convension.kt

@@ -179,30 +179,32 @@ private val PB_RESERVE_FOR_DOUTU = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".h
 private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
 
 @OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
-internal fun MsgComm.Msg.toMessageChain(bot: Bot, isGroup: Boolean, addSource: Boolean): MessageChain {
+internal fun MsgComm.Msg.toMessageChain(bot: Bot, groupIdOrZero: Long, addSource: Boolean): MessageChain {
     val elements = this.msgBody.richText.elems
 
     return buildMessageChain(elements.size + 1) {
         if (addSource) {
-            if (isGroup) {
+            if (groupIdOrZero != 0L) {
                 +MessageSourceFromGroupImpl(bot, this@toMessageChain)
             } else {
                 +MessageSourceFromFriendImpl(bot, this@toMessageChain)
             }
         }
-        elements.joinToMessageChain(bot, this)
+        elements.joinToMessageChain(groupIdOrZero, bot, this)
     }.cleanupRubbishMessageElements()
 }
 
 // These two functions have difference method signature, don't combine.
 
 @OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
-internal fun ImMsgBody.SourceMsg.toMessageChain(bot: Bot): MessageChain {
+internal fun ImMsgBody.SourceMsg.toMessageChain(bot: Bot, groupIdOrZero: Long, withSource: Boolean): MessageChain {
     val elements = this.elems!!
 
     return buildMessageChain(elements.size + 1) {
-        +OfflineMessageSourceImpl(delegate = this@toMessageChain, bot = bot)
-        elements.joinToMessageChain(bot, this)
+        if (withSource) {
+            +OfflineMessageSourceImpl(delegate = this@toMessageChain, bot = bot, groupIdOrZero = groupIdOrZero)
+        }
+        elements.joinToMessageChain(groupIdOrZero, bot, this)
     }.cleanupRubbishMessageElements()
 }
 
@@ -254,11 +256,11 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
 }
 
 @OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
-internal fun List<ImMsgBody.Elem>.joinToMessageChain(bot: Bot, message: MessageChainBuilder) {
+internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: Bot, message: MessageChainBuilder) {
     // (this._miraiContentToString())
     this.forEach {
         when {
-            it.srcMsg != null -> message.add(QuoteReply(OfflineMessageSourceImpl(it.srcMsg, bot)))
+            it.srcMsg != null -> message.add(QuoteReply(OfflineMessageSourceImpl(it.srcMsg, bot, groupIdOrZero)))
             it.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
             it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
             it.face != null -> message.add(Face(it.face.index))

+ 1 - 1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Msg.kt

@@ -885,7 +885,7 @@ internal class ImMsgBody : ProtoBuf {
         @ProtoId(6) val type: Int = 0,
         @ProtoId(7) val richMsg: ByteArray = EMPTY_BYTE_ARRAY,
         @ProtoId(8) val pbReserve: ByteArray = EMPTY_BYTE_ARRAY,
-        @ProtoId(9) val srcMsg: ByteArray = EMPTY_BYTE_ARRAY,
+        @ProtoId(9) val srcMsg: ByteArray? = null,
         @ProtoId(10) val toUin: Long = 0L,
         @ProtoId(11) val troopName: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf

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

@@ -80,7 +80,6 @@ internal class PbMessageSvc {
             messageRandom: Int, // 921878719
             time: Int
         ): OutgoingPacket = buildOutgoingUniPacket(client) {
-            val messageUid: Long = 262144L.shl(32) or messageRandom.toLong().and(0xffFFffFF)
             writeProtoBuf(
                 MsgSvc.PbMsgWithDrawReq.serializer(),
                 MsgSvc.PbMsgWithDrawReq(
@@ -92,8 +91,8 @@ internal class PbMessageSvc {
                                     fromUin = client.bot.id,
                                     toUin = toUin,
                                     msgSeq = messageSequenceId,
-                                    msgUid = messageUid,
-                                    msgTime = time.toULong().toLong(),
+                                    msgUid = 1000000000000000000L or messageRandom.toULong().toLong(),
+                                    msgTime = time.toLong(),
                                     routingHead = MsgSvc.RoutingHead(
                                         c2c = MsgSvc.C2C(
                                             toUin = toUin

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

@@ -229,7 +229,7 @@ internal class MessageSvc {
                                     if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
                                         return@mapNotNull FriendMessage(
                                             friend,
-                                            msg.toMessageChain(bot, isGroup = false, addSource = true)
+                                            msg.toMessageChain(bot, groupIdOrZero = 0, addSource = true)
                                         )
                                     }
                                 } else return@mapNotNull null

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

@@ -84,7 +84,7 @@ internal class OnlinePush {
             return GroupMessage(
                 senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
                 sender = group[pbPushMsg.msg.msgHead.fromUin],
-                message = pbPushMsg.msg.toMessageChain(bot, isGroup = true, addSource = true),
+                message = pbPushMsg.msg.toMessageChain(bot, groupIdOrZero = group.id, addSource = true),
                 permission = when {
                     flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
                     flags and 8 != 0 -> MemberPermission.OWNER

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

@@ -180,7 +180,7 @@ sealed class OnlineMessageSource : MessageSource() {
 /**
  * 引用这条消息
  */
-fun OnlineMessageSource.quote(): QuoteReply {
+fun MessageSource.quote(): QuoteReply {
     @OptIn(MiraiInternalAPI::class)
     return QuoteReply(this)
 }
@@ -213,8 +213,19 @@ inline fun MessageSource.recallIn(
 @SinceMirai("0.33.0")
 abstract class OfflineMessageSource : MessageSource() {
     companion object Key : Message.Key<OfflineMessageSource>
+
+    enum class Kind {
+        GROUP,
+        FRIEND
+    }
+
+    /**
+     * 消息种类
+     */
+    abstract val kind: Kind
+
     // final override fun toString(): String = "OfflineMessageSource(sender=$senderId, target=$targetId)"
-} // TODO: 2020/4/4 可能要分群和好友
+}
 
 // For MessageChain
 

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

@@ -31,11 +31,11 @@ import kotlin.jvm.JvmName
  * @see MessageSource 获取更多信息
  */
 @SinceMirai("0.33.0")
-class QuoteReply(val source: OnlineMessageSource) : Message, MessageMetadata {
+class QuoteReply(val source: MessageSource) : Message, MessageMetadata {
     // TODO: 2020/4/4 Metadata or Content?
     companion object Key : Message.Key<QuoteReply>
 
-    override fun toString(): String = "[mirai:quote]"
+    override fun toString(): String = "[mirai:quote:${source.id}]"
 }
 
 suspend inline fun QuoteReply.recall() = this.source.recall()