mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-03 15:10:14 +08:00
Group member permission determination and change
This commit is contained in:
parent
e3c3c71290
commit
a28a90ec3d
@ -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)
|
||||
|
||||
计划中: 添加好友
|
||||
|
||||
|
@ -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
|
||||
) {
|
||||
/**
|
||||
|
@ -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}")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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""")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user