diff --git a/gradle.properties b/gradle.properties index 96e105383..65ecdf504 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # style guide kotlin.code.style=official # config -mirai_version=0.1.7 +mirai_version=0.2.0 kotlin.incremental.multiplatform=true kotlin.parallel.tasks.in.project=true # kotlin diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index 6b8568b25..979c47812 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -59,9 +59,13 @@ inline fun Contact.withSession(block: BotSession.() -> R): R { /** * 只读联系人列表 */ -class ContactList internal constructor(private val delegate: MutableContactList) : Map by delegate +class ContactList internal constructor(private val delegate: MutableContactList) : Map by delegate { + override fun toString(): String = delegate.toString() +} /** * 可修改联系人列表. 只会在内部使用. */ -internal class MutableContactList : MutableMap by mutableMapOf() \ No newline at end of file +internal class MutableContactList : MutableMap by mutableMapOf() { + override fun toString(): String = asIterable().joinToString(separator = ", ", prefix = "ContactList(", postfix = ")") { it.value.toString() } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt index 53b68784c..3f5f06740 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt @@ -2,6 +2,7 @@ package net.mamoe.mirai.contact +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupInfo import net.mamoe.mirai.utils.internal.PositiveNumbers import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail @@ -20,6 +21,11 @@ interface Group : Contact { val member: ContactList suspend fun getMember(id: UInt): Member + + /** + * 查询群资料 + */ + suspend fun queryGroupInfo(): GroupInfo } /** @@ -36,7 +42,8 @@ inline class GroupId(inline val value: UInt) /** * 将 [this] 转为 [GroupId]. */ -fun UInt.groupId(): GroupId = GroupId(this) +@Suppress("NOTHING_TO_INLINE") +inline fun UInt.groupId(): GroupId = GroupId(this) /** * 将无符号整数格式的 [Long] 转为 [GroupId]. diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt index 674f88092..74ce14bf3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt @@ -10,6 +10,11 @@ interface Member : QQ, Contact { * 所在的群 */ val group: Group + + /** + * 权限 + */ + val permission: MemberPermission } /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt index 76a12a6bb..86c863efa 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/internal/ContactImpl.kt @@ -5,8 +5,7 @@ package net.mamoe.mirai.contact.internal import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import net.mamoe.mirai.* +import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.data.Profile import net.mamoe.mirai.message.Message @@ -14,8 +13,12 @@ import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.chain import net.mamoe.mirai.message.singleChain import net.mamoe.mirai.network.protocol.tim.packet.action.* +import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.network.sessionKey +import net.mamoe.mirai.qqAccount +import net.mamoe.mirai.sendPacket import net.mamoe.mirai.utils.SuspendLazy +import net.mamoe.mirai.withSession internal sealed class ContactImpl : Contact { abstract override suspend fun sendMessage(message: MessageChain) @@ -37,14 +40,18 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr override suspend fun getMember(id: UInt): Member = if (_members.containsKey(id)) _members[id]!! - else membersLock.withLock { + else throw NoSuchElementException("No such member whose id is $id in group $id") /*membersLock.withLock { _members.getOrPut(id) { MemberImpl(bot.getQQ(id), this) } - } + }*/ override suspend fun sendMessage(message: MessageChain) { bot.sendPacket(GroupPacket.Message(bot.qqAccount, internalId, bot.sessionKey, message)) } + override suspend fun queryGroupInfo(): GroupInfo = bot.withSession { + GroupPacket.QueryGroupInfo(qqAccount, internalId, sessionKey).sendAndExpect() + } + override fun toString(): String = "Group(${this.id})" } @@ -74,6 +81,6 @@ internal data class QQImpl internal constructor(override val bot: Bot, override /** * 群成员 */ -internal data class MemberImpl(private val delegate: QQ, override val group: Group) : QQ by delegate, Member { - override fun toString(): String = "Member(${this.id})" +internal data class MemberImpl(private val delegate: QQ, override val group: Group, override val permission: MemberPermission) : QQ by delegate, Member { + override fun toString(): String = "Member(id=${this.id}, permission=$permission)" } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt index bd684f0e1..5cc48a7fc 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/PacketId.kt @@ -81,6 +81,8 @@ enum class KnownPacketId(override inline val value: UShort, override inline val inline REQUEST_PROFILE_DETAILS(0x003Cu, RequestProfileDetailsPacket), inline QUERY_PREVIOUS_NAME(0x01BCu, QueryPreviousNamePacket), + + inline QUERY_FRIEND_REMARK(0x003Eu, QueryFriendRemarkPacket) // 031F 查询 "新朋友" 记录 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupPacket.kt index 96b87a530..67262fa8c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupPacket.kt @@ -2,38 +2,38 @@ package net.mamoe.mirai.network.protocol.tim.packet.action -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.writeFully -import kotlinx.io.core.writeUByte -import net.mamoe.mirai.contact.GroupInternalId +import kotlinx.io.core.* +import net.mamoe.mirai.contact.* +import net.mamoe.mirai.contact.internal.MemberImpl +import net.mamoe.mirai.getQQ import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.internal.toPacket import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.withSession -/* -// TODO: 2019/11/27 群公告 -群公告请求: -72 E2 E0 74 F7 00 00 00 00 - -群公告回复(同群消息的id): -72 00 E2 E0 74 F7 12 DB 48 77 00 00 00 03 01 01 00 04 01 00 80 01 40 6C 18 F5 DA 02 00 00 27 1B 00 00 00 00 27 1B 01 F4 01 00 00 00 01 00 00 0F 1F 0C 42 69 67 20 63 6F 6E 76 65 6E 65 27 00 00 96 E6 AF 95 E4 B8 9A E4 BA 86 EF BC 8C E5 B8 8C E6 9C 9B E5 A4 A7 E5 AE B6 E8 83 BD E5 A4 9F E5 83 8F E4 BB A5 E5 89 8D E9 82 A3 E6 A0 B7 E5 BC 80 E5 BF 83 EF BC 8C E5 AD A6 E4 B9 A0 E8 BF 9B E6 AD A5 EF BC 8C E5 A4 A9 E5 A4 A9 E5 BF AB E4 B9 90 E3 80 82 E6 AD A4 E7 BE A4 E7 A6 81 E6 AD A2 E9 AA 82 E4 BA BA EF BC 8C E5 88 B7 E5 B1 8F E6 9A B4 E5 8A 9B EF BC 8C E8 BF 9D E8 A7 84 E8 80 85 E7 A6 81 E8 A8 80 EF BC 8C E4 B8 A5 E9 87 8D E8 80 85 E5 B0 B1 76 E8 BF 9B E7 BE A4 E6 97 B6 EF BC 8C E8 AF B7 E4 BF AE E6 94 B9 E6 AD A3 E7 A1 AE E5 A7 93 E5 90 8D E3 80 82 E4 B8 8D E8 83 BD 54 E5 90 8C E5 AD A6 EF BC 8C E5 A4 AA E8 BF 87 E5 88 86 E7 9A 84 54 21 28 E4 BA 92 E8 B5 9E E7 BE A4 EF BC 8C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF E8 81 8A E5 A4 A9 E8 80 85 E5 8F AF E4 BB A5 E4 BA 92 E8 B5 9E E5 AF B9 E6 96 B9 00 38 D9 FD F5 21 A6 1F 8D 61 37 A1 7A 92 91 2A 2C 71 46 A9 B9 1C 45 EB 38 74 4A 74 EA 77 7D 14 DB 12 D0 B0 09 C2 AA 22 16 F1 D0 B9 97 21 F0 5A A0 06 59 A7 3B 2F 32 D2 B8 E3 00 0F 00 00 00 00 06 00 03 00 02 01 01 00 04 00 04 00 00 00 15 00 05 00 04 52 7C C5 7C 00 06 00 04 00 00 00 20 00 07 00 04 00 00 00 00 00 09 00 01 00 C5 15 BE BE 00 1C ED 9F 9B 00 00 26 D0 E1 3A 00 00 2D 5C 53 A6 00 01 2D BD 28 D2 00 00 2E 94 76 3E 00 00 35 F3 BC F2 00 00 37 D6 91 AB 00 00 3A 60 1C 3E 00 80 3A 86 EA A3 00 48 3D 7F E7 70 00 00 3E 03 3F A2 00 09 41 47 0C DD 00 40 41 B6 32 A8 00 80 44 C8 DA 23 00 00 45 3E 1B 6A 00 80 45 C6 59 E9 00 C0 4A BD C6 F9 00 00 4C 67 45 E8 00 00 4E AD C2 C2 00 80 4F A0 F7 EC 00 80 50 CB 11 E8 00 00 58 22 21 90 00 00 59 17 3E 05 00 01 5E 74 48 D9 00 00 5E A2 B5 88 00 00 66 A1 32 9B 00 40 68 07 29 0A 00 00 68 0F EF 4F 00 00 69 8B 14 F3 00 80 6A A5 27 4E 00 00 6C 11 A0 89 00 81 6C 18 F5 DA 00 08 6C 21 F8 E2 00 01 71 F8 F5 18 00 00 72 0B CC B6 00 00 75 53 38 DF 00 00 7A A1 8B 82 00 00 7C 8C 1D 1B 00 00 7C BC D3 C1 00 00 84 2D B8 5F 00 00 88 4C 33 76 00 00 8C C8 0D 43 00 00 90 B8 65 22 00 00 91 54 89 E9 00 00 9C E6 93 A5 00 01 9D 59 6A 36 00 00 9D 63 81 5C 00 00 9E 31 AF AC 00 00 9E 69 86 25 00 80 A1 FD CA 2D 00 00 A5 22 5C 48 00 00 A5 F2 9A B7 00 00 AF 25 74 9E 00 01 B1 50 24 00 00 00 B2 BD 81 A9 00 00 B5 0E B3 DD 00 00 B9 BF 0D BC 00 00 C5 15 BE BE 00 00 - */ +data class GroupInfo( + val group: Group, + val owner: Member, + val name: String, + val announcement: String, + val members: ContactList +) : GroupPacket.GroupPacketResponse @AnnotatedId(KnownPacketId.SEND_GROUP_MESSAGE) -@PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") -object GroupPacket : SessionPacketFactory() { +object GroupPacket : SessionPacketFactory() { @Suppress("FunctionName") + @PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") fun Message( - botQQ: UInt, + bot: UInt, groupInternalId: GroupInternalId, sessionKey: SessionKey, message: MessageChain - ): OutgoingPacket = buildSessionPacket(botQQ, sessionKey, name = "GroupMessage") { - writeByte(0x2A) + ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "GroupMessage") { + writeUByte(0x2Au) writeGroup(groupInternalId) writeShortLVPacket { @@ -51,25 +51,206 @@ object GroupPacket : SessionPacketFactory() { } /** - * 查询公告 + * 查询群信息 */ @Suppress("FunctionName") - fun QueryBulletin( - botQQ: UInt, + @PacketVersion(date = "2019.11.27", timVersion = "2.3.2 (21173)") + fun QueryGroupInfo( + bot: UInt, groupInternalId: GroupInternalId, sessionKey: SessionKey - ): OutgoingPacket = buildSessionPacket( - botQQ, sessionKey, name = "QueryBulletin" - ) { + ): OutgoingPacket = buildSessionPacket(bot, sessionKey, name = "QueryBulletin", headerSizeHint = 9) { writeUByte(0x72u) writeGroup(groupInternalId) writeZero(4) } + interface GroupPacketResponse : Packet + @NoLog - object Response : Packet { - override fun toString(): String = "SendGroupMessagePacket.Response" + object MessageResponse : Packet, GroupPacketResponse { + override fun toString(): String = "GroupPacket.MessageResponse" } - override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): Response = Response + @UseExperimental(ExperimentalStdlibApi::class) + override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupPacketResponse = handler.bot.withSession { + return when (readUByte().toUInt()) { + 0x72u -> { + discardExact(1) // 00 + discardExact(4) // group internal id + val group = readUInt().group() // group id + + discardExact(13) //00 00 00 03 01 01 00 04 01 00 80 01 40 + val owner = MemberImpl(readUInt().qq(), group, MemberPermission.OWNER) + discardExact(22) + val groupName = readUByteLVString() + + discardExact(readUByte()) // 00 + discardExact(readUByte()) // 00 + val announcement = readUByteLVString() + discardExact(readUByte()) // 00 + discardExact(readUByte()) // 00 + discardExact(readUByte()) // 38 ... 未知 + discardExact(readUByte()) // 00 + discardExact(readUByte()) // 0A ... + + discardExact(38) + + val stop = readUInt() // 标记读取群成员的结束 + discardExact(1) // 00 + val members: MutableContactList = MutableContactList() + members[owner.id] = owner + do { + val qq = readUInt() + val status = readUShort().toInt() // 这个群成员的状态, 最后一 bit 为管理员权限. 这里面还包含其他状态 + if (qq == owner.id) { + continue + } + + val permission = when (status.takeLowestOneBit()) { + 1 -> MemberPermission.OPERATOR + else -> MemberPermission.MEMBER + } + members[qq] = MemberImpl(handler.bot.getQQ(qq), group, permission) + } while (qq != stop && remaining != 0L) + return GroupInfo(group, owner, groupName, announcement, ContactList(members)) + /* + * 群 Mirai + * + * 00 00 00 03 01 41 00 04 01 40 23 04 40 + * B1 89 BE 09 群主 + * + * 02 00 00 00 00 00 00 00 00 00 21 00 C8 01 + * 00 00 00 01 00 00 + * 00 2D + * + * 06 4D 69 72 61 69 20 群名 + * 00 + * 00 + * 00 + * 00 + * 00 + * 38 87 5F D8 E8 D4 E9 79 73 8A A4 21 1C 3E 2C 43 D0 23 55 53 49 D3 1C DB F6 1F 84 59 77 66 DA 9C D7 26 0F E3 BD E1 F2 B9 29 D1 F6 97 1C 42 5E B0 AF 09 51 72 DA 03 37 AB 65 + * 00 + * 0A 00 00 00 00 06 00 03 00 02 00 + * 01 00 04 00 04 00 00 00 01 00 05 00 04 5D 90 A7 25 00 06 00 04 04 08 00 00 00 07 00 04 00 00 05 80 00 09 00 01 01 + * B1 89 BE 09 00 + * 3E 03 3F A2 00 01 + * 48 76 54 DC 00 00 + * 76 E4 B8 DD 00 00 + * 89 1A 5E AC 00 00 + * B1 89 BE 09 00 00 + */ + + /* + * 群 XkWhXi + * + * 00 00 00 03 01 41 00 04 01 40 21 04 40 + * 3E 03 3F A2 群主 + * + * 02 00 00 00 01 00 00 00 00 27 1A 00 C8 01 + * 00 00 00 01 00 00 + * F3 C8 + * + * 06 58 6B 57 68 58 69 + * 00 + * 00 + * 3B E6 AC A2 E8 BF 8E E5 BC 80 E8 BD A6 EF BC 8C E5 8E BB 74 6D E7 9A 84 E7 BD 91 E8 AD A6 0A E6 AC A2 E8 BF 8E E5 BC 80 E8 BD A6 EF BC 8C E5 8E BB 74 6D E7 9A 84 E7 BD 91 E8 AD A6 + * 00 + * 00 + * 38 EB 3B A5 90 AC E3 70 1F 42 51 B4 72 81 C8 F5 5A D8 80 69 B6 76 AD A4 AA CC 6A 17 4C 79 81 FF 82 04 BA 13 CE 28 DA 6C 3F 41 77 C0 77 40 B5 87 8E EE 29 20 65 FC 2D FF 63 + * 00 + * 0A 00 00 00 00 06 00 03 00 02 00 + * 01 00 04 00 04 00 00 00 05 00 05 00 04 57 94 6F 41 00 06 00 04 04 08 00 10 00 07 00 04 00 00 04 04 00 09 00 01 00 + * B1 89 BE 09 00 2D 5C 53 A6 00 01 2F 9B 1C F2 00 00 35 49 95 D1 00 01 3B FA 06 9F 00 00 3E 03 3F A2 00 00 42 C4 32 63 00 01 59 17 3E 05 00 01 6A 89 3E 3E 00 00 6D D7 4E CA 00 00 76 E4 B8 DD 00 00 7C BB 60 3C 00 01 7C BC D3 C1 00 01 87 73 86 9D 00 00 90 19 72 65 00 00 97 30 9A 6B 00 00 9C B1 E5 55 00 01 B1 89 BE 09 00 01 + */ + + /* + * 群 20秃顶28火葬30重生异世 + * + * + */ + + /* + * 群 Big convene' (与上面两个来自不同 bot) + * + * 00 00 00 03 01 01 00 04 01 00 80 01 40 + * 6C 18 F5 DA 群主 + * + * 02 00 00 27 1B 00 00 00 00 27 1B 01 F4 01 + * 00 00 00 01 00 00 + * 0F 1F + * + * 0C 42 69 67 20 63 6F 6E 76 65 6E 65 27 00 群名 + * 00 96 E6 AF 95 E4 B8 9A E4 BA 86 EF BC 8C E5 B8 8C E6 9C 9B E5 A4 A7 E5 AE B6 E8 83 BD E5 A4 9F E5 83 8F E4 BB A5 E5 89 8D E9 82 A3 E6 A0 B7 E5 BC 80 E5 BF 83 EF BC 8C E5 AD A6 E4 B9 A0 E8 BF 9B E6 AD A5 EF BC 8C E5 A4 A9 E5 A4 A9 E5 BF AB E4 B9 90 E3 80 82 E6 AD A4 E7 BE A4 E7 A6 81 E6 AD A2 E9 AA 82 E4 BA BA EF BC 8C E5 88 B7 E5 B1 8F E6 9A B4 E5 8A 9B EF BC 8C E8 BF 9D E8 A7 84 E8 80 85 E7 A6 81 E8 A8 80 EF BC 8C E4 B8 A5 E9 87 8D E8 80 85 E5 B0 B1 + * 76 E8 BF 9B E7 BE A4 E6 97 B6 EF BC 8C E8 AF B7 E4 BF AE E6 94 B9 E6 AD A3 E7 A1 AE E5 A7 93 E5 90 8D E3 80 82 E4 B8 8D E8 83 BD 54 E5 90 8C E5 AD A6 EF BC 8C E5 A4 AA E8 BF 87 E5 88 86 E7 9A 84 54 21 28 E4 BA 92 E8 B5 9E E7 BE A4 EF BC 8C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF E8 81 8A E5 A4 A9 E8 80 85 E5 8F AF E4 BB A5 E4 BA 92 E8 B5 9E E5 AF B9 E6 96 B9 + * 00 38 D9 FD F5 21 A6 1F 8D 61 37 A1 7A 92 91 2A 2C 71 46 A9 B9 1C 45 EB 38 74 4A 74 EA 77 7D 14 DB 12 D0 B0 09 C2 AA 22 16 F1 D0 B9 97 21 F0 5A A0 06 59 A7 3B 2F 32 D2 B8 E3 + * 00 0F 00 00 00 00 06 00 03 00 02 01 01 00 04 00 04 00 00 00 15 00 05 00 04 52 7C C5 7C 00 06 00 04 00 00 00 20 00 07 00 04 00 00 00 00 00 09 00 01 00 + * + * C5 15 BE BE 00 ???为啥这个只有一个呢 + * 1C ED 9F 9B 00 00 + * 26 D0 E1 3A 00 00 + * 2D 5C 53 A6 00 01 自己 管理员 + * 2D BD 28 D2 00 00 + * 2E 94 76 3E 00 00 + * 35 F3 BC F2 00 00 + * 37 D6 91 AB 00 00 + * 3A 60 1C 3E 00 80 10000000 群员, 好友 + * 3A 86 EA A3 00 48 01001000 群员 手机在线 + * 3D 7F E7 70 00 00 + * 3E 03 3F A2 00 09 00001001 好友, 特别关心, TIM PC 在线, 管理员 + * 41 47 0C DD 00 40 01000000 群员, 离线 + * 41 B6 32 A8 00 80 + * 44 C8 DA 23 00 00 + * 45 3E 1B 6A 00 80 10000000 群员 手机在线 + * 45 C6 59 E9 00 C0 群员 + * 4A BD C6 F9 00 00 + * 4C 67 45 E8 00 00 + * 4E AD C2 C2 00 80 + * 4F A0 F7 EC 00 80 + * 50 CB 11 E8 00 00 + * 58 22 21 90 00 00 + * 59 17 3E 05 00 01 管理员 好友 + * 5E 74 48 D9 00 00 + * 5E A2 B5 88 00 00 + * 66 A1 32 9B 00 40 + * 68 07 29 0A 00 00 + * 68 0F EF 4F 00 00 + * 69 8B 14 F3 00 80 + * 6A A5 27 4E 00 00 + * 6C 11 A0 89 00 81 10000001 管理员 + * 6C 18 F5 DA 00 08 群主 + * 6C 21 F8 E2 00 01 管理员 + * 71 F8 F5 18 00 00 + * 72 0B CC B6 00 00 + * 75 53 38 DF 00 00 + * 7A A1 8B 82 00 00 + * 7C 8C 1D 1B 00 00 + * 7C BC D3 C1 00 00 + * 84 2D B8 5F 00 00 + * 88 4C 33 76 00 00 + * 8C C8 0D 43 00 00 + * 90 B8 65 22 00 00 + * 91 54 89 E9 00 00 + * 9C E6 93 A5 00 01 管理员 + * 9D 59 6A 36 00 00 + * 9D 63 81 5C 00 00 + * 9E 31 AF AC 00 00 + * 9E 69 86 25 00 80 + * A1 FD CA 2D 00 00 + * A5 22 5C 48 00 00 + * A5 F2 9A B7 00 00 + * AF 25 74 9E 00 01 + * B1 50 24 00 00 00 + * B2 BD 81 A9 00 00 + * B5 0E B3 DD 00 00 + * B9 BF 0D BC 00 00 + * C5 15 BE BE 00 00 + */ + } + + 0x2Au -> MessageResponse + else -> unsupported() + } + } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt index c0a952706..2c7776301 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Unknown.kt @@ -18,6 +18,12 @@ data class UnknownEventPacket( override fun toString(): String = "UnknownEventPacket(id=${id.toUHexString()}, identity=$identity)\n = ${body.readBytes().toUHexString()}" } +/* +被好友拉入群 (已经进入) +Mirai 21:54:15 : Packet received: UnknownEventPacket(id=00 57, identity=(920503456->1994701021)) + = 00 00 00 08 00 0A 00 04 01 00 00 00 36 DD C4 A0 01 04 00 00 00 00 3E 03 3F A2 00 00 20 E5 96 01 BC 23 AE 03 C7 B8 9F BE B3 E5 E4 77 A9 0E FD 2B 7C 64 8B C0 5F 29 8B D7 DC 85 7E 44 7B 00 30 33 65 62 61 62 31 31 66 63 63 61 34 63 38 39 31 36 31 33 37 37 65 65 62 36 63 32 39 37 31 33 34 32 35 62 64 30 34 66 62 31 61 31 65 37 31 63 33 + */ + //TODO This class should be declared with `inline`, but a CompilationException will be thrown class UnknownEventParserAndHandler(override val id: UShort) : EventParserAndHandler { 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 ef5e93679..d96845200 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 @@ -7,6 +7,19 @@ import kotlinx.io.pool.useInstance import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic +@Suppress("NOTHING_TO_INLINE") +inline fun Input.discardExact(n: Short) = this.discardExact(n.toInt()) + +@Suppress("NOTHING_TO_INLINE") +@JvmSynthetic +inline fun Input.discardExact(n: UShort) = this.discardExact(n.toInt()) + +@Suppress("NOTHING_TO_INLINE") +@JvmSynthetic +inline fun Input.discardExact(n: UByte) = this.discardExact(n.toInt()) + +@Suppress("NOTHING_TO_INLINE") +inline fun Input.discardExact(n: Byte) = this.discardExact(n.toInt()) fun ByteReadPacket.readRemainingBytes( n: Int = remaining.toInt()//not that safe but adequate @@ -30,13 +43,17 @@ fun Input.readPacket(length: Int): ByteReadPacket = this.readBytes(length).toRea fun Input.readUVarIntLVString(): String = String(this.readUVarIntByteArray()) +fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray()) + fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) fun Input.readUVarIntByteArray(): ByteArray = this.readBytes(this.readUVarInt().toInt()) +fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt()) + fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) -private inline fun inline(block: () -> R): R = block() +internal inline fun inline(block: () -> R): R = block() @Suppress("DuplicatedCode") fun Input.readTLVMap(expectingEOF: Boolean = false, tagSize: Int = 1): MutableMap { @@ -138,6 +155,7 @@ fun Input.readString(length: Long): String = String(this.readBytes(length.toInt( fun Input.readString(length: Short): String = String(this.readBytes(length.toInt())) @JvmSynthetic fun Input.readString(length: UShort): String = String(this.readBytes(length.toInt())) + fun Input.readString(length: Byte): String = String(this.readBytes(length.toInt())) @JvmSynthetic diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt index e82837d66..c44d2ec1e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt @@ -16,7 +16,11 @@ import net.mamoe.mirai.utils.internal.coerceAtMostOrFail import kotlin.random.Random import kotlin.random.nextInt -fun BytePacketBuilder.writeZero(count: Int) = repeat(count) { this.writeByte(0) } +fun BytePacketBuilder.writeZero(count: Int) { + require(count != 0) { "Trying to write zero with count 0, you made a mistake?" } + require(count > 0) { "writeZero: count must > 0" } + repeat(count) { this.writeByte(0) } +} fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) } diff --git a/mirai-debug/src/main/kotlin/HexDebuggerGui.kt b/mirai-debug/src/main/kotlin/HexDebuggerGui.kt index 8b44da638..c95ef6bbe 100644 --- a/mirai-debug/src/main/kotlin/HexDebuggerGui.kt +++ b/mirai-debug/src/main/kotlin/HexDebuggerGui.kt @@ -7,7 +7,9 @@ import javafx.scene.layout.Region import javafx.scene.paint.Color import javafx.scene.text.FontWeight import kotlinx.coroutines.* +import kotlinx.io.core.readUByte import kotlinx.io.core.readUInt +import kotlinx.io.core.readUShort import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.read @@ -77,6 +79,7 @@ class HexDebuggerGui : View("s") { private lateinit var outShort: TextField private lateinit var outUInt: TextField private lateinit var outString: TextArea + private lateinit var outBits: TextField private val clip = Toolkit.getDefaultToolkit().systemClipboard @@ -151,6 +154,18 @@ class HexDebuggerGui : View("s") { value.hexToBytes().size.toString() } + outBits.text = runOrNull { + value.hexToBytes().read { + when (remaining.toInt()) { + 0 -> null + 1 -> readUByte().toString(2) + 2 -> readUShort().toString(2) + 4 -> readUInt().toString(2) + else -> "" + } + } + } + outString.text = runOrNull { value.hexToBytes().encodeToString() } @@ -201,6 +216,7 @@ class HexDebuggerGui : View("s") { label("UVarInt") label("short") label("uint") + label("bits") label("string") children.filterIsInstance().forEach { it.fitToParentWidth() @@ -230,6 +246,11 @@ class HexDebuggerGui : View("s") { isEditable = false } + outBits = textfield { + promptText = "Bits" + isEditable = false + } + outString = textarea { promptText = "String" isEditable = false diff --git a/mirai-debug/src/main/kotlin/PacketDebuger.kt b/mirai-debug/src/main/kotlin/PacketDebuger.kt index a083eed48..b6efb8f8a 100644 --- a/mirai-debug/src/main/kotlin/PacketDebuger.kt +++ b/mirai-debug/src/main/kotlin/PacketDebuger.kt @@ -116,7 +116,7 @@ object PacketDebugger { * 7. 运行完 `mov eax,dword ptr ss:[ebp+10]` * 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey` */ - val sessionKey: SessionKey = SessionKey("43 EA BD 3C FF 6A 07 8E E4 13 E9 42 7F AD 03 F1".hexToBytes()) + val sessionKey: SessionKey = SessionKey("10 52 CD 27 0B A5 9C 74 1B A2 15 7E 41 19 0C 7B".hexToBytes()) const val qq: UInt = 761025446u val IgnoredPacketIdList: List = listOf( diff --git a/mirai-debug/src/main/kotlin/test/PacketTest.kt b/mirai-debug/src/main/kotlin/test/PacketTest.kt new file mode 100644 index 000000000..275a0ab74 --- /dev/null +++ b/mirai-debug/src/main/kotlin/test/PacketTest.kt @@ -0,0 +1,17 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + +package test + +import DebugNetworkHandler +import net.mamoe.mirai.network.protocol.tim.packet.UnknownPacketId +import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket.decode +import net.mamoe.mirai.utils.io.hexToBytes +import net.mamoe.mirai.utils.io.read + +suspend fun main() { + + "00 00 00 03 01 41 00 04 01 00 23 04 40 1A F7 2F 11 02 00 00 00 00 00 00 00 00 00 21 00 C8 01 00 00 00 01 00 00 00 9E 1E 32 30 E7 A7 83 E9 A1 B6 32 38 E7 81 AB E8 91 AC 33 30 E9 87 8D E7 94 9F E5 BC 82 E4 B8 96 00 00 00 00 00 38 0D 3F 83 73 6A 9C F1 71 7C 10 DC CE F0 03 EA D9 FC 03 D5 87 E1 CD F5 F4 FA 63 37 26 07 17 9F 98 68 06 1A B6 4E 49 0A 14 DE B1 6B E1 32 99 30 29 AB 11 06 28 D9 0E 8A 3C 00 0A 00 00 00 00 06 00 03 00 02 00 01 00 04 00 04 00 00 00 01 00 05 00 04 5D BF EB 82 00 06 00 04 04 08 00 00 00 07 00 04 00 00 85 84 00 09 00 01 01 9F D6 4B F1 00 1A F7 2F 11 00 00 3E 03 3F A2 00 00 53 12 02 7F 00 00 76 E4 B8 DD 00 00 87 73 86 9D 00 00 87 CF 53 7B 00 00 8A 92 D8 1D 00 00 8C D9 1B 93 00 00 8E E0 1A 8A 00 01 9F D6 4B F1 00 00".hexToBytes() + .read { + decode(UnknownPacketId(0u), 0u, DebugNetworkHandler) + } +} \ No newline at end of file diff --git a/mirai-debug/src/main/kotlin/test/ProtoTest.kt b/mirai-debug/src/main/kotlin/test/ProtoTest.kt index 449185743..92c61e448 100644 --- a/mirai-debug/src/main/kotlin/test/ProtoTest.kt +++ b/mirai-debug/src/main/kotlin/test/ProtoTest.kt @@ -28,14 +28,6 @@ data class ProtoTest( @UseExperimental(MiraiInternalAPI::class) suspend fun main() { deserializeTest() - - /* - intArrayOf( - ).forEach { - println(it.toUShort().toUHexString() + " => " + ProtoFieldId(it.toUInt())) - } - - println(ProtoBuf.dump(ProtoTest.serializer(), ProtoTest(1)).toUHexString())*/ } suspend fun deserializeTest() { @@ -43,10 +35,10 @@ suspend fun deserializeTest() { """ - - 00 36 DD C4 A0 01 2D 5C 53 A6 03 3E 03 3F A2 06 B9 DC C0 ED D4 B1 00 30 31 63 35 35 31 34 63 62 36 64 37 39 61 65 61 66 35 66 33 34 35 64 39 63 32 34 64 65 37 32 36 64 39 64 36 39 36 64 66 66 32 38 64 63 38 32 37 36 - - +00 00 00 03 01 01 00 04 01 00 80 01 40 6C 18 F5 DA 02 00 00 27 1B 00 00 00 00 27 1B 01 F4 01 00 00 00 01 00 00 0F 1F 0C 42 69 67 20 63 6F 6E 76 65 6E 65 27 00 00 96 E6 AF 95 E4 B8 9A E4 BA 86 EF BC 8C E5 B8 8C E6 9C 9B E5 A4 A7 E5 AE B6 E8 83 BD E5 A4 9F E5 83 8F E4 BB A5 E5 89 8D E9 82 A3 E6 A0 B7 E5 BC 80 E5 BF 83 EF BC 8C E5 AD A6 E4 B9 A0 E8 BF 9B E6 AD A5 EF BC 8C E5 A4 A9 E5 A4 A9 E5 BF AB E4 B9 90 E3 80 82 E6 AD A4 E7 BE A4 E7 A6 81 E6 AD A2 E9 AA 82 E4 BA BA EF BC 8C E5 88 B7 E5 B1 8F E6 9A B4 E5 8A 9B EF BC 8C E8 BF 9D E8 A7 84 E8 80 85 E7 A6 81 E8 A8 80 EF BC 8C E4 B8 A5 E9 87 8D E8 80 85 E5 B0 B1 76 E8 BF 9B E7 BE A4 E6 97 B6 EF BC 8C E8 AF B7 E4 BF AE E6 94 B9 E6 AD A3 E7 A1 AE E5 A7 93 E5 90 8D E3 80 82 E4 B8 8D E8 83 BD 54 E5 90 8C E5 AD A6 EF BC 8C E5 A4 AA E8 BF 87 E5 88 86 E7 9A 84 54 21 28 E4 BA 92 E8 B5 9E E7 BE A4 EF BC 8C E6 89 8B E6 9C BA E5 9C A8 E7 BA BF E8 81 8A E5 A4 A9 E8 80 85 E5 8F AF E4 BB A5 E4 BA 92 E8 B5 9E E5 AF B9 E6 96 B9 00 38 D9 FD F5 21 A6 1F 8D 61 37 A1 7A 92 91 2A 2C 71 46 A9 B9 1C 45 EB 38 74 4A 74 EA 77 7D 14 DB 12 D0 B0 09 C2 AA 22 16 F1 D0 B9 97 21 F0 5A A0 06 59 A7 3B 2F 32 D2 B8 E3 00 0F 00 00 00 00 06 00 03 00 02 01 01 00 04 00 04 00 00 00 15 00 05 00 04 52 7C C5 7C 00 06 00 04 00 00 00 20 00 07 00 04 00 00 00 00 00 09 00 01 00 C5 15 BE BE 00 1C ED 9F 9B 00 00 26 D0 E1 3A 00 00 2D 5C 53 A6 00 01 2D BD 28 D2 00 00 2E 94 76 3E 00 00 35 F3 BC F2 00 00 37 D6 91 AB 00 00 3A 60 1C 3E 00 80 3A 86 EA A3 00 48 3D 7F E7 70 00 00 3E 03 3F A2 00 09 41 47 0C DD 00 40 41 B6 32 A8 00 80 44 C8 DA 23 00 00 45 3E 1B 6A 00 80 45 C6 59 E9 00 C0 4A BD C6 F9 00 00 4C 67 45 E8 00 00 4E AD C2 C2 00 80 4F A0 F7 EC 00 80 50 CB 11 E8 00 00 58 22 21 90 00 00 59 17 3E 05 00 01 5E 74 48 D9 00 00 5E A2 B5 88 00 00 66 A1 32 9B 00 40 68 07 29 0A 00 00 68 0F EF 4F 00 00 69 8B 14 F3 00 80 6A A5 27 4E 00 00 6C 11 A0 89 00 81 6C 18 F5 DA 00 08 6C 21 F8 E2 00 01 71 F8 F5 18 00 00 72 0B CC B6 00 00 75 53 38 DF 00 00 7A A1 8B 82 00 00 7C 8C 1D 1B 00 00 7C BC D3 C1 00 00 84 2D B8 5F 00 00 88 4C 33 76 00 00 8C C8 0D 43 00 00 90 B8 65 22 00 00 91 54 89 E9 00 00 9C E6 93 A5 00 01 9D 59 6A 36 00 00 9D 63 81 5C 00 00 9E 31 AF AC 00 00 9E 69 86 25 00 80 A1 FD CA 2D 00 00 A5 22 5C 48 00 00 A5 F2 9A B7 00 00 AF 25 74 9E 00 01 B1 50 24 00 00 00 B2 BD 81 A9 00 00 B5 0E B3 DD 00 00 B9 BF 0D BC 00 00 C5 15 BE BE 00 00 + + + """.trimIndent().replace("\n", " ").replace("[", "").replace("]", "") .hexToBytes() println(bytes.read { readProtoMap() }) 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 980201535..03530d2ca 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 @@ -64,6 +64,13 @@ suspend fun main() { "你好" reply "你好!" + "群资料" reply { + if (this is GroupMessage) { + group.queryGroupInfo().toString().reply() + } + "" + } + startsWith("profile", removePrefix = true) { val account = it.trim() if (account.isNotEmpty()) {