This commit is contained in:
Him188 2020-04-04 23:22:52 +08:00
parent 19bc7b80c5
commit 864cde060e
10 changed files with 89 additions and 29 deletions

View File

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

View File

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

View File

@ -60,7 +60,7 @@ internal class MessageSourceFromFriendImpl(
} }
override val id: Int get() = msg.msgBody.richText.attr!!.random override val id: Int get() = msg.msgBody.richText.attr!!.random
override val time: Int get() = msg.msgHead.msgTime 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 target: Bot get() = bot
override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin) 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 sequenceId: Int get() = msg.msgHead.msgSeq
override val id: Int get() = msg.msgBody.richText.attr!!.random override val id: Int get() = msg.msgBody.richText.attr!!.random
override val time: Int get() = msg.msgHead.msgTime 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 target: Bot get() = bot
override val sender: Member override val sender: Member
get() = bot.getGroup( get() = bot.getGroup(
@ -143,12 +149,11 @@ internal class MessageSourceFromGroupImpl(
} }
internal class OfflineMessageSourceImpl( // from others' quotation 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 { ) : OfflineMessageSource(), MessageSourceImpl {
override val kind: Kind get() = if (delegate.srcMsg == null) Kind.GROUP else Kind.FRIEND
init {
println(delegate._miraiContentToString())
}
private val isRecalled: AtomicBoolean = atomic(false) private val isRecalled: AtomicBoolean = atomic(false)
override var isRecalledOrPlanned: Boolean override var isRecalledOrPlanned: Boolean
@ -159,7 +164,7 @@ internal class OfflineMessageSourceImpl( // from others' quotation
override val sequenceId: Int override val sequenceId: Int
get() = delegate.origSeqs?.first() ?: error("cannot find sequenceId") get() = delegate.origSeqs?.first() ?: error("cannot find sequenceId")
override val time: Int get() = delegate.time 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 override val id: Long
get() = (delegate.origSeqs?.firstOrNull() get() = (delegate.origSeqs?.firstOrNull()
@ -172,7 +177,21 @@ internal class OfflineMessageSourceImpl( // from others' quotation
// override val sourceMessage: MessageChain get() = delegate.toMessageChain() // override val sourceMessage: MessageChain get() = delegate.toMessageChain()
override val fromId: Long get() = delegate.senderUin 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( internal class MessageSourceToFriendImpl(

View File

@ -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() private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @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 val elements = this.msgBody.richText.elems
return buildMessageChain(elements.size + 1) { return buildMessageChain(elements.size + 1) {
if (addSource) { if (addSource) {
if (isGroup) { if (groupIdOrZero != 0L) {
+MessageSourceFromGroupImpl(bot, this@toMessageChain) +MessageSourceFromGroupImpl(bot, this@toMessageChain)
} else { } else {
+MessageSourceFromFriendImpl(bot, this@toMessageChain) +MessageSourceFromFriendImpl(bot, this@toMessageChain)
} }
} }
elements.joinToMessageChain(bot, this) elements.joinToMessageChain(groupIdOrZero, bot, this)
}.cleanupRubbishMessageElements() }.cleanupRubbishMessageElements()
} }
// These two functions have difference method signature, don't combine. // These two functions have difference method signature, don't combine.
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @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!! val elements = this.elems!!
return buildMessageChain(elements.size + 1) { return buildMessageChain(elements.size + 1) {
+OfflineMessageSourceImpl(delegate = this@toMessageChain, bot = bot) if (withSource) {
elements.joinToMessageChain(bot, this) +OfflineMessageSourceImpl(delegate = this@toMessageChain, bot = bot, groupIdOrZero = groupIdOrZero)
}
elements.joinToMessageChain(groupIdOrZero, bot, this)
}.cleanupRubbishMessageElements() }.cleanupRubbishMessageElements()
} }
@ -254,11 +256,11 @@ internal inline fun <reified R> Iterable<*>.firstIsInstanceOrNull(): R? {
} }
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class) @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._miraiContentToString())
this.forEach { this.forEach {
when { 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.notOnlineImage != null -> message.add(OnlineFriendImageImpl(it.notOnlineImage))
it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace)) it.customFace != null -> message.add(OnlineGroupImageImpl(it.customFace))
it.face != null -> message.add(Face(it.face.index)) it.face != null -> message.add(Face(it.face.index))

View File

@ -885,7 +885,7 @@ internal class ImMsgBody : ProtoBuf {
@ProtoId(6) val type: Int = 0, @ProtoId(6) val type: Int = 0,
@ProtoId(7) val richMsg: ByteArray = EMPTY_BYTE_ARRAY, @ProtoId(7) val richMsg: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(8) val pbReserve: 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(10) val toUin: Long = 0L,
@ProtoId(11) val troopName: ByteArray = EMPTY_BYTE_ARRAY @ProtoId(11) val troopName: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf ) : ProtoBuf

View File

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

View File

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

View File

@ -84,7 +84,7 @@ internal class OnlinePush {
return GroupMessage( return GroupMessage(
senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard, senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
sender = group[pbPushMsg.msg.msgHead.fromUin], 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 { permission = when {
flags and 16 != 0 -> MemberPermission.ADMINISTRATOR flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
flags and 8 != 0 -> MemberPermission.OWNER flags and 8 != 0 -> MemberPermission.OWNER

View File

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

View File

@ -31,11 +31,11 @@ import kotlin.jvm.JvmName
* @see MessageSource 获取更多信息 * @see MessageSource 获取更多信息
*/ */
@SinceMirai("0.33.0") @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? // TODO: 2020/4/4 Metadata or Content?
companion object Key : Message.Key<QuoteReply> 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() suspend inline fun QuoteReply.recall() = this.source.recall()