diff --git a/README.md b/README.md index a1fb1f95f..c52de0bd5 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,11 @@ Mirai 在 JVM 平台采用插件模式运行,同时提供独立的跨平台核 - 接受解析好友消息(10/14) - 接收解析群消息(10/14) - 成员昵称(10/18) - - 成员权限(10/18, 计划优化) + - 成员权限(11/2) - 好友在线状态改变(10/14) - Android客户端上线/下线(10/18) - 上传并发送好友/群图片(10/21, 10/26) +- 群员权限改变(11/2) 计划中: 添加好友 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt index f93233726..a5ed04f55 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt @@ -18,7 +18,7 @@ import kotlin.jvm.JvmName /** * 消息事件时创建的临时容器. */ -abstract class SenderAndMessage( +abstract class SenderAndMessage( /** * 发送这条消息的用户. */ @@ -26,7 +26,7 @@ abstract class SenderAndMessage( /** * 消息事件主体. 对于好友消息, 这个属性为 [QQ] 的实例; 对于群消息, 这个属性为 [Group] 的实例 */ - val subject: S, + val subject: TContact, val message: MessageChain ) { /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt index fe6d4908c..e9b4f1600 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt @@ -59,9 +59,18 @@ fun main() { internal fun IoBuffer.parseMessageImage0x03(): Image { discardExact(1) - return Image(ImageId(String(readLVByteArray()))) + + return Image(ImageId(String(readLVByteArray()).adjustImageId())) } +private operator fun String.get(range: IntRange) = this.substring(range) + +// 有些时候会得到 724D95122B54EEAC1E214AAAC37259DF.gif +// 需要调整 {724D9512-2B54-EEAC-1E21-4AAAC37259DF}.gif + +private fun String.adjustImageId() = + "{${this[0..7]}-${this[8..11]}-${this[12..15]}-${this[16..19]}-${this[20..31]}}.${this.substringAfterLast(".")}" + internal fun ByteReadPacket.readMessage(): Message? { val messageType = this.readByte().toInt() val sectionLength = this.readShort() @@ -166,7 +175,6 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket { */ writeShortLVPacket { - //todo writeUByte(0x02u) writeShortLVString(id.value) writeHex("04 00 04 87 E5 09 3B 05 00 04 D2 C4 C0 B7 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 01 ED 16 00 04 00 00 02 17 18 00 04 00 00 EB 34 FF 00 5C 15 36 20 39 32 6B 41 31 43 38 37 65 35 30 39 33 62 64 32 63 34 63 30 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20") @@ -252,7 +260,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket { } } - else -> throw UnsupportedOperationException("${this::class.simpleName} is not supported(Full MessageChain=${this@toPacket})") + else -> throw UnsupportedOperationException("${this::class.simpleName} is not supported. Do NOT implement Message manually. Full MessageChain=${this@toPacket}") } }) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt index 741d48b5b..5fd96c1e9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt @@ -15,10 +15,7 @@ import net.mamoe.mirai.event.subscribe import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.protocol.tim.handler.* -import net.mamoe.mirai.network.protocol.tim.packet.HeartbeatPacket -import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.UnknownServerPacket +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket import net.mamoe.mirai.network.protocol.tim.packet.login.* import net.mamoe.mirai.network.session @@ -213,9 +210,11 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : } packet.use { - packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let { - bot.logger.verbose("Packet received: $packet") - } + packet::class.takeUnless { ResponsePacket::class.isInstance(packet) } + ?.simpleName?.takeUnless { it.endsWith("Encrypted") || it.endsWith("Raw") } + ?.let { + bot.logger.verbose("Packet received: $packet") + } // Remove first to release the lock handlersLock.withLock { @@ -264,7 +263,13 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : } } - bot.logger.verbose("Packet sent: $packet") + packet.takeUnless { _ -> + packet.packetId is KnownPacketId && packet.packetId.builder?.let { + it::class.annotations.filterIsInstance().any() + } == true + }?.let { + bot.logger.verbose("Packet sent: $it") + } PacketSentEvent(bot, packet).broadcast() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt index 76305fe9b..96e089dd6 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt @@ -11,13 +11,13 @@ import net.mamoe.mirai.getQQ import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.network.BotSession import net.mamoe.mirai.network.distributePacket -import net.mamoe.mirai.network.protocol.tim.packet.ServerFriendOnlineStatusChangedPacket +import net.mamoe.mirai.network.protocol.tim.packet.FriendOnlineStatusChangedPacket import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket +import net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessageEventPacket +import net.mamoe.mirai.network.protocol.tim.packet.event.GroupMessageEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.IgnoredServerEventPacket -import net.mamoe.mirai.network.protocol.tim.packet.event.ServerFriendMessageEventPacket -import net.mamoe.mirai.network.protocol.tim.packet.event.ServerGroupMessageEventPacket import net.mamoe.mirai.network.protocol.tim.packet.event.ServerGroupUploadFileEventPacket import net.mamoe.mirai.network.qqAccount @@ -36,13 +36,13 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) { //todo } - is ServerFriendMessageEventPacket -> { + is FriendMessageEventPacket -> { if (!packet.isPrevious) { FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast() } } - is ServerGroupMessageEventPacket -> { + is GroupMessageEventPacket -> { if (packet.qq == bot.account.id) return GroupMessageEvent( @@ -55,8 +55,8 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) { ).broadcast() } - is ServerFriendOnlineStatusChangedPacket.Encrypted -> distributePacket(packet.decrypt(sessionKey)) - is ServerFriendOnlineStatusChangedPacket -> { + is FriendOnlineStatusChangedPacket.Encrypted -> distributePacket(packet.decrypt(sessionKey)) + is FriendOnlineStatusChangedPacket -> { //TODO } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt index d4a867219..bd2defbd7 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/FriendOnlineStatusChanged.kt @@ -15,7 +15,7 @@ import kotlin.properties.Delegates * TODO 真的是在线状态改变么 */ @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE) -class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) { +class FriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) { var qq: UInt by Delegates.notNull() lateinit var status: OnlineStatus @@ -31,6 +31,7 @@ class ServerFriendOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacke @AnnotatedId(KnownPacketId.SEND_FRIEND_MESSAGE) class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): ServerFriendOnlineStatusChangedPacket = ServerFriendOnlineStatusChangedPacket(this.decryptBy(sessionKey)).applySequence(sequenceId) + fun decrypt(sessionKey: ByteArray): FriendOnlineStatusChangedPacket = + FriendOnlineStatusChangedPacket(this.decryptBy(sessionKey)).applySequence(sequenceId) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt index a5f32252f..0bb4f4797 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt @@ -8,6 +8,7 @@ import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.writeHex import net.mamoe.mirai.utils.io.writeQQ +@NoLog @AnnotatedId(KnownPacketId.HEARTBEAT) object HeartbeatPacket : OutgoingPacketBuilder { operator fun invoke( @@ -21,6 +22,7 @@ object HeartbeatPacket : OutgoingPacketBuilder { } } + @NoLog @AnnotatedId(KnownPacketId.HEARTBEAT) class Response(input: ByteReadPacket) : ResponsePacket(input) } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt index e11898dbb..cf5943810 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt @@ -5,6 +5,9 @@ package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.IoBuffer import net.mamoe.mirai.network.protocol.tim.packet.NullPacketId.value +import net.mamoe.mirai.network.protocol.tim.packet.action.* +import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket +import net.mamoe.mirai.network.protocol.tim.packet.login.* import net.mamoe.mirai.utils.io.toUHexString import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -69,34 +72,38 @@ object NullPacketId : PacketId { /** * 未知的 [PacketId] */ -inline class UnknownPacketId(override val value: UShort) : PacketId +inline class UnknownPacketId(override inline val value: UShort) : PacketId /** * 已知的 [PacketId]. 所有在 Mirai 中实现过的包都会使用这些 Id */ -enum class KnownPacketId(override val value: UShort) : PacketId { - inline TOUCH(0x0825u), - inline SESSION_KEY(0x0828u), - inline LOGIN(0X0836u), - inline CAPTCHA(0X00BAU), - inline SERVER_EVENT_1(0X00CEU), - inline SERVER_EVENT_2(0X0017U), - inline FRIEND_ONLINE_STATUS_CHANGE(0X0081U), - inline CHANGE_ONLINE_STATUS(0x00_ECu), +enum class KnownPacketId(override inline val value: UShort, internal inline val builder: OutgoingPacketBuilder?) : + PacketId { + inline TOUCH(0x0825u, TouchPacket), + inline SESSION_KEY(0x0828u, RequestSessionPacket), + inline LOGIN(0x0836u, SubmitPasswordPacket), + inline CAPTCHA(0x00BAu, SubmitCaptchaPacket), + inline SERVER_EVENT_1(0x00CEu, ServerEventPacket.EventResponse), + inline SERVER_EVENT_2(0x0017u, ServerEventPacket.EventResponse), + inline FRIEND_ONLINE_STATUS_CHANGE(0x0081u, null), + inline CHANGE_ONLINE_STATUS(0x00_ECu, null), - inline HEARTBEAT(0x0058u), - inline S_KEY(0X001DU), - inline ACCOUNT_INFO(0X005CU), - inline SEND_GROUP_MESSAGE(0X0002U), - inline SEND_FRIEND_MESSAGE(0X00CDU), - inline CAN_ADD_FRIEND(0X00A7U), - inline GROUP_IMAGE_ID(0X0388U), - inline FRIEND_IMAGE_ID(0X0352U), + inline HEARTBEAT(0x0058u, HeartbeatPacket), + inline S_KEY(0x001Du, RequestSKeyPacket), + inline ACCOUNT_INFO(0x005Cu, RequestAccountInfoPacket), + inline SEND_GROUP_MESSAGE(0x0002u, SendGroupMessagePacket), + inline SEND_FRIEND_MESSAGE(0x00CDu, SendFriendMessagePacket), + inline CAN_ADD_FRIEND(0x00A7u, CanAddFriendPacket), + inline GROUP_IMAGE_ID(0x0388u, GroupImageIdRequestPacket), + inline FRIEND_IMAGE_ID(0x0352u, FriendImageIdRequestPacket), - inline REQUEST_PROFILE(0x00_31u), - inline SUBMIT_IMAGE_FILE_NAME(0X01_BDu), + inline REQUEST_PROFILE(0x00_31u, RequestProfilePicturePacket), + @Suppress("DEPRECATION") + inline SUBMIT_IMAGE_FILE_NAME(0x01_BDu, SubmitImageFilenamePacket), ; + + override fun toString(): String = builder?.let { it::class.simpleName } ?: this.name } // endregion @@ -109,7 +116,7 @@ enum class KnownPacketId(override val value: UShort) : PacketId { @Suppress("unused") @MustBeDocumented @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) internal annotation class PacketVersion(val date: String, val timVersion: String) private object PacketNameFormatter { @@ -124,6 +131,7 @@ private object PacketNameFormatter { private object IgnoreIdListEquals : List by listOf( "idHex", "id", + "eventIdentity", "packetId", "sequenceIdInternal", "sequenceId", @@ -152,6 +160,13 @@ private object IgnoreIdListInclude : List by listOf( "RefVolatile" ) +/** + * 带有这个注解的 [Packet], 将不会被记录在 log 中. + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class NoLog + /** * 这个方法会翻倍内存占用, 考虑修改. */ @@ -187,7 +202,7 @@ private fun KProperty<*>.briefDescription(thisRef: Packet): String = thisRef, this ) ?: "[UnknownProperty]" - else -> value.toString() + else -> value.toString().replace("\n", """\n""") } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt index 0bd6f6b40..5af7b953a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt @@ -255,7 +255,7 @@ object FriendImageIdRequestPacket : OutgoingPacketBuilder { } @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID) - @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") + @PacketVersion(date = "2019.11.1", timVersion = "2.3.2.21173") class Response(input: ByteReadPacket) : ResponsePacket(input) { /** * 访问 HTTP API 时需要使用的一个 key. 128 位 @@ -289,7 +289,9 @@ object FriendImageIdRequestPacket : OutgoingPacketBuilder { if (readUByte() != UByte.MIN_VALUE) { discardExact(60) - discardExact(1)//4A, id + @Suppress("ControlFlowWithEmptyBody") + while (readUByte().toUInt() != 0x4Au); + uKey = readBytes(readUnsignedVarInt().toInt())//128 discardExact(1)//52, id diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupEventPackets.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupEventPackets.kt index 7394aa4bc..ea225eb72 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupEventPackets.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/GroupEventPackets.kt @@ -1,16 +1,19 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet.event import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact +import kotlinx.io.core.readUInt +import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.utils.io.readString /** * 群文件上传 */ -class ServerGroupUploadFileEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { +class ServerGroupUploadFileEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) { private lateinit var xmlMessage: String override fun decode() { @@ -21,7 +24,58 @@ class ServerGroupUploadFileEventPacket(input: ByteReadPacket, eventIdentity: Eve }//todo test } -class ServerGroupUnknownChangedEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { +class GroupMemberNickChangedEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) { + private val groupId: UInt get() = eventIdentity.from + private val group: UInt get() = eventIdentity.from + + override fun decode() { + // GroupId VarInt + // 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 00 00 F3 66 00 00 00 05 00 00 00 EE 00 00 00 05 + // ? 数据中没有哪个人的昵称改变了 + } +} + +@PacketVersion(date = "2019.11.2", timVersion = "2.3.2.21173") +class GroupMemberPermissionChangedPacket internal constructor( + input: ByteReadPacket, + eventIdentity: EventPacketIdentity +) : + ServerEventPacket(input, eventIdentity) { + val groupId: UInt get() = eventIdentity.from + var qq: UInt = 0u + lateinit var kind: Kind + + enum class Kind { + /** + * 变成管理员 + */ + BECOME_OPERATOR, + /** + * 不再是管理员 + */ + NO_LONGER_OPERATOR, + } // TODO: 2019/11/2 变成群主的情况 + + override fun decode(): Unit = with(input) { + // 群里一个人变成管理员: + // 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 76 E4 B8 DD 01 + // 取消管理员 + // 00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 00 76 E4 B8 DD 00 + discardExact(remaining - 5) + qq = readUInt() + kind = when (readByte().toInt()) { + 0x00 -> Kind.NO_LONGER_OPERATOR + 0x01 -> Kind.BECOME_OPERATOR + else -> { + error("Could not determine permission change kind") + } + } + } +} + +class ServerGroupUnknownChangedEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) { override fun decode() = with(input) { //00 00 00 08 00 0A 00 04 01 00 00 00 22 96 29 7B 01 01 00 00 F3 55 00 00 00 05 00 00 00 E9 00 00 00 05 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEventPackets.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEventPackets.kt index 6359afb7c..bce9f87b3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEventPackets.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/MessageEventPackets.kt @@ -8,7 +8,12 @@ import kotlinx.io.core.readUInt import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.NullMessageChain import net.mamoe.mirai.message.internal.readMessageChain -import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.io.printTLVMap +import net.mamoe.mirai.utils.io.read +import net.mamoe.mirai.utils.io.readLVByteArray +import net.mamoe.mirai.utils.io.readTLVMap import kotlin.properties.Delegates @@ -19,7 +24,9 @@ enum class SenderPermission { } @Suppress("EXPERIMENTAL_API_USAGE") -class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { +@PacketVersion(date = "2019.11.2", timVersion = "2.3.2.21173") +class GroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) { var groupNumber: UInt by Delegates.notNull() var qq: UInt by Delegates.notNull() lateinit var senderName: String @@ -41,56 +48,50 @@ class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventP message = readMessageChain() val map = readTLVMap(true) - //map.printTLVMap("父map") if (map.containsKey(18)) { map.getValue(18).read { val tlv = readTLVMap(true) - //tlv.printTLVMap("子map") + //tlv.printTLVMap("消息结尾 tag=18 的 TLV") ////群主的18: 05 00 04 00 00 00 03 08 00 04 00 00 00 04 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08 //群主的 子map= {5=00 00 00 03, 8=00 00 00 04, 1=48 69 6D 31 38 38 6D 6F 65, 3=04, 4=00 00 00 08} //管理员 子map= {5=00 00 00 03, 8=00 00 00 04, 2=65 6F 6D 38 38 31 6D 69 48, 3=02, 4=00 00 00 10} //群成员 子map= {5=00 00 00 03, 8=00 00 00 04, 2=65 6F 6D 38 38 31 6D 69 48, 3=02} - tlv.printTLVMap("Child TLV map") - senderPermission = when (val value0x03 = tlv.getValue(0x03)[0].toUInt()) { - 0x04u -> SenderPermission.OWNER - 0x03u -> SenderPermission.MEMBER - 0x02u -> { - if (!tlv.containsKey(0x04)) { - SenderPermission.MEMBER - } else when (val value0x04 = tlv.getValue(0x04)[3].toUInt()) { - 0x08u -> SenderPermission.OPERATOR - 0x10u -> SenderPermission.MEMBER - else -> error("Could not determine member permission, unknown tlv(key=0x04,value=$value0x04)") - } - } - 0x01u -> SenderPermission.MEMBER + // 4=08, 群主 + // 没有4, 群员 + // 4=10, 管理员 + senderPermission = when (tlv.takeIf { it.containsKey(0x04) }?.get(0x04)?.getOrNull(3)?.toUInt()) { + null -> SenderPermission.MEMBER + 0x08u -> SenderPermission.OWNER + 0x10u -> SenderPermission.OPERATOR else -> { - error("Could not determine member permission, unknown TLV(key=0x03,value=$value0x03;)") - //{5=00 00 00 01, 8=00 00 00 01, 1=48 69 6D 31 38 38 6D 6F 65, 3=03} + tlv.printTLVMap("TLV(tag=18) Map") + MiraiLogger.warning("Could not determine member permission, default permission MEMBER is being used") + SenderPermission.MEMBER } } senderName = when { tlv.containsKey(0x01) -> kotlinx.io.core.String(tlv.getValue(0x01))//这个人的qq昵称 tlv.containsKey(0x02) -> kotlinx.io.core.String(tlv.getValue(0x02))//这个人的群名片 - else -> "null" + else -> { + tlv.printTLVMap("TLV(tag=18) Map") + MiraiLogger.warning("Could not determine senderName") + "null" + } } } } } } -fun main() { - "00 00 00 08 00 0A 00 04 01 00 00 00 35 DB 60 A2 01 8D 62 3A B8 02 76 E4 B8 DD 06 B4 B4 BD A8 D5 DF 00 30 34 46 30 41 39 37 31 45 42 37 46 31 42 34 43 33 34 41 31 42 34 33 37 41 35 33 45 31 36 43 30 43 38 35 43 36 44 31 34 46 35 44 31 41 31 43 39 34".printStringFromHex() -} - // //以前的消息: 00 00 00 25 00 08 00 02 00 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 01 38 03 3E 03 3F A2 76 E4 B8 DD 58 2C 60 86 35 3A 30 B3 C7 63 4A 80 E7 CD 5B 64 00 0B 78 16 5D A3 0A FD 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A3 0A FD AB 77 16 02 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 04 01 00 01 36 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 //刚刚的消息: 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 01 38 03 3E 03 3F A2 76 E4 B8 DD 11 F4 B2 F2 1A E7 1F C4 F1 3F 23 FB 74 80 42 64 00 0B 78 1A 5D A3 26 C1 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A3 26 C1 AA 34 08 42 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E4 BD A0 E5 A5 BD 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 -class ServerFriendMessageEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { +class FriendMessageEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) { val qq: UInt get() = eventIdentity.from /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt index ef9605210..fa606aec0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt @@ -59,36 +59,24 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event } 0x002Du -> ServerGroupUploadFileEventPacket(input, eventIdentity) - 0x0052u -> ServerGroupMessageEventPacket(input, eventIdentity) + 0x002Cu -> GroupMemberPermissionChangedPacket(input, eventIdentity) - 0x00A6u -> ServerFriendMessageEventPacket(input, eventIdentity) + /* + * + inline GROUP_MEMBER_NICK_CHANGED(0x002Fu, null), + inline GROUP_MEMBER_PERMISSION_CHANGED(0x002Cu, null), - //0210: 00 00 00 0E 00 08 00 02 00 01 00 0A 00 04 01 00 00 00 00 00 00 06 00 00 00 26 08 02 1A 02 08 49 0A 08 08 00 10 B2 DE 8C ED 05 0A 0C 08 A2 FF 8C F0 03 10 E4 A1 A7 ED 05 0A 0C 08 DD F1 92 B7 07 10 B1 DE 8C ED 05 - // 00 00 00 08 00 0A 00 04 01 00 00 00 00 00 00 16 00 00 00 37 08 02 1A 12 08 95 02 10 90 04 40 98 E1 8C ED 05 48 AF 96 C3 A4 03 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 1A 29 08 00 10 05 18 98 E1 8C ED 05 20 01 28 FF FF FF FF 0F 32 15 E5 AF B9 E6 96 B9 E6 AD A3 E5 9C A8 E8 BE 93 E5 85 A5 2E 2E 2E - // 00 00 00 08 00 0A 00 04 01 00 00 00 00 00 00 07 00 00 00 - /*{ - //todo 02 10 可能是发起会话? 在手机 QQ 打开一个公众号也有这个包; 取消关注公众号也会有这个包; 手机打开某人聊天界面也会有 - 0x0210u -> discardExact(19) - println("type事件" + readUByte().toUInt().toByteArray().toUHexString()) + */ - //todo 错了. 可能是 00 79 才是. - ServerFriendTypingCanceledPacket(input, eventIdentity) - /* - if (readUByte().toUInt() == 0x37u) ServerFriendTypingStartedPacket(input, eventIdentity) - else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/ - }*/ - 0x0210u -> IgnoredServerEventPacket( - eventId = type.toByteArray(), - showData = true, - input = input, - eventIdentity = eventIdentity - ) + 0x0052u -> GroupMessageEventPacket(input, eventIdentity) - //"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity) + 0x00A6u -> FriendMessageEventPacket(input, eventIdentity) - else -> {//0x00 79u, 可能是正在输入的包 - MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}") - UnknownServerEventPacket(input, eventIdentity) + // "对方正在输入..." + 0x0210u -> IgnoredServerEventPacket(input, eventIdentity) + + else -> { + UnknownServerEventPacket(type.toByteArray(), true, input, eventIdentity) } }.applyId(packetId).applySequence(sequenceId) } @@ -106,6 +94,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event sessionKey: ByteArray ): OutgoingPacket = EventResponse(this.packetId, this.sequenceId, bot, sessionKey, this.eventIdentity) + @NoLog @Suppress("FunctionName") object EventResponse : OutgoingPacketBuilder { operator fun invoke( @@ -128,27 +117,25 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event * 忽略的事件 */ @Suppress("unused") -class IgnoredServerEventPacket( - val eventId: ByteArray, - private val showData: Boolean = false, - input: ByteReadPacket, - eventIdentity: EventPacketIdentity -) : ServerEventPacket(input, eventIdentity) { - override fun decode() { - if (showData) { - MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString()) - } else { - this.input.discard() - } - } -} +class IgnoredServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : + ServerEventPacket(input, eventIdentity) /** * Unknown event */ -class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : +class UnknownServerEventPacket( + val eventId: ByteArray, + private val showData: Boolean = false, + input: ByteReadPacket, + eventIdentity: EventPacketIdentity +) : ServerEventPacket(input, eventIdentity) { override fun decode() { - MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString()) + MiraiLogger.debug("UnknownEvent type = ${eventId.toUHexString()}") + if (showData) { + MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString()) + } else { + this.input.discard() + } } -} +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 4a7af8d2f..7302205d5 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -7,29 +7,29 @@ import net.mamoe.mirai.utils.MiraiLogger internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug") -internal fun debugPrintln(any: Any?) = DebugLogger.warning(any) +internal fun debugPrintln(any: Any?) = DebugLogger.debug(any) internal fun ByteArray.debugPrint(name: String): ByteArray { - DebugLogger.warning(name + "=" + this.toUHexString()) + DebugLogger.debug(name + "=" + this.toUHexString()) return this } @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this")) internal fun IoBuffer.debugPrint(name: String): IoBuffer { val readBytes = this.readBytes() - DebugLogger.warning(name + "=" + readBytes.toUHexString()) + DebugLogger.debug(name + "=" + readBytes.toUHexString()) return readBytes.toIoBuffer() } @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("discardExact(n)")) internal fun Input.debugDiscardExact(n: Number, name: String = "") { - DebugLogger.warning("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString()) + DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString()) } @Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this")) internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket { val bytes = this.readBytes() - DebugLogger.warning("ByteReadPacket $name=" + bytes.toUHexString()) + DebugLogger.debug("ByteReadPacket $name=" + bytes.toUHexString()) return bytes.toReadPacket() } @@ -65,7 +65,6 @@ internal fun String.printStringFromHex() { println(this.hexToBytes().stringOfWitch()) } - internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstConst: Boolean = false, compareTo: String? = null) { println("Hex比较 `$name`") if (compareTo != null) { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt index c4baee74b..beb6c7cce 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt @@ -66,7 +66,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { CHANGE_ONLINE_STATUS -> ServerLoginSuccessPacket(this) CAPTCHA -> ServerCaptchaPacket.Encrypted(this) SERVER_EVENT_1, SERVER_EVENT_2 -> ServerEventPacket.Raw.Encrypted(this, PacketId(id), sequenceId) - FRIEND_ONLINE_STATUS_CHANGE -> ServerFriendOnlineStatusChangedPacket.Encrypted(this) + FRIEND_ONLINE_STATUS_CHANGE -> FriendOnlineStatusChangedPacket.Encrypted(this) S_KEY -> ResponsePacket.Encrypted(this) ACCOUNT_INFO -> ResponsePacket.Encrypted(this) diff --git a/mirai-debug/src/main/java/HexDebuggerGui.kt b/mirai-debug/src/main/java/HexDebuggerGui.kt index 475f52b56..d6d9489cc 100644 --- a/mirai-debug/src/main/java/HexDebuggerGui.kt +++ b/mirai-debug/src/main/java/HexDebuggerGui.kt @@ -7,6 +7,7 @@ import javafx.scene.layout.Region import javafx.scene.paint.Color import javafx.scene.text.FontWeight import kotlinx.coroutines.* +import kotlinx.io.core.readUInt import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.stringOfWitch @@ -74,7 +75,7 @@ class HexDebuggerGui : View("s") { private lateinit var outSize: TextField private lateinit var outUVarInt: TextField private lateinit var outShort: TextField - private lateinit var outInt: TextField + private lateinit var outUInt: TextField private lateinit var outString: TextField @@ -134,9 +135,9 @@ class HexDebuggerGui : View("s") { } } - outInt.text = runOrNull { + outUInt.text = runOrNull { value.hexToBytes().read { - readInt().toString() + readUInt().toString() } } @@ -192,7 +193,7 @@ class HexDebuggerGui : View("s") { label("size") label("UVarInt") label("short") - label("int") + label("uint") label("string") children.filterIsInstance().forEach { it.fitToParentWidth() @@ -203,7 +204,7 @@ class HexDebuggerGui : View("s") { alignment = Pos.CENTER_RIGHT outSize = textfield { - promptText = "UVarInt" + promptText = "Size" isEditable = false } @@ -213,17 +214,17 @@ class HexDebuggerGui : View("s") { } outShort = textfield { - promptText = "short" + promptText = "Short" isEditable = false } - outInt = textfield { - promptText = "int" + outUInt = textfield { + promptText = "UInt" isEditable = false } outString = textfield { - promptText = "int" + promptText = "String" isEditable = false } diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt index 8f2c5a9c6..eddb7cfa4 100644 --- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt +++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt @@ -11,6 +11,7 @@ import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.subscribeAlways import net.mamoe.mirai.event.subscribeMessages import net.mamoe.mirai.login +import net.mamoe.mirai.message.Image import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess import java.io.File @@ -47,6 +48,10 @@ suspend fun main() { bot.subscribeMessages { "你好" reply "你好!" + has { + reply(message) + } + startsWith("随机图片", removePrefix = true) { withContext(Dispatchers.Default) { try {