123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- /*
- * Copyright 2019-2021 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_API_USAGE", "EXPERIMENTAL_OVERRIDE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
- package net.mamoe.mirai.internal.message
- import kotlinx.coroutines.CompletableDeferred
- import kotlinx.coroutines.CoroutineScope
- import kotlinx.coroutines.Deferred
- import kotlinx.coroutines.ExperimentalCoroutinesApi
- import kotlinx.serialization.Serializable
- import net.mamoe.mirai.Bot
- import net.mamoe.mirai.Mirai
- import net.mamoe.mirai.contact.*
- import net.mamoe.mirai.event.asyncFromEventOrNull
- import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor.SendGroupMessageReceipt
- import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
- import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
- import net.mamoe.mirai.internal.network.protocol.data.proto.SourceMsg
- import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
- 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.utils.toLongUnsigned
- import java.util.concurrent.atomic.AtomicBoolean
- private fun <T> T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg
- where T : MessageSourceInternal, T : MessageSource {
- val elements = originalMessage.toRichTextElems(subject, withGeneralFlags = true)
- val pdReserve = SourceMsg.ResvAttr(
- origUids = sequenceIds.zip(internalIds)
- .map { (seq, internal) -> seq.toLong().shl(32) or internal.toLongUnsigned() }
- )
- return ImMsgBody.SourceMsg(
- origSeqs = sequenceIds,
- senderUin = fromId,
- toUin = targetId,
- flag = 1,
- elems = elements,
- type = 0,
- time = time,
- pbReserve = pdReserve.toByteArray(SourceMsg.ResvAttr.serializer()),
- srcMsg = MsgComm.Msg(
- msgHead = MsgComm.MsgHead(
- fromUin = fromId, // qq
- toUin = targetId, // group
- msgType = 9, // 82?
- c2cCmd = 11,
- msgSeq = sequenceIds.first(),
- msgTime = time,
- msgUid = pdReserve.origUids!!.first(),
- // groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
- isSrcMsg = true
- ),
- msgBody = ImMsgBody.MsgBody(
- richText = ImMsgBody.RichText(
- elems = elements.toMutableList().also {
- if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
- }
- )
- )
- ).toByteArray(MsgComm.Msg.serializer())
- )
- }
- @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
- @Serializable(OnlineMessageSourceToFriendImpl.Serializer::class)
- internal class OnlineMessageSourceToFriendImpl(
- override val sequenceIds: IntArray,
- override val internalIds: IntArray,
- override val time: Int,
- override var originalMessage: MessageChain,
- override val sender: Bot,
- override val target: Friend,
- ) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal, OutgoingMessageSourceInternal {
- object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToFriend")
- override val bot: Bot
- get() = sender
- override val ids: IntArray
- get() = sequenceIds
- override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
- private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) }
- override fun toJceData(): ImMsgBody.SourceMsg = jceData
- }
- @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
- @Serializable(OnlineMessageSourceToStrangerImpl.Serializer::class)
- internal class OnlineMessageSourceToStrangerImpl(
- override val sequenceIds: IntArray,
- override val internalIds: IntArray,
- override val time: Int,
- override var originalMessage: MessageChain,
- override val sender: Bot,
- override val target: Stranger,
- ) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal, OutgoingMessageSourceInternal {
- constructor(
- delegate: Outgoing,
- target: Stranger,
- ) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
- object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
- override val bot: Bot
- get() = sender
- override val ids: IntArray
- get() = sequenceIds
- override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
- private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) }
- override fun toJceData(): ImMsgBody.SourceMsg = jceData
- }
- @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
- @Serializable(OnlineMessageSourceToTempImpl.Serializer::class)
- internal class OnlineMessageSourceToTempImpl(
- override val sequenceIds: IntArray,
- override val internalIds: IntArray,
- override val time: Int,
- override var originalMessage: MessageChain,
- override val sender: Bot,
- override val target: Member,
- ) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal, OutgoingMessageSourceInternal {
- constructor(
- delegate: Outgoing,
- target: Member,
- ) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
- object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
- override val bot: Bot
- get() = sender
- override val ids: IntArray
- get() = sequenceIds
- override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
- private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) }
- override fun toJceData(): ImMsgBody.SourceMsg = jceData
- }
- @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
- @Serializable(OnlineMessageSourceToGroupImpl.Serializer::class)
- internal class OnlineMessageSourceToGroupImpl(
- coroutineScope: CoroutineScope,
- override val internalIds: IntArray, // aka random
- override val time: Int,
- override var originalMessage: MessageChain,
- override val sender: Bot,
- override val target: Group,
- providedSequenceIds: IntArray? = null,
- ) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal, OutgoingMessageSourceInternal {
- object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToGroup")
- override val ids: IntArray
- get() = sequenceIds
- override val bot: Bot
- get() = sender
- override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
- private val sequenceIdDeferred: Deferred<IntArray?> = providedSequenceIds?.let { CompletableDeferred(it) } ?: run {
- val multi = mutableMapOf<Int, Int>()
- coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, IntArray>(
- timeoutMillis = 3000L * this@OnlineMessageSourceToGroupImpl.internalIds.size
- ) {
- if (it.messageRandom in this@OnlineMessageSourceToGroupImpl.internalIds) {
- multi[it.messageRandom] = it.sequenceId
- if (multi.size == this@OnlineMessageSourceToGroupImpl.internalIds.size) {
- IntArray(multi.size) { index ->
- multi[this@OnlineMessageSourceToGroupImpl.internalIds[index]]!!
- }
- } else null
- } else null
- }
- }
- @OptIn(ExperimentalCoroutinesApi::class)
- override val sequenceIds: IntArray
- get() = when {
- sequenceIdDeferred.isCompleted -> sequenceIdDeferred.getCompleted() ?: intArrayOf()
- !sequenceIdDeferred.isActive -> intArrayOf()
- else -> error("sequenceIds not yet available")
- }
- suspend fun ensureSequenceIdAvailable() = kotlin.run { sequenceIdDeferred.await() }
- private val jceData: ImMsgBody.SourceMsg by lazy {
- val elements = originalMessage.toRichTextElems(subject, withGeneralFlags = true)
- ImMsgBody.SourceMsg(
- origSeqs = sequenceIds,
- senderUin = fromId,
- toUin = Mirai.calculateGroupUinByGroupCode(targetId),
- flag = 1,
- elems = elements,
- type = 0,
- time = time,
- pbReserve = SourceMsg.ResvAttr(
- origUids = internalIds.map { it.toLongUnsigned() } // ids is actually messageRandom
- ).toByteArray(SourceMsg.ResvAttr.serializer()),
- srcMsg = MsgComm.Msg(
- msgHead = MsgComm.MsgHead(
- fromUin = fromId, // qq
- toUin = Mirai.calculateGroupUinByGroupCode(targetId), // group
- msgType = 82, // 82?
- c2cCmd = 1,
- msgSeq = sequenceIds.single(),
- msgTime = time,
- msgUid = internalIds.single().toLongUnsigned(),
- groupInfo = MsgComm.GroupInfo(groupCode = targetId),
- isSrcMsg = true
- ),
- msgBody = ImMsgBody.MsgBody(
- richText = ImMsgBody.RichText(
- elems = elements.toMutableList().also {
- if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
- }
- )
- )
- ).toByteArray(MsgComm.Msg.serializer())
- )
- }
- override fun toJceData(): ImMsgBody.SourceMsg = jceData
- }
|