mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 22:09:50 +08:00
Rearrange implementations for MessageSource, implement recall state check
This commit is contained in:
parent
7fbc06b4b2
commit
db8cb84c99
@ -291,11 +291,14 @@ internal abstract class QQAndroidBotBase constructor(
|
|||||||
|
|
||||||
@Suppress("RemoveExplicitTypeArguments") // false positive
|
@Suppress("RemoveExplicitTypeArguments") // false positive
|
||||||
override suspend fun recall(source: MessageSource) {
|
override suspend fun recall(source: MessageSource) {
|
||||||
// println(source._miraiContentToString())
|
check(source is MessageSourceInternal)
|
||||||
|
|
||||||
check(source is MessageSourceImpl)
|
|
||||||
source.ensureSequenceIdAvailable()
|
source.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
|
@Suppress("BooleanLiteralArgument") // false positive
|
||||||
|
check(!source.isRecalledOrPlanned.value && source.isRecalledOrPlanned.compareAndSet(false, true)) {
|
||||||
|
"$source had already been recalled."
|
||||||
|
}
|
||||||
|
|
||||||
val response: PbMessageSvc.PbMsgWithDraw.Response = when (source) {
|
val response: PbMessageSvc.PbMsgWithDraw.Response = when (source) {
|
||||||
is MessageSourceToGroupImpl,
|
is MessageSourceToGroupImpl,
|
||||||
is MessageSourceFromGroupImpl
|
is MessageSourceFromGroupImpl
|
||||||
|
@ -1,506 +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_API_USAGE", "EXPERIMENTAL_OVERRIDE")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.qqandroid.message
|
|
||||||
|
|
||||||
import kotlinx.atomicfu.AtomicBoolean
|
|
||||||
import kotlinx.atomicfu.atomic
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Deferred
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.contact.Member
|
|
||||||
import net.mamoe.mirai.contact.QQ
|
|
||||||
import net.mamoe.mirai.event.subscribingGetAsync
|
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
|
||||||
import net.mamoe.mirai.message.data.MessageSource
|
|
||||||
import net.mamoe.mirai.message.data.OfflineMessageSource
|
|
||||||
import net.mamoe.mirai.message.data.OnlineMessageSource
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
|
||||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
|
|
||||||
import net.mamoe.mirai.qqandroid.utils._miraiContentToString
|
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
|
|
||||||
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
|
||||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
|
||||||
|
|
||||||
|
|
||||||
internal interface MessageSourceImpl {
|
|
||||||
val sequenceId: Int
|
|
||||||
|
|
||||||
var isRecalledOrPlanned: Boolean // // TODO: 2020/4/7 实现 isRecalledOrPlanned
|
|
||||||
|
|
||||||
fun toJceData(): ImMsgBody.SourceMsg
|
|
||||||
}
|
|
||||||
|
|
||||||
internal suspend inline fun MessageSource.ensureSequenceIdAvailable() {
|
|
||||||
if (this is MessageSourceToGroupImpl) {
|
|
||||||
this.ensureSequenceIdAvailable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceFromFriendImpl(
|
|
||||||
override val bot: Bot,
|
|
||||||
val msg: MsgComm.Msg
|
|
||||||
) : OnlineMessageSource.Incoming.FromFriend(), MessageSourceImpl {
|
|
||||||
override val sequenceId: Int get() = msg.msgHead.msgSeq
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
groupIdOrZero = 0,
|
|
||||||
onlineSource = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin)
|
|
||||||
|
|
||||||
private val elems by lazy {
|
|
||||||
msg.msgBody.richText.elems.toMutableList().also {
|
|
||||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(msg.msgHead.msgSeq),
|
|
||||||
senderUin = msg.msgHead.fromUin,
|
|
||||||
toUin = msg.msgHead.toUin,
|
|
||||||
flag = 1,
|
|
||||||
elems = msg.msgBody.richText.elems,
|
|
||||||
type = 0,
|
|
||||||
time = msg.msgHead.msgTime,
|
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
|
||||||
origUids = id.toLong() and 0xFFFF_FFFF
|
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
|
||||||
srcMsg = MsgComm.Msg(
|
|
||||||
msgHead = MsgComm.MsgHead(
|
|
||||||
fromUin = msg.msgHead.fromUin, // qq
|
|
||||||
toUin = msg.msgHead.toUin, // group
|
|
||||||
msgType = msg.msgHead.msgType, // 82?
|
|
||||||
c2cCmd = msg.msgHead.c2cCmd,
|
|
||||||
msgSeq = msg.msgHead.msgSeq,
|
|
||||||
msgTime = msg.msgHead.msgTime,
|
|
||||||
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
|
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
|
||||||
isSrcMsg = true
|
|
||||||
),
|
|
||||||
msgBody = ImMsgBody.MsgBody(
|
|
||||||
richText = ImMsgBody.RichText(
|
|
||||||
elems = elems
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).toByteArray(MsgComm.Msg.serializer())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceFromTempImpl(
|
|
||||||
override val bot: Bot,
|
|
||||||
private val msg: MsgComm.Msg
|
|
||||||
) : OnlineMessageSource.Incoming.FromTemp(), MessageSourceImpl {
|
|
||||||
override val sequenceId: Int get() = msg.msgHead.msgSeq
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
groupIdOrZero = 0,
|
|
||||||
onlineSource = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override val sender: Member
|
|
||||||
get() = with(msg.msgHead) {
|
|
||||||
bot.getGroup(c2cTmpMsgHead!!.groupUin)[fromUin]
|
|
||||||
}
|
|
||||||
|
|
||||||
private val elems by lazy {
|
|
||||||
msg.msgBody.richText.elems.toMutableList().also {
|
|
||||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(msg.msgHead.msgSeq),
|
|
||||||
senderUin = msg.msgHead.fromUin,
|
|
||||||
toUin = msg.msgHead.toUin,
|
|
||||||
flag = 1,
|
|
||||||
elems = msg.msgBody.richText.elems,
|
|
||||||
type = 0,
|
|
||||||
time = msg.msgHead.msgTime,
|
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
|
||||||
origUids = id.toLong() and 0xFFFF_FFFF
|
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
|
||||||
srcMsg = MsgComm.Msg(
|
|
||||||
msgHead = MsgComm.MsgHead(
|
|
||||||
fromUin = msg.msgHead.fromUin, // qq
|
|
||||||
toUin = msg.msgHead.toUin, // group
|
|
||||||
msgType = msg.msgHead.msgType, // 82?
|
|
||||||
c2cCmd = msg.msgHead.c2cCmd,
|
|
||||||
msgSeq = msg.msgHead.msgSeq,
|
|
||||||
msgTime = msg.msgHead.msgTime,
|
|
||||||
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
|
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = msg.msgHead.groupInfo.groupCode),
|
|
||||||
isSrcMsg = true
|
|
||||||
),
|
|
||||||
msgBody = ImMsgBody.MsgBody(
|
|
||||||
richText = ImMsgBody.RichText(
|
|
||||||
elems = elems
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).toByteArray(MsgComm.Msg.serializer())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceFromGroupImpl(
|
|
||||||
override val bot: Bot,
|
|
||||||
private val msg: MsgComm.Msg
|
|
||||||
) : OnlineMessageSource.Incoming.FromGroup(), MessageSourceImpl {
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
groupIdOrZero = group.id,
|
|
||||||
onlineSource = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override val sender: Member
|
|
||||||
get() = bot.getGroup(
|
|
||||||
msg.msgHead.groupInfo?.groupCode
|
|
||||||
?: error("cannot find groupCode for MessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}")
|
|
||||||
).getOrNull(msg.msgHead.fromUin)
|
|
||||||
?: error("cannot find member for MessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}")
|
|
||||||
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(msg.msgHead.msgSeq),
|
|
||||||
senderUin = msg.msgHead.fromUin,
|
|
||||||
toUin = 0,
|
|
||||||
flag = 1,
|
|
||||||
elems = msg.msgBody.richText.elems,
|
|
||||||
type = 0,
|
|
||||||
time = msg.msgHead.msgTime,
|
|
||||||
pbReserve = EMPTY_BYTE_ARRAY,
|
|
||||||
srcMsg = EMPTY_BYTE_ARRAY
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OfflineMessageSourceImplByMsg(
|
|
||||||
// from other sources' originalMessage
|
|
||||||
val delegate: MsgComm.Msg,
|
|
||||||
override val bot: Bot
|
|
||||||
) : OfflineMessageSource(), MessageSourceImpl {
|
|
||||||
override val kind: Kind = if (delegate.msgHead.groupInfo != null) Kind.GROUP else Kind.FRIEND
|
|
||||||
override val id: Int
|
|
||||||
get() = delegate.msgHead.msgUid.toInt()
|
|
||||||
override val time: Int
|
|
||||||
get() = delegate.msgHead.msgTime
|
|
||||||
override val fromId: Long
|
|
||||||
get() = delegate.msgHead.fromUin
|
|
||||||
override val targetId: Long
|
|
||||||
get() = delegate.msgHead.groupInfo?.groupCode ?: delegate.msgHead.toUin
|
|
||||||
override val originalMessage: MessageChain by lazy {
|
|
||||||
delegate.toMessageChain(bot,
|
|
||||||
groupIdOrZero = delegate.msgHead.groupInfo?.groupCode ?: 0,
|
|
||||||
onlineSource = false,
|
|
||||||
isTemp = delegate.msgHead.c2cTmpMsgHead != null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override val sequenceId: Int
|
|
||||||
get() = delegate.msgHead.msgSeq
|
|
||||||
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
|
||||||
senderUin = delegate.msgHead.fromUin,
|
|
||||||
toUin = 0,
|
|
||||||
flag = 1,
|
|
||||||
elems = delegate.msgBody.richText.elems,
|
|
||||||
type = 0,
|
|
||||||
time = delegate.msgHead.msgTime,
|
|
||||||
pbReserve = EMPTY_BYTE_ARRAY,
|
|
||||||
srcMsg = EMPTY_BYTE_ARRAY
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OfflineMessageSourceImplBySourceMsg(
|
|
||||||
// from others' quotation
|
|
||||||
val delegate: ImMsgBody.SourceMsg,
|
|
||||||
override val bot: Bot,
|
|
||||||
groupIdOrZero: Long
|
|
||||||
) : OfflineMessageSource(), MessageSourceImpl {
|
|
||||||
override val kind: Kind get() = if (delegate.srcMsg == null) Kind.GROUP else Kind.FRIEND
|
|
||||||
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
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, groupIdOrZero) }
|
|
||||||
/*
|
|
||||||
override val id: Long
|
|
||||||
get() = (delegate.origSeqs?.firstOrNull()
|
|
||||||
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
|
|
||||||
delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF)
|
|
||||||
*/
|
|
||||||
|
|
||||||
override val id: Int
|
|
||||||
get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids?.toInt()
|
|
||||||
?: 0
|
|
||||||
|
|
||||||
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
|
||||||
override val fromId: Long get() = delegate.senderUin
|
|
||||||
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 }
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
)*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return delegate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceToFriendImpl(
|
|
||||||
override val sequenceId: Int,
|
|
||||||
override val id: Int,
|
|
||||||
override val time: Int,
|
|
||||||
override val originalMessage: MessageChain,
|
|
||||||
override val sender: Bot,
|
|
||||||
override val target: QQ
|
|
||||||
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceImpl {
|
|
||||||
override val bot: Bot
|
|
||||||
get() = sender
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
private val elems by lazy {
|
|
||||||
originalMessage.toRichTextElems(forGroup = false, withGeneralFlags = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
val messageUid: Long = sequenceId.toLong().shl(32) or id.toLong().and(0xffFFffFF)
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(sequenceId),
|
|
||||||
senderUin = fromId,
|
|
||||||
toUin = targetId,
|
|
||||||
flag = 1,
|
|
||||||
elems = elems,
|
|
||||||
type = 0,
|
|
||||||
time = time,
|
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
|
||||||
origUids = messageUid
|
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
|
||||||
srcMsg = MsgComm.Msg(
|
|
||||||
msgHead = MsgComm.MsgHead(
|
|
||||||
fromUin = fromId, // qq
|
|
||||||
toUin = targetId, // group
|
|
||||||
msgType = 9, // 82?
|
|
||||||
c2cCmd = 11,
|
|
||||||
msgSeq = sequenceId,
|
|
||||||
msgTime = time,
|
|
||||||
msgUid = messageUid, // ok
|
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
|
||||||
isSrcMsg = true
|
|
||||||
),
|
|
||||||
msgBody = ImMsgBody.MsgBody(
|
|
||||||
richText = ImMsgBody.RichText(
|
|
||||||
elems = elems.toMutableList().also {
|
|
||||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).toByteArray(MsgComm.Msg.serializer())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceToTempImpl(
|
|
||||||
override val sequenceId: Int,
|
|
||||||
override val id: Int,
|
|
||||||
override val time: Int,
|
|
||||||
override val originalMessage: MessageChain,
|
|
||||||
override val sender: Bot,
|
|
||||||
override val target: Member
|
|
||||||
) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceImpl {
|
|
||||||
override val bot: Bot
|
|
||||||
get() = sender
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
private val elems by lazy {
|
|
||||||
originalMessage.toRichTextElems(forGroup = false, withGeneralFlags = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
val messageUid: Long = sequenceId.toLong().shl(32) or id.toLong().and(0xffFFffFF)
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(sequenceId),
|
|
||||||
senderUin = fromId,
|
|
||||||
toUin = targetId,
|
|
||||||
flag = 1,
|
|
||||||
elems = elems,
|
|
||||||
type = 0,
|
|
||||||
time = time,
|
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
|
||||||
origUids = messageUid
|
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
|
||||||
srcMsg = MsgComm.Msg(
|
|
||||||
msgHead = MsgComm.MsgHead(
|
|
||||||
fromUin = fromId, // qq
|
|
||||||
toUin = targetId, // group
|
|
||||||
msgType = 9, // 82?
|
|
||||||
c2cCmd = 11,
|
|
||||||
msgSeq = sequenceId,
|
|
||||||
msgTime = time,
|
|
||||||
msgUid = messageUid, // ok
|
|
||||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
|
||||||
isSrcMsg = true
|
|
||||||
),
|
|
||||||
msgBody = ImMsgBody.MsgBody(
|
|
||||||
richText = ImMsgBody.RichText(
|
|
||||||
elems = elems.toMutableList().also {
|
|
||||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).toByteArray(MsgComm.Msg.serializer())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class MessageSourceToGroupImpl(
|
|
||||||
override val id: Int,
|
|
||||||
override val time: Int,
|
|
||||||
override val originalMessage: MessageChain,
|
|
||||||
override val sender: Bot,
|
|
||||||
override val target: Group
|
|
||||||
) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceImpl {
|
|
||||||
override val bot: Bot
|
|
||||||
get() = sender
|
|
||||||
private val isRecalled: AtomicBoolean = atomic(false)
|
|
||||||
override var isRecalledOrPlanned: Boolean
|
|
||||||
get() = isRecalled.value
|
|
||||||
set(value) {
|
|
||||||
isRecalled.value = value
|
|
||||||
}
|
|
||||||
private val elems by lazy {
|
|
||||||
originalMessage.toRichTextElems(forGroup = false, withGeneralFlags = true)
|
|
||||||
}
|
|
||||||
private lateinit var sequenceIdDeferred: Deferred<Int>
|
|
||||||
override val sequenceId: Int get() = sequenceIdDeferred.getCompleted()
|
|
||||||
|
|
||||||
@OptIn(MiraiExperimentalAPI::class)
|
|
||||||
internal fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
|
|
||||||
sequenceIdDeferred =
|
|
||||||
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
|
||||||
timeoutMillis = 3000
|
|
||||||
) {
|
|
||||||
if (it.messageRandom == this@MessageSourceToGroupImpl.id) {
|
|
||||||
it.sequenceId
|
|
||||||
} else null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun ensureSequenceIdAvailable() {
|
|
||||||
sequenceIdDeferred.join()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun toJceData(): ImMsgBody.SourceMsg {
|
|
||||||
return ImMsgBody.SourceMsg(
|
|
||||||
origSeqs = listOf(sequenceId),
|
|
||||||
senderUin = fromId,
|
|
||||||
toUin = Group.calculateGroupUinByGroupCode(targetId),
|
|
||||||
flag = 1,
|
|
||||||
elems = elems,
|
|
||||||
type = 0,
|
|
||||||
time = time,
|
|
||||||
pbReserve = SourceMsg.ResvAttr(
|
|
||||||
origUids = id.toLong() and 0xffFFffFF // id is actually messageRandom
|
|
||||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
|
||||||
srcMsg = MsgComm.Msg(
|
|
||||||
msgHead = MsgComm.MsgHead(
|
|
||||||
fromUin = fromId, // qq
|
|
||||||
toUin = Group.calculateGroupUinByGroupCode(targetId), // group
|
|
||||||
msgType = 82, // 82?
|
|
||||||
c2cCmd = 1,
|
|
||||||
msgSeq = sequenceId,
|
|
||||||
msgTime = time,
|
|
||||||
msgUid = id.toLong() and 0xffFFffFF, // ok
|
|
||||||
groupInfo = MsgComm.GroupInfo(groupCode = targetId),
|
|
||||||
isSrcMsg = true
|
|
||||||
),
|
|
||||||
msgBody = ImMsgBody.MsgBody(
|
|
||||||
richText = ImMsgBody.RichText(
|
|
||||||
elems = elems.toMutableList().also {
|
|
||||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).toByteArray(MsgComm.Msg.serializer())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,7 +40,7 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
|
|||||||
|
|
||||||
if (this.anyIsInstance<QuoteReply>()) {
|
if (this.anyIsInstance<QuoteReply>()) {
|
||||||
when (val source = this[QuoteReply].source) {
|
when (val source = this[QuoteReply].source) {
|
||||||
is MessageSourceImpl -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
is MessageSourceInternal -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||||
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}. Don't implement your own MessageSource.")
|
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}. Don't implement your own MessageSource.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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_API_USAGE", "EXPERIMENTAL_OVERRIDE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.qqandroid.message
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.contact.Member
|
||||||
|
import net.mamoe.mirai.contact.QQ
|
||||||
|
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
|
||||||
|
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.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||||
|
import net.mamoe.mirai.qqandroid.utils._miraiContentToString
|
||||||
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||||
|
|
||||||
|
internal interface MessageSourceInternal {
|
||||||
|
val sequenceId: Int
|
||||||
|
|
||||||
|
val isRecalledOrPlanned: MiraiAtomicBoolean
|
||||||
|
|
||||||
|
fun toJceData(): ImMsgBody.SourceMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
internal suspend inline fun MessageSource.ensureSequenceIdAvailable() {
|
||||||
|
if (this is MessageSourceToGroupImpl) {
|
||||||
|
this.ensureSequenceIdAvailable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageSourceFromFriendImpl(
|
||||||
|
override val bot: Bot,
|
||||||
|
val msg: MsgComm.Msg
|
||||||
|
) : OnlineMessageSource.Incoming.FromFriend(), MessageSourceInternal {
|
||||||
|
override val sequenceId: Int get() = msg.msgHead.msgSeq
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
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, 0, false) }
|
||||||
|
override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin)
|
||||||
|
|
||||||
|
private val jceData by lazy { msg.toJceDataFriendOrTemp(id) }
|
||||||
|
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg = jceData
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MsgComm.Msg.toJceDataFriendOrTemp(id: Int): ImMsgBody.SourceMsg {
|
||||||
|
val elements = msgBody.richText.elems.toMutableList().also {
|
||||||
|
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||||
|
}
|
||||||
|
return ImMsgBody.SourceMsg(
|
||||||
|
origSeqs = listOf(this.msgHead.msgSeq),
|
||||||
|
senderUin = this.msgHead.fromUin,
|
||||||
|
toUin = this.msgHead.toUin,
|
||||||
|
flag = 1,
|
||||||
|
elems = this.msgBody.richText.elems,
|
||||||
|
type = 0,
|
||||||
|
time = this.msgHead.msgTime,
|
||||||
|
pbReserve = SourceMsg.ResvAttr(
|
||||||
|
origUids = id.toLong() and 0xFFFF_FFFF
|
||||||
|
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||||
|
srcMsg = MsgComm.Msg(
|
||||||
|
msgHead = MsgComm.MsgHead(
|
||||||
|
fromUin = this.msgHead.fromUin, // qq
|
||||||
|
toUin = this.msgHead.toUin, // group
|
||||||
|
msgType = this.msgHead.msgType, // 82?
|
||||||
|
c2cCmd = this.msgHead.c2cCmd,
|
||||||
|
msgSeq = this.msgHead.msgSeq,
|
||||||
|
msgTime = this.msgHead.msgTime,
|
||||||
|
msgUid = id.toLong() and 0xFFFF_FFFF, // ok
|
||||||
|
// groupInfo = MsgComm.GroupInfo(groupCode = this.msgHead.groupInfo.groupCode),
|
||||||
|
isSrcMsg = true
|
||||||
|
),
|
||||||
|
msgBody = ImMsgBody.MsgBody(
|
||||||
|
richText = ImMsgBody.RichText(
|
||||||
|
elems = elements
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).toByteArray(MsgComm.Msg.serializer())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageSourceFromTempImpl(
|
||||||
|
override val bot: Bot,
|
||||||
|
private val msg: MsgComm.Msg
|
||||||
|
) : OnlineMessageSource.Incoming.FromTemp(), MessageSourceInternal {
|
||||||
|
override val sequenceId: Int get() = msg.msgHead.msgSeq
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
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, 0, false) }
|
||||||
|
override val sender: Member get() = with(msg.msgHead) { bot.getGroup(c2cTmpMsgHead!!.groupUin)[fromUin] }
|
||||||
|
|
||||||
|
private val jceData by lazy { msg.toJceDataFriendOrTemp(id) }
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg = jceData
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class MessageSourceFromGroupImpl(
|
||||||
|
override val bot: Bot,
|
||||||
|
private val msg: MsgComm.Msg
|
||||||
|
) : OnlineMessageSource.Incoming.FromGroup(), MessageSourceInternal {
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
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, groupIdOrZero = group.id, onlineSource = false)
|
||||||
|
}
|
||||||
|
override val sender: Member
|
||||||
|
get() = bot.getGroup(
|
||||||
|
msg.msgHead.groupInfo?.groupCode
|
||||||
|
?: error("cannot find groupCode for MessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}")
|
||||||
|
).getOrNull(msg.msgHead.fromUin)
|
||||||
|
?: error("cannot find member for MessageSourceFromGroupImpl. msg=${msg._miraiContentToString()}")
|
||||||
|
|
||||||
|
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg {
|
||||||
|
return ImMsgBody.SourceMsg(
|
||||||
|
origSeqs = listOf(msg.msgHead.msgSeq),
|
||||||
|
senderUin = msg.msgHead.fromUin,
|
||||||
|
toUin = 0,
|
||||||
|
flag = 1,
|
||||||
|
elems = msg.msgBody.richText.elems,
|
||||||
|
type = 0,
|
||||||
|
time = msg.msgHead.msgTime,
|
||||||
|
pbReserve = EMPTY_BYTE_ARRAY,
|
||||||
|
srcMsg = EMPTY_BYTE_ARRAY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* 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_API_USAGE", "EXPERIMENTAL_OVERRIDE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.qqandroid.message
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
|
||||||
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
|
import net.mamoe.mirai.message.data.OfflineMessageSource
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||||
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
|
||||||
|
|
||||||
|
|
||||||
|
internal class OfflineMessageSourceImplByMsg(
|
||||||
|
// from other sources' originalMessage
|
||||||
|
val delegate: MsgComm.Msg,
|
||||||
|
override val bot: Bot
|
||||||
|
) : OfflineMessageSource(), MessageSourceInternal {
|
||||||
|
override val kind: Kind = if (delegate.msgHead.groupInfo != null) Kind.GROUP else Kind.FRIEND
|
||||||
|
override val id: Int
|
||||||
|
get() = delegate.msgHead.msgUid.toInt()
|
||||||
|
override val time: Int
|
||||||
|
get() = delegate.msgHead.msgTime
|
||||||
|
override val fromId: Long
|
||||||
|
get() = delegate.msgHead.fromUin
|
||||||
|
override val targetId: Long
|
||||||
|
get() = delegate.msgHead.groupInfo?.groupCode ?: delegate.msgHead.toUin
|
||||||
|
override val originalMessage: MessageChain by lazy {
|
||||||
|
delegate.toMessageChain(bot,
|
||||||
|
groupIdOrZero = delegate.msgHead.groupInfo?.groupCode ?: 0,
|
||||||
|
onlineSource = false,
|
||||||
|
isTemp = delegate.msgHead.c2cTmpMsgHead != null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override val sequenceId: Int
|
||||||
|
get() = delegate.msgHead.msgSeq
|
||||||
|
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg {
|
||||||
|
return ImMsgBody.SourceMsg(
|
||||||
|
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||||
|
senderUin = delegate.msgHead.fromUin,
|
||||||
|
toUin = 0,
|
||||||
|
flag = 1,
|
||||||
|
elems = delegate.msgBody.richText.elems,
|
||||||
|
type = 0,
|
||||||
|
time = delegate.msgHead.msgTime,
|
||||||
|
pbReserve = EMPTY_BYTE_ARRAY,
|
||||||
|
srcMsg = EMPTY_BYTE_ARRAY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class OfflineMessageSourceImplBySourceMsg(
|
||||||
|
// from others' quotation
|
||||||
|
val delegate: ImMsgBody.SourceMsg,
|
||||||
|
override val bot: Bot,
|
||||||
|
groupIdOrZero: Long
|
||||||
|
) : OfflineMessageSource(), MessageSourceInternal {
|
||||||
|
override val kind: Kind get() = if (delegate.srcMsg == null) Kind.GROUP else Kind.FRIEND
|
||||||
|
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
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, groupIdOrZero) }
|
||||||
|
/*
|
||||||
|
override val id: Long
|
||||||
|
get() = (delegate.origSeqs?.firstOrNull()
|
||||||
|
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
|
||||||
|
delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.and(0xFFFFFFFF)
|
||||||
|
*/
|
||||||
|
|
||||||
|
override val id: Int
|
||||||
|
get() = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids?.toInt()
|
||||||
|
?: 0
|
||||||
|
|
||||||
|
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||||
|
override val fromId: Long get() = delegate.senderUin
|
||||||
|
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 }
|
||||||
|
)
|
||||||
|
}"
|
||||||
|
)*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg {
|
||||||
|
return delegate
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* 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_API_USAGE", "EXPERIMENTAL_OVERRIDE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.qqandroid.message
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Deferred
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.contact.Group
|
||||||
|
import net.mamoe.mirai.contact.Member
|
||||||
|
import net.mamoe.mirai.contact.QQ
|
||||||
|
import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
|
||||||
|
import net.mamoe.mirai.event.subscribingGetAsync
|
||||||
|
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.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||||
|
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
|
||||||
|
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
|
||||||
|
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T> T.toJceDataImpl(): ImMsgBody.SourceMsg
|
||||||
|
where T : MessageSourceInternal, T : MessageSource {
|
||||||
|
|
||||||
|
val elements = originalMessage.toRichTextElems(forGroup = false, withGeneralFlags = true)
|
||||||
|
val messageUid: Long = sequenceId.toLong().shl(32) or id.toLong().and(0xffFFffFF)
|
||||||
|
return ImMsgBody.SourceMsg(
|
||||||
|
origSeqs = listOf(sequenceId),
|
||||||
|
senderUin = fromId,
|
||||||
|
toUin = targetId,
|
||||||
|
flag = 1,
|
||||||
|
elems = elements,
|
||||||
|
type = 0,
|
||||||
|
time = time,
|
||||||
|
pbReserve = SourceMsg.ResvAttr(
|
||||||
|
origUids = messageUid
|
||||||
|
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||||
|
srcMsg = MsgComm.Msg(
|
||||||
|
msgHead = MsgComm.MsgHead(
|
||||||
|
fromUin = fromId, // qq
|
||||||
|
toUin = targetId, // group
|
||||||
|
msgType = 9, // 82?
|
||||||
|
c2cCmd = 11,
|
||||||
|
msgSeq = sequenceId,
|
||||||
|
msgTime = time,
|
||||||
|
msgUid = messageUid, // ok
|
||||||
|
// 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())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageSourceToFriendImpl(
|
||||||
|
override val sequenceId: Int,
|
||||||
|
override val id: Int,
|
||||||
|
override val time: Int,
|
||||||
|
override val originalMessage: MessageChain,
|
||||||
|
override val sender: Bot,
|
||||||
|
override val target: QQ
|
||||||
|
) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
|
||||||
|
override val bot: Bot
|
||||||
|
get() = sender
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
private val jceData by lazy { toJceDataImpl() }
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg = jceData
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageSourceToTempImpl(
|
||||||
|
override val sequenceId: Int,
|
||||||
|
override val id: Int,
|
||||||
|
override val time: Int,
|
||||||
|
override val originalMessage: MessageChain,
|
||||||
|
override val sender: Bot,
|
||||||
|
override val target: Member
|
||||||
|
) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal {
|
||||||
|
override val bot: Bot
|
||||||
|
get() = sender
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
private val jceData by lazy { toJceDataImpl() }
|
||||||
|
override fun toJceData(): ImMsgBody.SourceMsg = jceData
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class MessageSourceToGroupImpl(
|
||||||
|
override val id: Int,
|
||||||
|
override val time: Int,
|
||||||
|
override val originalMessage: MessageChain,
|
||||||
|
override val sender: Bot,
|
||||||
|
override val target: Group
|
||||||
|
) : OnlineMessageSource.Outgoing.ToGroup(), MessageSourceInternal {
|
||||||
|
override val bot: Bot
|
||||||
|
get() = sender
|
||||||
|
override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
|
||||||
|
private lateinit var sequenceIdDeferred: Deferred<Int>
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override val sequenceId: Int
|
||||||
|
get() = sequenceIdDeferred.getCompleted()
|
||||||
|
|
||||||
|
@OptIn(MiraiExperimentalAPI::class)
|
||||||
|
internal fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
|
||||||
|
sequenceIdDeferred =
|
||||||
|
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
||||||
|
timeoutMillis = 3000
|
||||||
|
) {
|
||||||
|
if (it.messageRandom == this@MessageSourceToGroupImpl.id) {
|
||||||
|
it.sequenceId
|
||||||
|
} else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun ensureSequenceIdAvailable() = sequenceIdDeferred.join()
|
||||||
|
|
||||||
|
private val jceData by lazy {
|
||||||
|
val elements = originalMessage.toRichTextElems(forGroup = false, withGeneralFlags = true)
|
||||||
|
ImMsgBody.SourceMsg(
|
||||||
|
origSeqs = listOf(sequenceId),
|
||||||
|
senderUin = fromId,
|
||||||
|
toUin = Group.calculateGroupUinByGroupCode(targetId),
|
||||||
|
flag = 1,
|
||||||
|
elems = elements,
|
||||||
|
type = 0,
|
||||||
|
time = time,
|
||||||
|
pbReserve = SourceMsg.ResvAttr(
|
||||||
|
origUids = id.toLong() and 0xffFFffFF // id is actually messageRandom
|
||||||
|
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||||
|
srcMsg = MsgComm.Msg(
|
||||||
|
msgHead = MsgComm.MsgHead(
|
||||||
|
fromUin = fromId, // qq
|
||||||
|
toUin = Group.calculateGroupUinByGroupCode(targetId), // group
|
||||||
|
msgType = 82, // 82?
|
||||||
|
c2cCmd = 1,
|
||||||
|
msgSeq = sequenceId,
|
||||||
|
msgTime = time,
|
||||||
|
msgUid = id.toLong() and 0xffFFffFF, // ok
|
||||||
|
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
|
||||||
|
}
|
@ -157,6 +157,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
|
|||||||
* @param source 消息源. 可从 [MessageReceipt.source] 获得, 或从消息事件中的 [MessageChain] 获得.
|
* @param source 消息源. 可从 [MessageReceipt.source] 获得, 或从消息事件中的 [MessageChain] 获得.
|
||||||
*
|
*
|
||||||
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
* @throws PermissionDeniedException 当 [Bot] 无权限操作时
|
||||||
|
* @throws IllegalStateException 当这条消息已经被撤回时 (仅同步主动操作)
|
||||||
*
|
*
|
||||||
* @see Bot.recall (扩展函数) 接受参数 [MessageChain]
|
* @see Bot.recall (扩展函数) 接受参数 [MessageChain]
|
||||||
* @see MessageSource.recall
|
* @see MessageSource.recall
|
||||||
|
Loading…
Reference in New Issue
Block a user