Group member permission determination and change

This commit is contained in:
Him188 2019-11-02 00:17:56 +08:00
parent e3c3c71290
commit a28a90ec3d
16 changed files with 215 additions and 134 deletions

View File

@ -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)
计划中: 添加好友

View File

@ -18,7 +18,7 @@ import kotlin.jvm.JvmName
/**
* 消息事件时创建的临时容器.
*/
abstract class SenderAndMessage<S : Contact>(
abstract class SenderAndMessage<TContact : Contact>(
/**
* 发送这条消息的用户.
*/
@ -26,7 +26,7 @@ abstract class SenderAndMessage<S : Contact>(
/**
* 消息事件主体. 对于好友消息, 这个属性为 [QQ] 的实例; 对于群消息, 这个属性为 [Group] 的实例
*/
val subject: S,
val subject: TContact,
val message: MessageChain
) {
/**

View File

@ -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}")
}
})
}

View File

@ -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<NoLog>().any()
} == true
}?.let {
bot.logger.verbose("Packet sent: $it")
}
PacketSentEvent(bot, packet).broadcast()

View File

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

View File

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

View File

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

View File

@ -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<String> by listOf(
"idHex",
"id",
"eventIdentity",
"packetId",
"sequenceIdInternal",
"sequenceId",
@ -152,6 +160,13 @@ private object IgnoreIdListInclude : List<String> 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""")
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<RequestSKeyPacket.Response>(this)
ACCOUNT_INFO -> ResponsePacket.Encrypted<RequestAccountInfoPacket.Response>(this)

View File

@ -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<Region>().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
}

View File

@ -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<Image> {
reply(message)
}
startsWith("随机图片", removePrefix = true) {
withContext(Dispatchers.Default) {
try {