mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-31 03:22:36 +08:00
Support syncing message sent from other clients, close #644
This commit is contained in:
parent
77b5de8d41
commit
bef91ea9d0
@ -552,6 +552,42 @@ public class GroupMessageEvent(
|
||||
"GroupMessageEvent(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
|
||||
}
|
||||
|
||||
/**
|
||||
* 机器人在其他客户端发送消息同步到这个客户端的事件.
|
||||
*
|
||||
* 本事件发生于**机器人账号**在另一个客户端向一个群或一个好友主动发送消息, 这条消息同步到机器人这个客户端上.
|
||||
*
|
||||
* @see MessageEvent
|
||||
*/
|
||||
public interface MessageSyncEvent : MessageEvent
|
||||
|
||||
|
||||
/**
|
||||
* 机器人在其他客户端发送群消息同步到这个客户端的事件
|
||||
*
|
||||
* @see MessageSyncEvent
|
||||
*/
|
||||
public class GroupMessageSyncEvent(
|
||||
override val group: Group,
|
||||
override val message: MessageChain,
|
||||
override val sender: Member,
|
||||
override val senderName: String,
|
||||
override val time: Int
|
||||
) : AbstractMessageEvent(), GroupAwareMessageEvent, MessageSyncEvent {
|
||||
init {
|
||||
val source = message[MessageSource] ?: error("Cannot find MessageSource from message")
|
||||
check(source is OnlineMessageSource.Incoming.FromGroup) { "source provided to a GroupMessage must be an instance of OnlineMessageSource.Incoming.FromGroup" }
|
||||
}
|
||||
|
||||
override val bot: Bot get() = group.bot
|
||||
override val subject: Group get() = group
|
||||
override val source: OnlineMessageSource.Incoming.FromGroup get() = message.source as OnlineMessageSource.Incoming.FromGroup
|
||||
|
||||
public override fun toString(): String =
|
||||
"OtherClientGroupMessageSyncEvent(group=${group.id}, message=$message)"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 机器人收到的群临时会话消息的事件
|
||||
*
|
||||
|
@ -69,6 +69,8 @@ internal class GroupImpl(
|
||||
) : Group, AbstractContact(bot, coroutineContext) {
|
||||
companion object
|
||||
|
||||
val groupPkgMsgParsingCache = GroupPkgMsgParsingCache()
|
||||
|
||||
val uin: Long = groupInfo.uin
|
||||
|
||||
override lateinit var owner: NormalMember
|
||||
@ -460,6 +462,4 @@ internal class GroupImpl(
|
||||
|
||||
|
||||
override fun toString(): String = "Group($id)"
|
||||
|
||||
val groupPkgMsgParsingCache = GroupPkgMsgParsingCache()
|
||||
}
|
||||
|
@ -21,10 +21,7 @@ import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend
|
||||
import net.mamoe.mirai.message.*
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.QuoteReply
|
||||
import net.mamoe.mirai.message.data.asMessageChain
|
||||
import net.mamoe.mirai.message.data.firstIsInstanceOrNull
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.verbose
|
||||
import kotlin.contracts.InvocationKind
|
||||
@ -87,20 +84,21 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
|
||||
}
|
||||
|
||||
internal fun Contact.logMessageSent(message: Message) {
|
||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||
if (message !is net.mamoe.mirai.message.data.LongMessage) {
|
||||
if (message !is LongMessage) {
|
||||
bot.logger.verbose("$this <- $message".replaceMagicCodes())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("RemoveRedundantQualifierName") // compiler bug
|
||||
internal fun net.mamoe.mirai.event.events.MessageEvent.logMessageReceived() {
|
||||
fun renderGroupMessage(group: Group, senderName: String, sender: Member, message: MessageChain): String {
|
||||
val displayId = if (sender is AnonymousMember) "匿名" else sender.id.toString()
|
||||
return "[${group.name}(${group.id})] ${senderName}($displayId) -> $message".replaceMagicCodes()
|
||||
}
|
||||
|
||||
when (this) {
|
||||
is net.mamoe.mirai.event.events.GroupMessageEvent -> bot.logger.verbose {
|
||||
"[${group.name}(${group.id})] ${senderName}(${
|
||||
if (sender is AnonymousMember) "匿名"
|
||||
else sender.id
|
||||
}) -> $message".replaceMagicCodes()
|
||||
renderGroupMessage(group, senderName, sender, message)
|
||||
}
|
||||
is net.mamoe.mirai.event.events.TempMessageEvent -> bot.logger.verbose {
|
||||
"[${group.name}(${group.id})] $senderName(Temp ${sender.id}) -> $message".replaceMagicCodes()
|
||||
@ -109,8 +107,12 @@ internal fun net.mamoe.mirai.event.events.MessageEvent.logMessageReceived() {
|
||||
"${sender.nick}(${sender.id}) -> $message".replaceMagicCodes()
|
||||
}
|
||||
is net.mamoe.mirai.event.events.OtherClientMessageEvent -> bot.logger.verbose {
|
||||
"${client.kind}(${sender.id}) -> $message".replaceMagicCodes()
|
||||
"${client.kind} -> $message".replaceMagicCodes()
|
||||
}
|
||||
is GroupMessageSyncEvent -> bot.logger.verbose {
|
||||
renderGroupMessage(group, senderName, sender, message)
|
||||
}
|
||||
else -> bot.logger.verbose(toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,6 +246,12 @@ internal open class QQAndroidClient(
|
||||
)
|
||||
|
||||
val onlinePushReqPushCacheList = SyncingCacheList<OnlinePushReqPushSyncId>(50)
|
||||
|
||||
internal data class PendingGroupMessageReceiptSyncId(
|
||||
val messageRandom: Int,
|
||||
)
|
||||
|
||||
val pendingGroupMessageReceiptCacheList = SyncingCacheList<PendingGroupMessageReceiptSyncId>(50)
|
||||
}
|
||||
|
||||
val syncingController = MessageSvcSyncData()
|
||||
|
@ -20,4 +20,19 @@ internal class SyncingCacheList<E>(private val size: Int = 50) {
|
||||
if (packetIdList.size >= size) packetIdList.removeFirst()
|
||||
return true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeFirst(condition: (E) -> Boolean): Boolean {
|
||||
val itr = packetIdList.listIterator()
|
||||
for (element in itr) {
|
||||
if (element.let(condition)) {
|
||||
itr.remove()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun contains(condition: (E) -> Boolean): Boolean = packetIdList.any(condition)
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2019-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("SpellCheckingInspection")
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.internal.utils.io.JceStruct
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
||||
|
||||
@Serializable
|
||||
internal class SvcRequestPushReadedNotify(
|
||||
@JvmField @TarsId(0) val notifyType: Byte,
|
||||
@JvmField @TarsId(1) val vC2CReadedNotify: List<C2CMsgReadedNotify>? = null,
|
||||
@JvmField @TarsId(2) val vGroupReadedNotify: List<GroupMsgReadedNotify>? = null,
|
||||
@JvmField @TarsId(3) val vDisReadedNotify: List<DisMsgReadedNotify>? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class C2CMsgReadedNotify(
|
||||
@JvmField @TarsId(0) val peerUin: Long? = null,
|
||||
@JvmField @TarsId(1) val lastReadTime: Long? = null,
|
||||
@JvmField @TarsId(2) val flag: Long? = null,
|
||||
@JvmField @TarsId(3) val phoneNum: String? = "",
|
||||
@JvmField @TarsId(4) val bindedUin: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class DisMsgReadedNotify(
|
||||
@JvmField @TarsId(0) val disUin: Long? = null,
|
||||
@JvmField @TarsId(1) val opType: Long? = null,
|
||||
@JvmField @TarsId(2) val memberSeq: Long? = null,
|
||||
@JvmField @TarsId(3) val disMsgSeq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class GPicInfo(
|
||||
@JvmField @TarsId(0) val vPath: ByteArray,
|
||||
@JvmField @TarsId(1) val vHost: ByteArray? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
internal class GroupMsgHead(
|
||||
@JvmField @TarsId(0) val usCmdType: Int,
|
||||
@JvmField @TarsId(1) val totalPkg: Byte,
|
||||
@JvmField @TarsId(2) val curPkg: Byte,
|
||||
@JvmField @TarsId(3) val usPkgSeq: Int,
|
||||
@JvmField @TarsId(4) val dwReserved: Long
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class GroupMsgReadedNotify(
|
||||
@JvmField @TarsId(0) val groupCode: Long? = null,
|
||||
@JvmField @TarsId(1) val opType: Long? = null,
|
||||
@JvmField @TarsId(2) val memberSeq: Long? = null,
|
||||
@JvmField @TarsId(3) val groupMsgSeq: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class RequestPushGroupMsg(
|
||||
@JvmField @TarsId(0) val uin: Long,
|
||||
@JvmField @TarsId(1) val type: Byte,
|
||||
@JvmField @TarsId(2) val service: String = "",
|
||||
@JvmField @TarsId(3) val cmd: String = "",
|
||||
@JvmField @TarsId(4) val groupCode: Long,
|
||||
@JvmField @TarsId(5) val groupType: Byte,
|
||||
@JvmField @TarsId(6) val sendUin: Long,
|
||||
@JvmField @TarsId(7) val lsMsgSeq: Long,
|
||||
@JvmField @TarsId(8) val uMsgTime: Int,
|
||||
@JvmField @TarsId(9) val infoSeq: Long,
|
||||
@JvmField @TarsId(10) val shMsgLen: Short,
|
||||
@JvmField @TarsId(11) val vMsg: ByteArray,
|
||||
@JvmField @TarsId(12) val groupCard: String? = "",
|
||||
@JvmField @TarsId(13) val uAppShareID: Long? = null,
|
||||
@JvmField @TarsId(14) val vGPicInfo: List<GPicInfo>? = null,
|
||||
@JvmField @TarsId(15) val vAppShareCookie: ByteArray? = null,
|
||||
@JvmField @TarsId(16) val stShareData: shareData? = null,
|
||||
@JvmField @TarsId(17) val fromInstId: Long? = null,
|
||||
@JvmField @TarsId(18) val stGroupMsgHead: GroupMsgHead? = null,
|
||||
@JvmField @TarsId(19) val wUserActive: Int? = null,
|
||||
@JvmField @TarsId(20) val vMarketFace: List<MarketFaceInfo>? = null,
|
||||
@JvmField @TarsId(21) val uSuperQQBubbleId: Long? = null
|
||||
) : JceStruct
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2019-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
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.data.jce
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.internal.utils.io.JceStruct
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
||||
|
||||
@Suppress("ClassName", "SpellCheckingInspection")
|
||||
@Serializable
|
||||
internal class shareData(
|
||||
@JvmField @TarsId(0) val pkgname: String = "",
|
||||
@JvmField @TarsId(1) val msgtail: String = "",
|
||||
@JvmField @TarsId(2) val picurl: String = "",
|
||||
@JvmField @TarsId(3) val url: String = ""
|
||||
) : JceStruct
|
@ -160,6 +160,7 @@ internal object KnownPacketFactories {
|
||||
OnlinePushReqPush,
|
||||
OnlinePushPbPushTransMsg,
|
||||
MessageSvcPushNotify,
|
||||
MessageSvcPushReaded,
|
||||
ConfigPushSvc.PushReq,
|
||||
StatSvc.ReqMSFOffline,
|
||||
StatSvc.SvcReqMSFLoginNotify
|
||||
|
@ -23,6 +23,7 @@ import net.mamoe.mirai.internal.message.MessageSourceToTempImpl
|
||||
import net.mamoe.mirai.internal.message.toRichTextElems
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient.MessageSvcSyncData.PendingGroupMessageReceiptSyncId
|
||||
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.MsgCtrl
|
||||
@ -374,16 +375,24 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
|
||||
contract {
|
||||
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
val messageRandom = Random.nextInt().absoluteValue
|
||||
val source = MessageSourceToGroupImpl(
|
||||
group,
|
||||
internalIds = intArrayOf(Random.nextInt().absoluteValue),
|
||||
internalIds = intArrayOf(messageRandom),
|
||||
sender = client.bot,
|
||||
target = group,
|
||||
time = currentTimeSeconds().toInt(),
|
||||
originalMessage = message//,
|
||||
// sourceMessage = message
|
||||
)
|
||||
|
||||
sourceCallback(source)
|
||||
|
||||
client.syncingController.pendingGroupMessageReceiptCacheList.addCache(
|
||||
PendingGroupMessageReceiptSyncId(
|
||||
messageRandom = messageRandom,
|
||||
)
|
||||
)
|
||||
return createToGroupImpl(
|
||||
client,
|
||||
group,
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019-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
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
|
||||
|
||||
internal object MessageSvcPushReaded : IncomingPacketFactory<Packet?>(
|
||||
"MessageSvc.PushReaded", ""
|
||||
) {
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
|
||||
// val notify = readUniPacket(SvcRequestPushReadedNotify.serializer())
|
||||
|
||||
// just ignore.
|
||||
return null
|
||||
}
|
||||
}
|
@ -12,12 +12,14 @@
|
||||
package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.contact.nameCardOrNick
|
||||
import net.mamoe.mirai.event.AbstractEvent
|
||||
import net.mamoe.mirai.event.Event
|
||||
import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.GroupMessageEvent
|
||||
import net.mamoe.mirai.event.events.GroupMessageSyncEvent
|
||||
import net.mamoe.mirai.event.events.MemberCardChangeEvent
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.GroupImpl
|
||||
@ -25,6 +27,7 @@ import net.mamoe.mirai.internal.contact.MemberImpl
|
||||
import net.mamoe.mirai.internal.message.toMessageChain
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
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.MsgOnlinePush
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x8fc
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
|
||||
@ -53,18 +56,30 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
|
||||
// 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00
|
||||
if (!bot.firstLoginSucceed) return null
|
||||
val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer())
|
||||
// bot.logger.debug(pbPushMsg._miraiContentToString())
|
||||
|
||||
if (pbPushMsg.msg.msgHead.fromUin == bot.id) {
|
||||
return SendGroupMessageReceipt(
|
||||
pbPushMsg.msg.msgBody.richText.attr!!.random,
|
||||
pbPushMsg.msg.msgHead.msgSeq
|
||||
)
|
||||
val msgHead = pbPushMsg.msg.msgHead
|
||||
|
||||
val isFromSelfAccount = msgHead.fromUin == bot.id
|
||||
if (isFromSelfAccount) {
|
||||
val messageRandom = pbPushMsg.msg.msgBody.richText.attr?.random ?: return null
|
||||
|
||||
if (bot.client.syncingController.pendingGroupMessageReceiptCacheList.contains { it.messageRandom == messageRandom }) {
|
||||
// message sent by bot
|
||||
return SendGroupMessageReceipt(
|
||||
messageRandom,
|
||||
msgHead.msgSeq
|
||||
)
|
||||
}
|
||||
// else: sync form other device
|
||||
}
|
||||
val group =
|
||||
bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) as GroupImpl? ?: return null // 机器人还正在进群
|
||||
val msgs = group.groupPkgMsgParsingCache.put(pbPushMsg)
|
||||
if (msgs.isEmpty()) return null
|
||||
|
||||
if (msgHead.groupInfo == null) return null
|
||||
|
||||
val group = bot.getGroup(msgHead.groupInfo.groupCode) as GroupImpl? ?: return null // 机器人还正在进群
|
||||
|
||||
|
||||
// fragmented message
|
||||
val msgs = group.groupPkgMsgParsingCache.tryMerge(pbPushMsg).ifEmpty { return null }
|
||||
|
||||
var extraInfo: ImMsgBody.ExtraInfo? = null
|
||||
var anonymous: ImMsgBody.AnonymousGroupMsg? = null
|
||||
@ -78,50 +93,75 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin
|
||||
}
|
||||
}
|
||||
|
||||
val sender = if (anonymous != null) {
|
||||
group.newAnonymous(anonymous.anonNick.encodeToString(), anonymous.anonId.encodeToBase64())
|
||||
} else {
|
||||
group[pbPushMsg.msg.msgHead.fromUin] as MemberImpl
|
||||
|
||||
val sender: Member // null if sync from other client
|
||||
val name: String
|
||||
|
||||
if (anonymous != null) { // anonymous member
|
||||
sender = group.newAnonymous(anonymous.anonNick.encodeToString(), anonymous.anonId.encodeToBase64())
|
||||
name = sender.nameCard
|
||||
} else { // normal member chat
|
||||
sender = group[msgHead.fromUin] as MemberImpl
|
||||
name = findSenderName(extraInfo, msgHead.groupInfo) ?: sender.nameCardOrNick
|
||||
}
|
||||
|
||||
val name = if (anonymous != null) {
|
||||
sender.nameCard
|
||||
if (isFromSelfAccount) {
|
||||
return GroupMessageSyncEvent(
|
||||
message = msgs.toMessageChain(bot, groupIdOrZero = group.id, onlineSource = true),
|
||||
time = msgHead.msgTime,
|
||||
group = group,
|
||||
sender = sender,
|
||||
senderName = name,
|
||||
)
|
||||
} else {
|
||||
extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.run {
|
||||
kotlin.runCatching {
|
||||
if (this[0] == 0x0A.toByte()) {
|
||||
val nameBuf = loadAs(Oidb0x8fc.CommCardNameBuf.serializer())
|
||||
if (nameBuf.richCardName.isNotEmpty()) {
|
||||
return@runCatching nameBuf.richCardName.joinToString("") { it.text.encodeToString() }
|
||||
}
|
||||
}
|
||||
return@runCatching null
|
||||
}.getOrNull() ?: encodeToString()
|
||||
} ?: pbPushMsg.msg.msgHead.groupInfo.groupCard.takeIf { it.isNotEmpty() }
|
||||
?: sender.nameCardOrNick // 没有 extraInfo 就从 head 里取
|
||||
}
|
||||
|
||||
val flags = extraInfo?.flags ?: 0
|
||||
return GroupMessageEvent(
|
||||
senderName = name.also {
|
||||
if (sender is MemberImpl && it != sender.nameCard) {
|
||||
val origin = sender._nameCard
|
||||
sender._nameCard = name
|
||||
MemberCardChangeEvent(origin, name, sender).broadcast()
|
||||
}
|
||||
},
|
||||
sender = sender,
|
||||
message = msgs.toMessageChain(bot, groupIdOrZero = group.id, onlineSource = true),
|
||||
permission = when {
|
||||
flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
|
||||
flags and 8 != 0 -> MemberPermission.OWNER
|
||||
flags == 0 || flags == 1 -> MemberPermission.MEMBER
|
||||
else -> {
|
||||
bot.logger.warning { "判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${flags._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题." }
|
||||
sender.permission
|
||||
}
|
||||
},
|
||||
time = pbPushMsg.msg.msgHead.msgTime
|
||||
)
|
||||
broadcastNameCardChangedEventIfNecessary(sender, name)
|
||||
|
||||
return GroupMessageEvent(
|
||||
senderName = name,
|
||||
sender = sender,
|
||||
message = msgs.toMessageChain(bot, groupIdOrZero = group.id, onlineSource = true),
|
||||
permission = findMemberPermission(extraInfo?.flags ?: 0, sender, bot),
|
||||
time = msgHead.msgTime
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun broadcastNameCardChangedEventIfNecessary(sender: Member, name: String) {
|
||||
val currentNameCard = sender.nameCard
|
||||
if (sender is MemberImpl && name != currentNameCard) {
|
||||
sender._nameCard = name
|
||||
MemberCardChangeEvent(currentNameCard, name, sender).broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findMemberPermission(
|
||||
flags: Int,
|
||||
sender: Member,
|
||||
bot: QQAndroidBot,
|
||||
) = when {
|
||||
flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
|
||||
flags and 8 != 0 -> MemberPermission.OWNER
|
||||
flags == 0 || flags == 1 -> MemberPermission.MEMBER
|
||||
else -> {
|
||||
bot.logger.warning { "判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${flags._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题." }
|
||||
sender.permission
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSenderName(
|
||||
extraInfo: ImMsgBody.ExtraInfo?,
|
||||
groupInfo: MsgComm.GroupInfo
|
||||
) = extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.decodeCommCardNameBuf()
|
||||
?: groupInfo.groupCard.takeIf { it.isNotEmpty() }
|
||||
|
||||
private fun ByteArray.decodeCommCardNameBuf() = kotlin.runCatching {
|
||||
if (this[0] == 0x0A.toByte()) {
|
||||
val nameBuf = loadAs(Oidb0x8fc.CommCardNameBuf.serializer())
|
||||
if (nameBuf.richCardName.isNotEmpty()) {
|
||||
return@runCatching nameBuf.richCardName.joinToString("") { it.text.encodeToString() }
|
||||
}
|
||||
}
|
||||
return@runCatching null
|
||||
}.getOrNull() ?: encodeToString()
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
/**
|
||||
* fragmented message
|
||||
*/
|
||||
internal class GroupPkgMsgParsingCache {
|
||||
class PkgMsg(
|
||||
val size: Int,
|
||||
@ -31,7 +34,7 @@ internal class GroupPkgMsgParsingCache {
|
||||
}
|
||||
}
|
||||
|
||||
fun put(msg: MsgOnlinePush.PbPushMsg): List<MsgOnlinePush.PbPushMsg> {
|
||||
fun tryMerge(msg: MsgOnlinePush.PbPushMsg): List<MsgOnlinePush.PbPushMsg> {
|
||||
val head = msg.msg.contentHead ?: return listOf(msg)
|
||||
val size = head.pkgNum
|
||||
if (size < 2) return listOf(msg)
|
||||
|
Loading…
Reference in New Issue
Block a user