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>()
}
}
is OfflineMessageSource,
is MessageSourceFromFriendImpl,
is MessageSourceToFriendImpl
-> network.run {
check(source.fromId == this@QQAndroidBotBase.id) {
"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 == 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!")
}
// 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" }
}

View File

@ -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()

View File

@ -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(

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()
@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))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()