From 09046ae6865d228c35be1aada0965cb334add0e7 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 16 Oct 2019 22:33:02 +0800 Subject: [PATCH] Rewrite packets --- .../network/BotNetworkHandler.kt | 9 +- .../protocol/tim/TIMBotNetworkHandler.kt | 91 +++++++++---- .../network/protocol/tim/TIMProtocol.kt | 11 +- .../tim/handler/ActionPacketHandler.kt | 22 ++-- .../protocol/tim/packet/ClientPacket.kt | 27 +++- .../tim/packet/FriendOnlineStatusChanged.kt | 8 +- .../network/protocol/tim/packet/GradeInfo.kt | 17 +-- .../network/protocol/tim/packet/Heartbeat.kt | 10 +- .../network/protocol/tim/packet/Packet.kt | 34 ++--- .../network/protocol/tim/packet/PacketId.kt | 7 +- .../protocol/tim/packet/ServerEvent.kt | 13 +- .../protocol/tim/packet/ServerPacket.kt | 122 ++++++++---------- .../tim/packet/UnknownServerPacket.kt | 18 ++- .../protocol/tim/packet/UploadImage.kt | 2 +- .../protocol/tim/packet/action/AddContact.kt | 68 +++++----- .../action/ClientSendFriendMessagePacket.kt | 6 +- .../action/ClientSendGroupMessagePacket.kt | 7 +- .../tim/packet/action/ProfilePicture.kt | 18 +++ .../login/ClientChangeOnlineStatusPacket.kt | 8 +- .../{ClientLogin.kt => PasswordSubmission.kt} | 71 +++------- .../network/protocol/tim/packet/login/SKey.kt | 25 ++-- .../tim/packet/login/ServerLoginResponse.kt | 66 +++++----- .../packet/login/ServerLoginSuccessPacket.kt | 4 +- .../protocol/tim/packet/login/Session.kt | 18 +-- .../protocol/tim/packet/login/Touch.kt | 46 +++---- .../tim/packet/login/VerificationCode.kt | 68 +++------- .../utils/ByteReadPacketUtil.kt | 74 ++++------- .../utils/PacketBuilderUtil.kt | 1 + .../protocol/tim/packet/PacketInternalJvm.kt | 7 +- .../protocol/tim/packet/UploadImageJvm.kt | 6 +- .../mamoe/mirai/utils/CaptchaResolverJvm.kt | 8 +- .../net/mamoe/mirai/utils/SocketBridgeJvm.kt | 4 +- .../src/jvmTest/kotlin/ImageOutputTest.java | 14 -- mirai-debug/src/main/java/PacketDebuger.kt | 29 +++-- .../mirai-demo-1/src/main/java/demo1/Main.kt | 12 +- 35 files changed, 461 insertions(+), 490 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ProfilePicture.kt rename mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/{ClientLogin.kt => PasswordSubmission.kt} (58%) delete mode 100644 mirai-core/src/jvmTest/kotlin/ImageOutputTest.java diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt index beb7d8ac9..fba713b9a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt @@ -1,7 +1,6 @@ package net.mamoe.mirai.network -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.cancel +import kotlinx.coroutines.* import kotlinx.io.core.Closeable import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocket import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler @@ -11,6 +10,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRe import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.utils.LoginConfiguration import net.mamoe.mirai.utils.PlatformDatagramChannel +import kotlin.coroutines.ContinuationInterceptor /** * Mirai 的网络处理器, 它承担所有数据包([Packet])的处理任务. @@ -69,11 +69,12 @@ interface BotNetworkHandler : Closeable { suspend fun sendPacket(packet: ClientPacket) override fun close() { - NetworkScope.cancel("handler closed", HandlerClosedException()) + //todo check?? + NetworkScope.coroutineContext[ContinuationInterceptor]!!.cancelChildren(HandlerClosedException()) } } /** * [BotNetworkHandler] closed */ -class HandlerClosedException : Exception() \ No newline at end of file +class HandlerClosedException : CancellationException("handler closed") \ No newline at end of file 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 0c3e39871..31352b284 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 @@ -204,6 +204,24 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : } } + /* todo 修改为这个模式是否更好? + + interface Pk + + object TestPacket : Pk { + operator fun invoke(bot: UInt): TestPacket.(BytePacketBuilder) -> Unit { + + } + } + + override inline fun send(p: P.(BytePacketBuilder) -> Unit): UShort { + val encoded = with(P::class.objectInstance!!){ + buildPacket { + this@with.p(this) + } + } + }*/ + override suspend fun sendPacket(packet: ClientPacket) = withContext(NetworkScope.coroutineContext) { check(channel.isOpen) { "channel is not open" } @@ -282,13 +300,22 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : this.loginIP = packet.loginIP this.loginTime = packet.loginTime this.token0825 = packet.token0825 - socket.sendPacket(ClientPasswordSubmissionPacket(bot.qqNumber, bot.account.password, packet.loginTime, packet.loginIP, this.privateKey, packet.token0825, socket.configuration.randomDeviceName)) + + socket.sendPacket(ClientPasswordSubmissionPacket( + bot = bot.qqNumber, + password = bot.account.password, + loginTime = loginTime, + loginIP = loginIP, + privateKey = privateKey, + token0825 = token0825, + token00BA = null, + randomDeviceName = socket.configuration.randomDeviceName + )) } } is ServerLoginResponseFailedPacket -> { loginResult.complete(packet.loginResult) - bot.close() return } @@ -296,24 +323,34 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : this.privateKey = getRandomByteArray(16)//似乎是必须的 this.token00BA = packet.token00BA - socket.sendPacket(ClientLoginResendPacket3105(bot.qqNumber, bot.account.password, this.loginTime, this.loginIP, this.privateKey, this.token0825, packet.token00BA, socket.configuration.randomDeviceName)) + socket.sendPacket(ClientPasswordSubmissionPacket( + bot = bot.qqNumber, + password = bot.account.password, + loginTime = loginTime, + loginIP = loginIP, + privateKey = privateKey, + token0825 = token0825, + token00BA = packet.token00BA, + randomDeviceName = socket.configuration.randomDeviceName + )) } - is ServerLoginResponseVerificationCodeInitPacket -> { + is ServerLoginResponseCaptchaInitPacket -> { //[token00BA]来源之一: 验证码 this.token00BA = packet.token00BA this.captchaCache = packet.verifyCodePart1 if (packet.unknownBoolean == true) { this.captchaSectionId = 1 - socket.sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, bot.qqNumber, this.token0825, this.captchaSectionId++, packet.token00BA)) + socket.sendPacket(ClientCaptchaTransmissionRequestPacket(bot.qqNumber, this.token0825, this.captchaSectionId++, packet.token00BA)) } } is ServerCaptchaTransmissionPacket -> { - if (packet is ServerCaptchaWrongPacket) { + //packet is ServerCaptchaWrongPacket + if (this.captchaSectionId == 0) { bot.error("验证码错误, 请重新输入") - captchaSectionId = 1 + this.captchaSectionId = 1 this.captchaCache = null } @@ -322,15 +359,17 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : if (packet.transmissionCompleted) { val code = solveCaptcha(captchaCache!!) + + this.captchaCache = null if (code == null) { - this.captchaCache = null - this.captchaSectionId = 1 - socket.sendPacket(ClientVerificationCodeRefreshPacket(packet.packetIdLast + 1, bot.qqNumber, token0825)) + this.captchaSectionId = 1//意味着正在刷新验证码 + socket.sendPacket(ClientCaptchaRefreshPacket(bot.qqNumber, token0825)) } else { - socket.sendPacket(ClientVerificationCodeSubmitPacket(packet.packetIdLast + 1, bot.qqNumber, token0825, code, packet.verificationToken)) + this.captchaSectionId = 0//意味着已经提交验证码 + socket.sendPacket(ClientCaptchaSubmitPacket(bot.qqNumber, token0825, code, packet.verificationToken)) } } else { - socket.sendPacket(ClientVerificationCodeTransmissionRequestPacket(packet.packetIdLast + 1, bot.qqNumber, token0825, captchaSectionId++, packet.token00BA)) + socket.sendPacket(ClientCaptchaTransmissionRequestPacket(bot.qqNumber, token0825, captchaSectionId++, packet.token00BA)) } } @@ -339,20 +378,20 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : socket.sendPacket(ClientSessionRequestPacket(bot.qqNumber, socket.serverIp, packet.token38, packet.token88, packet.encryptionKey)) } - //是ClientPasswordSubmissionPacket之后服务器回复的 + //是ClientPasswordSubmissionPacket之后服务器回复的可能之一 is ServerLoginResponseKeyExchangePacket -> { - //if (packet.tokenUnknown != null) { - //this.token00BA = packet.token00BA!! - //println("token00BA changed!!! to " + token00BA.toUByteArray()) - //} - if (packet.flag == ServerLoginResponseKeyExchangePacket.Flag.`08 36 31 03`) { - this.privateKey = packet.privateKeyUpdate - socket.sendPacket(ClientLoginResendPacket3104(bot.qqNumber, bot.account.password, loginTime, loginIP, privateKey, token0825, packet.tokenUnknown - ?: token00BA, socket.configuration.randomDeviceName, packet.tlv0006)) - } else { - socket.sendPacket(ClientLoginResendPacket3106(bot.qqNumber, bot.account.password, loginTime, loginIP, privateKey, token0825, packet.tokenUnknown - ?: token00BA, socket.configuration.randomDeviceName, packet.tlv0006)) - } + this.privateKey = packet.privateKeyUpdate + + socket.sendPacket(ClientPasswordSubmissionPacket( + bot = bot.qqNumber, + password = bot.account.password, + loginTime = loginTime, + loginIP = loginIP, + privateKey = privateKey, + token0825 = token0825, + token00BA = packet.tokenUnknown ?: token00BA, + randomDeviceName = socket.configuration.randomDeviceName + )) } is ServerSessionKeyResponsePacket -> { @@ -386,7 +425,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : is ServerCaptchaPacket.Encrypted -> socket.distributePacket(packet.decrypt()) - is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> socket.distributePacket(packet.decrypt()) + is ServerLoginResponseCaptchaInitPacket.Encrypted -> socket.distributePacket(packet.decrypt()) is ServerLoginResponseKeyExchangePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.privateKey)) is ServerLoginResponseSuccessPacket.Encrypted -> socket.distributePacket(packet.decrypt(this.privateKey)) is ServerSessionKeyResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.sessionResponseDecryptionKey)) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt index f91d47dda..3562b4446 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMProtocol.kt @@ -41,10 +41,13 @@ object TIMProtocol { */ const val touchKey = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"//16 - /** - * Redirection 发出时写入, 并用于加密, 接受 Redirection response 时解密. - */ - const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"//16 + + //统一替换为了 touchKey + ///** + // * Redirection 发出时写入, 并用于加密, 接受 Redirection response 时解密. + // * 这个 key 似乎是可以任意的. + // */ + //const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"//16 /** * 并非常量. 设置为常量是为了让 [shareKey] 为常量 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt index 1fd2d7623..7f95165d2 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.handler import kotlinx.coroutines.CompletableDeferred @@ -15,6 +17,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRequestPacket import net.mamoe.mirai.network.protocol.tim.packet.login.ServerSKeyResponsePacket import net.mamoe.mirai.utils.getGTK import net.mamoe.mirai.utils.hexToBytes +import kotlin.properties.Delegates /** * 动作: 获取好友列表, 点赞, 踢人等. @@ -107,17 +110,16 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { private val future: CompletableDeferred //private val image: BufferedImage ) { - lateinit var id: ByteArray - + var id: UShort = UninitializedPacketId fun onPacketReceived(packet: ServerPacket) { - if (!::id.isInitialized) { + if (id == UninitializedPacketId) { return } when (packet) { is ServerCanAddFriendResponsePacket -> { - if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) { + if (packet.id != id) { return } @@ -160,17 +162,17 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { private val future: CompletableDeferred, private val message: Lazy ) { - lateinit var id: ByteArray + var id: UShort = UninitializedPacketId suspend fun onPacketReceived(packet: ServerPacket) { - if (!::id.isInitialized) { + if (id == UninitializedPacketId) { return } when (packet) { is ServerCanAddFriendResponsePacket -> { - if (!(packet.idByteArray.contentEquals(id))) { + if (packet.id != id) { return } @@ -201,11 +203,13 @@ class ActionPacketHandler(session: LoginSession) : PacketHandler(session) { suspend fun sendAddRequest() { - session.socket.sendPacket(ClientCanAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey).also { this.id = it.idHex.hexToBytes() }) + session.socket.sendPacket(ClientCanAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey)) } fun close() { // uploadImageSessions.remove(this) } } -} \ No newline at end of file +} + +private val UninitializedPacketId: UShort = 0u \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt index c8bd68ad8..ba9631285 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt @@ -2,11 +2,14 @@ package net.mamoe.mirai.network.protocol.tim.packet +import kotlinx.atomicfu.atomic import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.utils.writeHex -//TODO 将序列 ID 从包 ID 中独立出来 +/** + * 发给服务器的数据包. 必须有 [PacketId] 注解或 `override` [packetId]. 否则将会抛出 [IllegalStateException] + */ abstract class ClientPacket : Packet(), Closeable { /** * Encode this packet. @@ -15,25 +18,30 @@ abstract class ClientPacket : Packet(), Closeable { */ protected abstract fun encode(builder: BytePacketBuilder) + override val sequenceId: UShort by lazy { + atomicNextSequenceId() + } + companion object { - @Suppress("PrivatePropertyName") - private val UninitializedByteReadPacket = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool) + private val sequenceIdInternal = atomic(1) + internal fun atomicNextSequenceId() = sequenceIdInternal.getAndIncrement().toUShort() } /** * 务必 [ByteReadPacket.close] 或 [close] 或使用 [Closeable.use] */ - var packet: ByteReadPacket = UninitializedByteReadPacket + internal var packet: ByteReadPacket = UninitializedByteReadPacket get() { if (field === UninitializedByteReadPacket) build() return field } + private set private fun build(): ByteReadPacket { packet = buildPacket { writeHex(TIMProtocol.head) writeHex(TIMProtocol.ver) - writeHex(idHex) + writePacketId() encode(this) writeHex(TIMProtocol.tail) } @@ -43,4 +51,11 @@ abstract class ClientPacket : Packet(), Closeable { override fun toString(): String = packetToString() override fun close() = if (this.packet === UninitializedByteReadPacket) Unit else this.packet.close() -} \ No newline at end of file + + private fun BytePacketBuilder.writePacketId() { + writeUShort(this@ClientPacket.id) + writeUShort(sequenceId) + } +} + +private val UninitializedByteReadPacket = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool) 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 bf3a91b54..345eb67b5 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 @@ -1,4 +1,4 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet @@ -9,11 +9,10 @@ import kotlinx.io.core.readUInt import net.mamoe.mirai.utils.OnlineStatus import kotlin.properties.Delegates - /** * 好友在线状态改变 */ -@PacketId("00 81") +@PacketId(0x00_81u) class ServerFieldOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket(input) { var qq: UInt by Delegates.notNull() lateinit var status: OnlineStatus @@ -27,7 +26,8 @@ class ServerFieldOnlineStatusChangedPacket(input: ByteReadPacket) : ServerPacket //在线 XX XX XX XX 01 00 00 00 00 00 00 00 0A 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00 //忙碌 XX XX XX XX 01 00 00 00 00 00 00 00 32 15 E3 10 00 01 2E 01 00 00 00 00 00 00 00 00 00 00 00 13 08 02 C2 76 E4 B8 DD 00 00 00 00 00 00 00 00 00 00 00 + @PacketId(0x00_81u) class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): ServerFieldOnlineStatusChangedPacket = ServerFieldOnlineStatusChangedPacket(this.decryptBy(sessionKey)).setId(this.idHex) + fun decrypt(sessionKey: ByteArray): ServerFieldOnlineStatusChangedPacket = ServerFieldOnlineStatusChangedPacket(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/GradeInfo.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt index 66dcc81fb..44af11ace 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/GradeInfo.kt @@ -1,4 +1,4 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet @@ -16,26 +16,23 @@ import net.mamoe.mirai.utils.writeRandom * * @author Him188moe */ - -@PacketId("00 5C") +@PacketId(0x00_5Cu) class ClientAccountInfoRequestPacket( private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeRandom(2) - this.writeQQ(qq) this.writeHex(TIMProtocol.fixVer2) this.encryptAndWrite(sessionKey) { - writeUByte(0x88.toUByte()) + writeUByte(0x88u) writeQQ(qq) writeByte(0x00) } } } -@PacketId("00 5C") +@PacketId(0x00_5Cu) class ServerAccountInfoResponsePacket(input: ByteReadPacket) : ServerPacket(input) { //等级 //升级剩余活跃天数 @@ -44,8 +41,8 @@ class ServerAccountInfoResponsePacket(input: ByteReadPacket) : ServerPacket(inpu } - @PacketId("00 5C") - class Encrypted(inputStream: ByteReadPacket) : ServerPacket(inputStream) { - fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket = ServerAccountInfoResponsePacket(this.decryptBy(sessionKey)).setId(this.idHex) + @PacketId(0x00_5Cu) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { + fun decrypt(sessionKey: ByteArray): ServerAccountInfoResponsePacket = ServerAccountInfoResponsePacket(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 05f1ba41c..72e3489b5 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 @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.BytePacketBuilder @@ -5,16 +7,11 @@ import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.utils.* - -@PacketId("00 58") +@PacketId(0x00_58u) class ClientHeartbeatPacket( private val bot: Long, private val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(bot) this.writeHex(TIMProtocol.fixVer) @@ -24,4 +21,5 @@ class ClientHeartbeatPacket( } } +@PacketId(0x00_58u) class ServerHeartbeatResponsePacket(input: ByteReadPacket) : ServerPacket(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 dc4a60552..238bc20b6 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 @@ -1,31 +1,25 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet -import net.mamoe.mirai.utils.hexToUBytes - +import kotlinx.io.core.Closeable +import net.mamoe.mirai.utils.toUHexString /** - * 数据包 + * 数据包. */ -abstract class Packet { - open val idHex: String by lazy { - this::class.annotations.filterIsInstance().firstOrNull()?.value?.trim() ?: "" - } +abstract class Packet : Closeable { + /** + * 2 Ubyte + */ + open val id: UShort = (this::class.annotations.firstOrNull { it is PacketId } as? PacketId)?.value ?: error("Annotation PacketId not found") - open val fixedId: String by lazy { - when (this.idHex.length) { - 0 -> "__ __ __ __" - 2 -> this.idHex + " __ __ __" - 5 -> this.idHex + " __ __" - 7 -> this.idHex + " __" - else -> this.idHex - } - } + /** + * 包序列 id. 唯一 + */ + abstract val sequenceId: UShort - open val idByteArray: ByteArray by lazy { - idHex.hexToUBytes().toByteArray() - } + val idHexString: String get() = (id.toInt().shl(16) or sequenceId.toInt()).toUHexString() } internal expect fun Packet.packetToString(): String \ 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 066600f40..89dc384c0 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 @@ -1,11 +1,10 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.network.protocol.tim.packet @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) annotation class PacketId( - /** - * 用于识别的包 ID - */ - val value: String + val value: UShort ) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt index 9b4b7b37f..3b3162725 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt @@ -33,7 +33,6 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = */ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: EventPacketIdentity) : ServerPacket(input) { class Raw(input: ByteReadPacket) : ServerPacket(input) { - fun distribute(): ServerEventPacket = with(input) { val eventIdentity = EventPacketIdentity( from = readUInt(), @@ -67,8 +66,9 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event //todo 错了. 可能是 00 79 才是. return@with ServerFriendTypingCanceledPacket(input, eventIdentity) + /* if (readUByte().toUInt() == 0x37u) ServerFriendTypingStartedPacket(input, eventIdentity) - else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity) + else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/ } "00 79" -> IgnoredServerEventPacket(type, input, eventIdentity) @@ -78,11 +78,11 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event MiraiLogger.logDebug("UnknownEvent type = ${type.toUHexString()}") UnknownServerEventPacket(input, eventIdentity) } - }.setId(idHex) + }.applyId(id).applySequence(sequenceId) } class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey)).setId(this.idHex) + fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey)).applyId(id).applySequence(sequenceId) } } @@ -90,9 +90,8 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event val bot: Long, val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String = this@ServerEventPacket.idHex - override val idByteArray: ByteArray = this@ServerEventPacket.idByteArray - override val fixedId: String = idHex + override val id: UShort get() = this@ServerEventPacket.id + override val sequenceId: UShort get() = this@ServerEventPacket.sequenceId override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(bot) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt index 8a8ff2977..95d6c179b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt @@ -7,22 +7,17 @@ import kotlinx.io.core.Closeable import kotlinx.io.core.IoBuffer import kotlinx.io.core.readBytes import net.mamoe.mirai.utils.* +import kotlin.properties.Delegates /** * 来自服务器的数据包 + * + * @see parseServerPacket */ abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable { - override var idHex: String = EMPTY_ID_HEX - get() { - if (field === EMPTY_ID_HEX) { - idHex = (this::class.annotations.firstOrNull { it::class == PacketId::class } as? PacketId)?.value?.trim() - ?: "" - } - return field - } - - var encoded: Boolean = false + override var id: UShort = super.id + override var sequenceId: UShort by Delegates.notNull() open fun decode() { @@ -30,65 +25,58 @@ abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable { override fun close() = this.input.close() - companion object { - private const val EMPTY_ID_HEX = "EMPTY_ID_HEX" - } - - override fun toString(): String = this.packetToString() - - fun getFixedId(id: String): String = when (id.length) { - 0 -> "__ __ __ __" - 2 -> "$id __ __ __" - 5 -> "$id __ __" - 7 -> "$id __" - else -> id - } - - fun decryptBy(key: ByteArray): ByteReadPacket { - return ByteReadPacket(decryptAsByteArray(key)) - } - - fun decryptBy(key: IoBuffer): ByteReadPacket { - return ByteReadPacket(decryptAsByteArray(key)) - } - - fun decryptBy(keyHex: String): ByteReadPacket { - return this.decryptBy(keyHex.hexToBytes()) - } - - fun decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket { - return TEA.decrypt(this.decryptAsByteArray(key1), key2).toReadPacket() - } - - - fun decryptBy(key1: String, key2: ByteArray): ByteReadPacket { - return this.decryptBy(key1.hexToBytes(), key2) - } - - fun decryptBy(key1: String, key2: IoBuffer): ByteReadPacket { - return this.decryptBy(key1.hexToBytes(), key2.readBytes()) - } - - - fun decryptBy(key1: ByteArray, key2: String): ByteReadPacket { - return this.decryptBy(key1, key2.hexToBytes()) - } - - - fun decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket { - return this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes()) - } - - fun decryptAsByteArray(key: ByteArray): ByteArray { - return TEA.decrypt(input.readRemainingBytes().cutTail(1), key) - } - - fun decryptAsByteArray(keyHex: String): ByteArray = this.decryptAsByteArray(keyHex.hexToBytes()) - fun decryptAsByteArray(buffer: IoBuffer): ByteArray = this.decryptAsByteArray(buffer.readBytes()) } -fun

P.setId(idHex: String): P { - this.idHex = idHex +fun S.applyId(id: UShort): S { + this.id = id return this } + +fun S.applySequence(sequenceId: UShort): S { + this.sequenceId = sequenceId + return this +} + +fun ServerPacket.decryptBy(key: ByteArray): ByteReadPacket { + return ByteReadPacket(decryptAsByteArray(key)) +} + +fun ServerPacket.decryptBy(key: IoBuffer): ByteReadPacket { + return ByteReadPacket(decryptAsByteArray(key)) +} + +fun ServerPacket.decryptBy(keyHex: String): ByteReadPacket { + return this.decryptBy(keyHex.hexToBytes()) +} + +fun ServerPacket.decryptBy(key1: ByteArray, key2: ByteArray): ByteReadPacket { + return TEA.decrypt(this.decryptAsByteArray(key1), key2).toReadPacket() +} + + +fun ServerPacket.decryptBy(key1: String, key2: ByteArray): ByteReadPacket { + return this.decryptBy(key1.hexToBytes(), key2) +} + +fun ServerPacket.decryptBy(key1: String, key2: IoBuffer): ByteReadPacket { + return this.decryptBy(key1.hexToBytes(), key2.readBytes()) +} + + +fun ServerPacket.decryptBy(key1: ByteArray, key2: String): ByteReadPacket { + return this.decryptBy(key1, key2.hexToBytes()) +} + + +fun ServerPacket.decryptBy(keyHex1: String, keyHex2: String): ByteReadPacket { + return this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes()) +} + +fun ServerPacket.decryptAsByteArray(key: ByteArray): ByteArray { + return TEA.decrypt(input.readRemainingBytes().cutTail(1), key) +} + +fun ServerPacket.decryptAsByteArray(keyHex: String): ByteArray = this.decryptAsByteArray(keyHex.hexToBytes()) + +fun ServerPacket.decryptAsByteArray(buffer: IoBuffer): ByteArray = this.decryptAsByteArray(buffer.readBytes()) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt index cdb28f936..e19cbdccf 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UnknownServerPacket.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE") + package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.ByteReadPacket @@ -7,18 +9,26 @@ import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.toUHexString -class UnknownServerPacket(input: ByteReadPacket) : ServerPacket(input) { +class UnknownServerPacket( + input: ByteReadPacket, + override var id: UShort, + override var sequenceId: UShort +) : ServerPacket(input) { override fun decode() { val raw = this.input.readBytes() MiraiLogger.logDebug("UnknownServerPacket data: " + raw.toUHexString()) } - - class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): UnknownServerPacket = UnknownServerPacket(this.decryptBy(sessionKey)).setId(this.idHex) + class Encrypted( + input: ByteReadPacket, + override var id: UShort, + override var sequenceId: UShort + ) : ServerPacket(input) { + fun decrypt(sessionKey: ByteArray): UnknownServerPacket = UnknownServerPacket(this.decryptBy(sessionKey), this.id, this.sequenceId) } override fun toString(): String { + @Suppress("RemoveRedundantQualifierName") return LoggerTextFormat.LIGHT_RED.toString() + super.toString() } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt index 9fdedd4cd..ddb98a3a3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt @@ -25,7 +25,7 @@ abstract class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : Server println(data.size) println(data.size) if (data.size == 209) { - return ServerTryGetImageIDSuccessPacket(data.toReadPacket()).setId(this.idHex) + return ServerTryGetImageIDSuccessPacket(data.toReadPacket()).applySequence(sequenceId) } return ServerTryGetImageIDFailedPacket(data.toReadPacket()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt index 9cd0f6eb7..6113ea118 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/AddContact.kt @@ -2,14 +2,9 @@ package net.mamoe.mirai.network.protocol.tim.packet.action -import kotlinx.io.core.BytePacketBuilder -import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.readBytes +import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* /** @@ -17,59 +12,69 @@ import net.mamoe.mirai.utils.* * * @author Him188moe */ -@PacketId("00 A7") +@PacketId(0x00_A7u) class ClientCanAddFriendPacket( val bot: Long, val qq: Long, val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(bot) - this.writeHex(TIMProtocol.fixVer2) - this.encryptAndWrite(sessionKey) { + writeQQ(bot) + writeHex(TIMProtocol.fixVer2) + encryptAndWrite(sessionKey) { writeQQ(qq) } } } -@PacketId("00 A7") +@PacketId(0x00_A7u) class ServerCanAddFriendResponsePacket(input: ByteReadPacket) : ServerPacket(input) { lateinit var state: State enum class State { + /** + * 已经添加 + */ ALREADY_ADDED, + /** + * 需要验证信息 + */ REQUIRE_VERIFICATION, + /** + * 不需要验证信息 + */ NOT_REQUIRE_VERIFICATION, + + /** + * 对方拒绝添加 + */ FAILED, } - override fun decode() { - input.readBytes() - val data = input.readRemainingBytes() - if (data.size == 99) { + override fun decode() = with(input) { + //需要验证信息 00 23 24 8B 00 01 + + if (input.remaining > 20) {//todo check state = State.ALREADY_ADDED return } - state = when (data[data.size - 1].toUInt()) { + discardExact(4)//对方qq号 + state = when (val state = readUShort().toUInt()) { 0x00u -> State.NOT_REQUIRE_VERIFICATION - 0x01u -> State.REQUIRE_VERIFICATION + 0x01u -> State.REQUIRE_VERIFICATION//需要验证信息 0x99u -> State.ALREADY_ADDED + 0x03u, 0x04u -> State.FAILED - else -> throw IllegalArgumentException(data.contentToString()) + else -> throw IllegalStateException(state.toString()) } } - - @PacketId("00 A7") - class Encrypted(inputStream: ByteReadPacket) : ServerPacket(inputStream) { + @PacketId(0x00_A7u) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { fun decrypt(sessionKey: ByteArray): ServerCanAddFriendResponsePacket { - return ServerCanAddFriendResponsePacket(this.decryptBy(sessionKey)).setId(this.idHex) + return ServerCanAddFriendResponsePacket(decryptBy(sessionKey)).applySequence(sequenceId) } } } @@ -78,16 +83,12 @@ class ServerCanAddFriendResponsePacket(input: ByteReadPacket) : ServerPacket(inp /** * 请求添加好友 */ -@PacketId("00 AE") +@PacketId(0x00_AEu) class ClientAddFriendPacket( val bot: Long, val qq: Long, val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(bot) this.writeHex(TIMProtocol.fixVer2) @@ -109,7 +110,6 @@ class ServerAddGroupResponsePacket(input: ByteReadPacket) : ServerAddContactResp */ abstract class ServerAddContactResponsePacket(input: ByteReadPacket) : ServerPacket(input) { - class Raw(input: ByteReadPacket) : ServerPacket(input) { override fun decode() { @@ -122,7 +122,7 @@ abstract class ServerAddContactResponsePacket(input: ByteReadPacket) : ServerPac } class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey)) + fun decrypt(sessionKey: ByteArray): Raw = Raw(decryptBy(sessionKey)) } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt index ebfb5a796..3333592e3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendFriendMessagePacket.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.action import kotlinx.io.core.* @@ -10,7 +12,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.utils.* -@PacketId("00 CD") +@PacketId(0x00_CDu) class ClientSendFriendMessagePacket( private val botQQ: Long, private val targetQQ: Long, @@ -68,5 +70,5 @@ class ClientSendFriendMessagePacket( } } -@PacketId("00 CD") +@PacketId(0x00_CDu) class ServerSendFriendMessageResponsePacket(input: ByteReadPacket) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt index 66c6b83b1..cc08893b3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ClientSendGroupMessagePacket.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.action import kotlinx.io.core.BytePacketBuilder @@ -11,7 +13,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket import net.mamoe.mirai.utils.* -@PacketId("00 02") +@PacketId(0x00_02u) class ClientSendGroupMessagePacket( private val botQQ: Long, private val groupId: Long,//不是 number @@ -19,7 +21,6 @@ class ClientSendGroupMessagePacket( private val message: MessageChain ) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeRandom(2) this.writeQQ(botQQ) this.writeHex(TIMProtocol.fixVer2) @@ -48,5 +49,5 @@ class ClientSendGroupMessagePacket( } } -@PacketId("00 02") +@PacketId(0x00_02u) class ServerSendGroupMessageResponsePacket(input: ByteReadPacket) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ProfilePicture.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ProfilePicture.kt new file mode 100644 index 000000000..fad6e2f6c --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/ProfilePicture.kt @@ -0,0 +1,18 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + +package net.mamoe.mirai.network.protocol.tim.packet.action + +import kotlinx.io.core.BytePacketBuilder +import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket +import net.mamoe.mirai.network.protocol.tim.packet.PacketId + +// 用户资料的头像 +/** + * 请求获取头像 + */ +@PacketId(0x00_31u) +class ClientProfilePictureRequestPacket : ClientPacket() { + override fun encode(builder: BytePacketBuilder) { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt index ebda936af..baa3ff463 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt @@ -1,4 +1,4 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet.login @@ -12,16 +12,12 @@ import net.mamoe.mirai.utils.* /** * 改变在线状态: "我在线上", "隐身" 等 */ -@PacketId("00 EC") +@PacketId(0x00_ECu) class ClientChangeOnlineStatusPacket( private val qq: Long, private val sessionKey: ByteArray, private val loginStatus: OnlineStatus ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) this.writeHex(TIMProtocol.fixVer2) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientLogin.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/PasswordSubmission.kt similarity index 58% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientLogin.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/PasswordSubmission.kt index 3b4e5914c..3f7322c96 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientLogin.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/PasswordSubmission.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.BytePacketBuilder @@ -10,64 +12,22 @@ import net.mamoe.mirai.utils.* /** - * Password submission (0836_622) + * 提交密码 */ -@PacketId("08 36 31 03") -@Tested -class ClientPasswordSubmissionPacket( - private val qq: Long, - private val password: String, - private val loginTime: Int, - private val loginIP: String, - private val privateKey: ByteArray,//16 random by client - private val token0825: ByteArray,//56 from server - private val randomDeviceName: Boolean -) : ClientPacket() { - - override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(qq) - this.writeHex(TIMProtocol.passwordSubmissionTLV1) - - this.writeShort(25) - this.writeHex(TIMProtocol.publicKey)//25 - - this.writeHex("00 00 00 10") - this.writeHex(TIMProtocol.key0836) - - //TODO shareKey 极大可能为 publicKey, key0836 计算得到 - this.encryptAndWrite(TIMProtocol.shareKey.hexToBytes()) { - writePart1(qq, password, loginTime, loginIP, privateKey, token0825, randomDeviceName) - writePart2() - } - } -} - -//实际上这些包性质都是一样的. 31 04 仅是一个序列 id, 可随机 -//但为简化处理, 特固定这个 id - -@PacketId("08 36 31 04") -class ClientLoginResendPacket3104(qq: Long, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, randomDeviceName: Boolean, tlv0006: IoBuffer? = null) : ClientLoginResendPacket(qq, password, loginTime, loginIP, privateKey, token0825, token00BA, randomDeviceName, tlv0006) - -@PacketId("08 36 31 05") -class ClientLoginResendPacket3105(qq: Long, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, randomDeviceName: Boolean, tlv0006: IoBuffer? = null) : ClientLoginResendPacket(qq, password, loginTime, loginIP, privateKey, token0825, token00BA, randomDeviceName, tlv0006) - -@PacketId("08 36 31 06") -class ClientLoginResendPacket3106(qq: Long, password: String, loginTime: Int, loginIP: String, privateKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, randomDeviceName: Boolean, tlv0006: IoBuffer? = null) : ClientLoginResendPacket(qq, password, loginTime, loginIP, privateKey, token0825, token00BA, randomDeviceName, tlv0006) - - -open class ClientLoginResendPacket constructor( - private val qq: Long, +@PacketId(0x08_36u) +class ClientPasswordSubmissionPacket constructor( + private val bot: Long, private val password: String, private val loginTime: Int, private val loginIP: String, private val privateKey: ByteArray, private val token0825: ByteArray, - private val token00BA: ByteArray, + private val token00BA: ByteArray? = null,// private val randomDeviceName: Boolean = false, private val tlv0006: IoBuffer? = null ) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(qq) + this.writeQQ(bot) this.writeHex(TIMProtocol.passwordSubmissionTLV1) this.writeShort(25) @@ -76,15 +36,18 @@ open class ClientLoginResendPacket constructor( this.writeHex("00 00 00 10")//=16 this.writeHex(TIMProtocol.key0836)//16 + //TODO shareKey 极大可能为 publicKey, key0836 计算得到 this.encryptAndWrite(TIMProtocol.shareKey.hexToBytes()) { - writePart1(qq, password, loginTime, loginIP, privateKey, token0825, randomDeviceName, tlv0006) + writePart1(bot, password, loginTime, loginIP, privateKey, token0825, randomDeviceName, tlv0006) - writeHex("01 10") - writeHex("00 3C") - writeHex("00 01") + if (token00BA != null) { + writeHex("01 10") + writeHex("00 3C") + writeHex("00 01") - writeHex("00 38") - writeFully(token00BA) + writeHex("00 38") + writeFully(token00BA) + } writePart2() } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt index c968cc135..595cfb793 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt @@ -1,27 +1,22 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* /** * SKey 用于 http api */ -@PacketId("00 1D") +@PacketId(0x00_1Du) class ClientSKeyRequestPacket( private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { writeQQ(qq) writeHex(TIMProtocol.fixVer2) @@ -31,15 +26,11 @@ class ClientSKeyRequestPacket( } } -@PacketId("00 1D") +@PacketId(0x00_1Du) class ClientSKeyRefreshmentRequestPacket( private val qq: Long, private val sessionKey: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + getRandomByteArray(2).toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) this.encryptAndWrite(sessionKey) { @@ -48,6 +39,7 @@ class ClientSKeyRefreshmentRequestPacket( } } +@PacketId(0x00_1Du) class ServerSKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) { lateinit var sKey: String @@ -57,7 +49,8 @@ class ServerSKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) { MiraiLogger.logDebug("SKey=$sKey") } - class Encrypted(inputStream: ByteReadPacket) : ServerPacket(inputStream) { - fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket = ServerSKeyResponsePacket(this.decryptBy(sessionKey)).setId(this.idHex) + @PacketId(0x00_1Du) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { + fun decrypt(sessionKey: ByteArray): ServerSKeyResponsePacket = ServerSKeyResponsePacket(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/login/ServerLoginResponse.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt index 0619e8742..bc4403efa 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt @@ -1,17 +1,17 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* import kotlin.properties.Delegates +@PacketId(0x08_36u) sealed class ServerLoginResponsePacket(input: ByteReadPacket) : ServerPacket(input) +@PacketId(0x08_36u) class ServerLoginResponseFailedPacket(val loginResult: LoginResult, input: ByteReadPacket) : ServerLoginResponsePacket(input) /** @@ -19,15 +19,11 @@ class ServerLoginResponseFailedPacket(val loginResult: LoginResult, input: ByteR * * @author NaturalHG */ -@PacketId("08 36 31 03") -class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket, val flag: Flag) : ServerLoginResponsePacket(input) { - enum class Flag { - `08 36 31 03`, - OTHER, - } - +@PacketId(0x08_36u) +class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { lateinit var tlv0006: IoBuffer//120bytes var tokenUnknown: ByteArray? = null + lateinit var privateKeyUpdate: ByteArray//16bytes @Tested @@ -37,24 +33,19 @@ class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket, val flag: Flag this.input.discardExact(4)//00 06 00 78 tlv0006 = this.input.readIoBuffer(0x78) - when (flag) { - Flag.`08 36 31 03` -> {//TODO 在解析时分类而不是在这里 - this.input.discardExact(8)//01 10 00 3C 00 01 00 38 - tokenUnknown = this.input.readBytes(56) - //println(tokenUnknown!!.toUHexString()) - } - - Flag.OTHER -> { - //do nothing in this packet. - //[this.token] will be set in [BotNetworkHandler] - //token - } + //todo 这边原本会判断是否 `08 36 31 03`, 是才会进行下列2行读取. + try { + this.input.discardExact(8)//01 10 00 3C 00 01 00 38 + tokenUnknown = this.input.readBytes(56) + } catch (e: EOFException) { + //什么都不做. 因为有的包就是没有这个数据. } } - class Encrypted(input: ByteReadPacket, private val flag: Flag) : ServerPacket(input) { + @PacketId(0x08_36u) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { @Tested - fun decrypt(privateKey: ByteArray): ServerLoginResponseKeyExchangePacket = ServerLoginResponseKeyExchangePacket(this.decryptBy(TIMProtocol.shareKey, privateKey), flag).setId(this.idHex) + fun decrypt(privateKey: ByteArray): ServerLoginResponseKeyExchangePacket = ServerLoginResponseKeyExchangePacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).applySequence(sequenceId) } } @@ -66,6 +57,7 @@ enum class Gender(val id: Boolean) { /** * @author NaturalHG */ +@PacketId(0x08_36u) class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { lateinit var sessionResponseDecryptionKey: IoBuffer//16 bytes| @@ -87,12 +79,15 @@ class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginRespo discardExact(60)//00 20 01 60 C5 A1 39 7A 12 8E BC 34 C3 56 70 E3 1A ED 20 67 ED A9 DB 06 C1 70 81 3C 01 69 0D FF 63 DA 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 - discardExact(when (val flag = readBytes(2).toUHexString()) { - "01 07" -> 0 - "00 33" -> 28 - "01 10" -> 64 - else -> throw IllegalStateException(flag) - }) + discardExact(when (readUByte().toUInt()) { + 0x00u -> if (readUByte().toUInt() == 0x33u) 28 else null + 0x01u -> when (readUByte().toUInt()) { + 0x07u -> 0 + 0x10u -> 64 + else -> null + } + else -> null + } ?: error("Unknown length flag")) discardExact(23 + 3)//01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D DE 00 09 3A 80 00 @@ -117,8 +112,9 @@ class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginRespo gender = if (readBoolean()) Gender.FEMALE else Gender.MALE } + @PacketId(0x08_36u) class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(privateKey: ByteArray): ServerLoginResponseSuccessPacket = ServerLoginResponseSuccessPacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).setId(this.idHex) + fun decrypt(privateKey: ByteArray): ServerLoginResponseSuccessPacket = ServerLoginResponseSuccessPacket(this.decryptBy(TIMProtocol.shareKey, privateKey)).applySequence(sequenceId) } } @@ -128,7 +124,8 @@ class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginRespo * * @author Him188moe */ -class ServerLoginResponseVerificationCodeInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { +@PacketId(0x08_36u) +class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { lateinit var verifyCodePart1: IoBuffer lateinit var token00BA: ByteArray @@ -151,7 +148,8 @@ class ServerLoginResponseVerificationCodeInitPacket(input: ByteReadPacket) : Ser } + @PacketId(0x08_36u) class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(): ServerLoginResponseVerificationCodeInitPacket = ServerLoginResponseVerificationCodeInitPacket(this.decryptAsByteArray(TIMProtocol.shareKey).toReadPacket()).setId(this.idHex) + fun decrypt(): ServerLoginResponseCaptchaInitPacket = ServerLoginResponseCaptchaInitPacket(decryptAsByteArray(TIMProtocol.shareKey).toReadPacket()).applySequence(sequenceId) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt index e4e28397a..d63511844 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginSuccessPacket.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.ByteReadPacket @@ -9,5 +11,5 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket * * @author Him188moe */ -@PacketId("00 EC") +@PacketId(0x00_ECu) class ServerLoginSuccessPacket(input: ByteReadPacket) : ServerPacket(input) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt index 3d8354ade..56e3dfe70 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt @@ -1,14 +1,13 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* -@PacketId("08 28 04 34") +@PacketId(0x08_28u) class ClientSessionRequestPacket( private val qq: Long, private val serverIp: String, @@ -60,8 +59,8 @@ class ClientSessionRequestPacket( } -@PacketId("08 28 04 34") -class ServerSessionKeyResponsePacket(inputStream: ByteReadPacket) : ServerPacket(inputStream) { +@PacketId(0x08_28u) +class ServerSessionKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) { lateinit var sessionKey: ByteArray lateinit var tlv0105: ByteReadPacket @@ -109,8 +108,9 @@ Discarded(11) =41 01 00 02 03 3C 01 03 00 00 86 } - class Encrypted(inputStream: ByteReadPacket) : ServerPacket(inputStream) { + @PacketId(0x08_28u) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { fun decrypt(sessionResponseDecryptionKey: IoBuffer): ServerSessionKeyResponsePacket = - ServerSessionKeyResponsePacket(this.decryptBy(sessionResponseDecryptionKey)).setId(this.idHex) + ServerSessionKeyResponsePacket(decryptBy(sessionResponseDecryptionKey)).applySequence(sequenceId) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt index 1596f76e3..eaa7febd0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Touch.kt @@ -1,3 +1,5 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.BytePacketBuilder @@ -5,38 +7,29 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact import kotlinx.io.core.readBytes import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* /** * The packet received when logging in, used to redirect server address * - * @see ClientServerRedirectionPacket + * @see ClientTouchRedirectionPacket * @see ClientPasswordSubmissionPacket * * @author Him188moe */ @Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") -@PacketId("08 25 31 01") -class ServerTouchResponsePacket(inputStream: ByteReadPacket) : ServerPacket(inputStream) { +@PacketId(0x08_25u) +class ServerTouchResponsePacket(input: ByteReadPacket) : ServerPacket(input) { var serverIP: String? = null var loginTime: Int = 0 lateinit var loginIP: String lateinit var token0825: ByteArray//56 - enum class Type { - TYPE_08_25_31_01, - TYPE_08_25_31_02, - } - - override fun decode() = with(input) { when (val id = readByte().toUByte().toInt()) { - 0xFE -> {//todo 在 packet 解析时分类而不是在这里分类 + 0xFE -> { discardExact(94) serverIP = readIP() } @@ -55,12 +48,9 @@ class ServerTouchResponsePacket(inputStream: ByteReadPacket) : ServerPacket(inpu } } - class Encrypted(private val type: Type, inputStream: ByteReadPacket) : ServerPacket(inputStream) { - - fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(when (type) { - Type.TYPE_08_25_31_02 -> TIMProtocol.redirectionKey.hexToBytes() - Type.TYPE_08_25_31_01 -> TIMProtocol.touchKey.hexToBytes() - })).setId(this.idHex) + @PacketId(0x08_25u) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { + fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(TIMProtocol.touchKey.hexToBytes())).applySequence(sequenceId) } } @@ -69,17 +59,17 @@ class ServerTouchResponsePacket(inputStream: ByteReadPacket) : ServerPacket(inpu * * @author Him188moe */ -@PacketId("08 25 31 01") -class ClientTouchPacket(private val qq: Long, private val serverIp: String) : ClientPacket() { +@PacketId(0x08_25u) +class ClientTouchPacket(private val bot: Long, private val serverIp: String) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(qq) + this.writeQQ(bot) this.writeHex(TIMProtocol.fixVer) this.writeHex(TIMProtocol.touchKey) this.encryptAndWrite(TIMProtocol.touchKey) { writeHex(TIMProtocol.constantData1) writeHex(TIMProtocol.constantData2) - writeQQ(qq) + writeQQ(bot) writeHex("00 00 00 00 03 09 00 08 00 01") writeIP(serverIp) writeHex("00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19") @@ -93,14 +83,14 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl * * @author Him188moe */ -@PacketId("08 25 31 02") -class ClientServerRedirectionPacket(private val serverIP: String, private val qq: Long) : ClientPacket() { +@PacketId(0x08_25u) +class ClientTouchRedirectionPacket(private val serverIP: String, private val qq: Long) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) this.writeHex(TIMProtocol.fixVer) - this.writeHex(TIMProtocol.redirectionKey) + this.writeHex(TIMProtocol.touchKey)//redirection key - this.encryptAndWrite(TIMProtocol.redirectionKey) { + this.encryptAndWrite(TIMProtocol.touchKey) { this.writeHex(TIMProtocol.constantData1) this.writeHex(TIMProtocol.constantData2) this.writeQQ(qq) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/VerificationCode.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/VerificationCode.kt index 6096a4548..6ef89af28 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/VerificationCode.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/VerificationCode.kt @@ -4,27 +4,19 @@ package net.mamoe.mirai.network.protocol.tim.packet.login import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol -import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket -import net.mamoe.mirai.network.protocol.tim.packet.PacketId -import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket -import net.mamoe.mirai.network.protocol.tim.packet.setId +import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.utils.* /** * 客户端请求验证码图片数据的第几部分 */ -@PacketId("00 BA 31") -class ClientVerificationCodeTransmissionRequestPacket( - private val packetIdLast: Int,//ubyte +@PacketId(0x00_BAu) +class ClientCaptchaTransmissionRequestPacket( private val qq: Long, private val token0825: ByteArray, private val verificationSequence: Int, private val token00BA: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + packetIdLast.toByte().toUHexString() - } - @Tested override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) @@ -50,9 +42,8 @@ class ClientVerificationCodeTransmissionRequestPacket( /** * 提交验证码 */ -@PacketId("00 BA 32") -class ClientVerificationCodeSubmitPacket( - private val packetIdLast: Int,//ubyte +@PacketId(0x00_BAu) +class ClientCaptchaSubmitPacket( private val qq: Long, private val token0825: ByteArray, private val captcha: String, @@ -62,10 +53,6 @@ class ClientVerificationCodeSubmitPacket( require(captcha.length == 4) { "captcha.length must == 4" } } - override val idHex: String by lazy { - super.idHex + " " + packetIdLast.toByte().toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) this.writeHex(TIMProtocol.fixVer) @@ -94,16 +81,11 @@ class ClientVerificationCodeSubmitPacket( /** * 刷新验证码 */ -@PacketId("00 BA 31") -class ClientVerificationCodeRefreshPacket( - private val packetIdLast: Int, +@PacketId(0x00_BAu) +class ClientCaptchaRefreshPacket( private val qq: Long, private val token0825: ByteArray ) : ClientPacket() { - override val idHex: String by lazy { - super.idHex + " " + packetIdLast.toByte().toUHexString() - } - override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeQQ(qq) this.writeHex(TIMProtocol.fixVer) @@ -122,18 +104,12 @@ class ClientVerificationCodeRefreshPacket( } /** - * 验证码输入错误, 同时也会给一部分验证码 - */ -@PacketId("00 BA 32") -class ServerCaptchaWrongPacket(input: ByteReadPacket, packetIdLast: Int) : ServerCaptchaTransmissionPacket(input, packetIdLast) - -/** - * 服务器发送验证码图片文件一部分过来 + * 服务器发送验证码图片文件一部分过来. 当验证码输入错误时, 服务器的返回也会是这个包. * * @author Him188moe */ -@PacketId("00 BA 31") -open class ServerCaptchaTransmissionPacket(input: ByteReadPacket, val packetIdLast: Int) : ServerCaptchaPacket(input) { +@PacketId(0x00_BAu) +open class ServerCaptchaTransmissionPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { lateinit var captchaSectionN: IoBuffer lateinit var verificationToken: IoBuffer//56bytes @@ -160,7 +136,7 @@ open class ServerCaptchaTransmissionPacket(input: ByteReadPacket, val packetIdLa /* fun main() { val data = "13 00 05 01 00 00 01 23 00 38 59 32 29 5A 3E 3D 2D FC F5 22 EB 9E 2D FB 9C 4F AA 06 C8 32 3D F0 3C 2C 2B BA 8D 05 C4 9B C1 74 3B 70 F1 99 90 BB 6E 3E 6F 74 48 97 D3 61 B7 04 C0 A3 F1 DF 40 A4 DC 2B 00 A2 01 2D BB BB E8 FE B8 AF B3 6F 39 7C EA E2 5B 91 BE DB 59 38 CF 58 BC F2 88 F1 09 CF 92 E9 F7 FB 13 76 C5 68 29 23 3F 8E 43 16 2E 50 D7 FA 4D C1 F7 67 EF 27 FB C6 F1 A7 25 A4 BC 45 39 3A EA B2 A5 38 02 FF 4B C9 FF EB BD 89 E5 5D B9 4A 2A BE 5F 52 F1 EB 09 29 CB 3E 66 CF EF 97 89 47 BB 6B E0 7B 4A 3E A1 BC 3F FB F2 0A 83 CB E3 EA B9 43 E1 26 88 03 0B A7 E0 B2 AD 7F 83 CC DA 74 85 83 72 08 EC D2 F9 95 05 15 05 96 F7 1C FF 00 82 C3 90 22 A4 BA 90 D5 00 00 00 00 49 45 4E 44 AE 42 60 82 03 00 00 28 EA 32 5A 85 C8 D2 73 B3 40 39 77 85 65 98 00 FE 03 A2 A5 95 B4 2F E6 79 7A DE 5A 03 10 C8 3D BF 6D 3D 8B 51 84 C2 6D 49 00 10 92 AA 69 FB C6 3D 60 5A 7A A4 AC 7A B0 71 00 36".hexToBytes() - ServerVerificationCodeTransmissionPacket(data.toReadPacket(), data.size, "00 BA 31 01".hexToBytes()).let { + ServerCaptchaTransmissionPacket(data.toReadPacket(), data.size, "00 BA 31 01".hexToBytes()).let { it.dataDecode() println(it.toString()) } @@ -171,7 +147,7 @@ fun main() { * * @author Him188moe */ -@PacketId("00 BA 32") +@PacketId(0x00_BAu) class ServerCaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(input) { lateinit var token00BA: ByteArray//56 bytes @@ -181,22 +157,20 @@ class ServerCaptchaCorrectPacket(input: ByteReadPacket) : ServerCaptchaPacket(in } } +@PacketId(0x00_BAu) abstract class ServerCaptchaPacket(input: ByteReadPacket) : ServerPacket(input) { - @PacketId("00 BA") - class Encrypted(input: ByteReadPacket, override var idHex: String) : ServerPacket(input) { + @PacketId(0x00_BAu) + class Encrypted(input: ByteReadPacket) : ServerPacket(input) { fun decrypt(): ServerCaptchaPacket { val data = this.decryptAsByteArray(TIMProtocol.key00BA) - if (idHex.startsWith("00 BA 32")) { - return when (data.size) { - 66, - 95 -> ServerCaptchaCorrectPacket(data.toReadPacket()) - //66 -> ServerVerificationCodeUnknownPacket(data.toReadPacket()) - else -> return ServerCaptchaWrongPacket(data.toReadPacket(), idHex.substringAfterLast(" ").toInt(16)) - }.setId(this.idHex) - } - return ServerCaptchaTransmissionPacket(data.toReadPacket(), idHex.substringAfterLast(" ").toInt(16)).setId(this.idHex) + return when (data.size) { + 66, + 95 -> ServerCaptchaCorrectPacket(data.toReadPacket()) + //66 -> ServerCaptchaUnknownPacket(data.toReadPacket()) + else -> ServerCaptchaTransmissionPacket(data.toReadPacket()) + }.applySequence(sequenceId) } } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt index baea3b905..2598ff28b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt @@ -1,4 +1,4 @@ -@file:Suppress("EXPERIMENTAL_API_USAGE") +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS") package net.mamoe.mirai.utils @@ -22,30 +22,24 @@ fun ByteReadPacket.readIoBuffer( fun ByteReadPacket.readIoBuffer(n: Number) = this.readIoBuffer(n.toInt()) //必须消耗完 packet -fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {//TODO 优化 +fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { discardExact(3) - val idHex = readInt().toUHexString(" ") + val id = readUShort() + val sequenceId = readUShort() discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8 - return when (idHex) { - "08 25 31 01" -> ServerTouchResponsePacket.Encrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, this) - "08 25 31 02" -> ServerTouchResponsePacket.Encrypted(ServerTouchResponsePacket.Type.TYPE_08_25_31_02, this) - - "08 36 31 03", "08 36 31 04", "08 36 31 05", "08 36 31 06" -> { + return when (id.toUInt()) { + 0x08_25u -> ServerTouchResponsePacket.Encrypted(this) + 0x08_36u -> { when (size) { - 271, 207 -> return ServerLoginResponseKeyExchangePacket.Encrypted(this, when (idHex) { - "08 36 31 03" -> ServerLoginResponseKeyExchangePacket.Flag.`08 36 31 03` - else -> ServerLoginResponseKeyExchangePacket.Flag.OTHER - }).setId(idHex) - 871 -> return ServerLoginResponseVerificationCodeInitPacket.Encrypted(this).setId(idHex) + 271, 207 -> return ServerLoginResponseKeyExchangePacket.Encrypted(this).applySequence(sequenceId) + 871 -> return ServerLoginResponseCaptchaInitPacket.Encrypted(this).applySequence(sequenceId) } - if (size > 700) { - return ServerLoginResponseSuccessPacket.Encrypted(this).setId(idHex) - } + if (size > 700) return ServerLoginResponseSuccessPacket.Encrypted(this).applySequence(sequenceId) - println(size) + println("登录包size=$size") return ServerLoginResponseFailedPacket(when (size) { 135 -> {//包数据错误. 目前怀疑是 tlv0006 this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read { @@ -58,9 +52,9 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {//TODO 优化 319, 351 -> LoginResult.WRONG_PASSWORD //135 -> LoginState.RETYPE_PASSWORD - 63, 279 -> LoginResult.BLOCKED + 63 -> LoginResult.BLOCKED 263 -> LoginResult.UNKNOWN_QQ_NUMBER - 551, 487 -> LoginResult.DEVICE_LOCK + 279, 495, 551, 487 -> LoginResult.DEVICE_LOCK 343, 359 -> LoginResult.TAKEN_BACK else -> LoginResult.UNKNOWN @@ -70,36 +64,24 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {//TODO 优化 351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)") else -> throw IllegalArgumentException(bytes.size.toString())*/ - }, this).setId(idHex) + }, this).applySequence(sequenceId) } + 0x08_28u -> ServerSessionKeyResponsePacket.Encrypted(this) - "08 28 04 34" -> ServerSessionKeyResponsePacket.Encrypted(this) + 0x00_EC_u -> ServerSKeyResponsePacket(this) + 0x00_1D_u -> ServerSKeyResponsePacket.Encrypted(this) + 0x00_5C_u -> ServerAccountInfoResponsePacket.Encrypted(this) + 0x00_58_u -> ServerHeartbeatResponsePacket(this) + 0x00_BA_u -> ServerCaptchaPacket.Encrypted(this) + 0x00_CE_u, 0x00_17_u -> ServerEventPacket.Raw.Encrypted(this) + 0x00_81_u -> ServerFieldOnlineStatusChangedPacket.Encrypted(this) + 0x00_CD_u -> ServerSendFriendMessageResponsePacket(this) + 0x00_02_u -> ServerSendGroupMessageResponsePacket(this) + 0x00_A7_u -> ServerCanAddFriendResponsePacket(this) + 0x03_88_u -> ServerTryGetImageIDResponsePacket.Encrypted(this) - - else -> when (idHex.substring(0, 5)) { - "00 EC" -> ServerLoginSuccessPacket(this) - "00 1D" -> ServerSKeyResponsePacket.Encrypted(this) - "00 5C" -> ServerAccountInfoResponsePacket.Encrypted(this) - - "00 58" -> ServerHeartbeatResponsePacket(this) - - "00 BA" -> ServerCaptchaPacket.Encrypted(this, idHex) - - - "00 CE", "00 17" -> ServerEventPacket.Raw.Encrypted(this) - - "00 81" -> ServerFieldOnlineStatusChangedPacket.Encrypted(this) - - "00 CD" -> ServerSendFriendMessageResponsePacket(this) - "00 02" -> ServerSendGroupMessageResponsePacket(this) - - "00 A7" -> ServerCanAddFriendResponsePacket(this) - - "03 88" -> ServerTryGetImageIDResponsePacket.Encrypted(this) - - else -> UnknownServerPacket.Encrypted(this) - } - }.setId(idHex) + else -> UnknownServerPacket.Encrypted(this, id, sequenceId) + }.applyId(id).applySequence(sequenceId) } fun Input.readIP(): String = buildString(4 + 3) { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt index fbf2c0e75..3290c21fd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PacketBuilderUtil.kt @@ -4,6 +4,7 @@ package net.mamoe.mirai.utils import kotlinx.io.core.* import net.mamoe.mirai.network.protocol.tim.TIMProtocol +import net.mamoe.mirai.network.protocol.tim.packet.PacketId import kotlin.random.Random import kotlin.random.nextInt diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt index 82124e62a..01b5fe094 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt @@ -25,6 +25,10 @@ internal object PacketNameFormatter { private object IgnoreIdList : List by listOf( "idHex", + "id", + "packetId", + "sequenceIdInternal", + "sequenceId", "fixedId", "idByteArray", "encoded", @@ -36,8 +40,7 @@ private object IgnoreIdList : List by listOf( "UninitializedByteReadPacket" ) -@UseExperimental(DangerousInternalIoApi::class) -internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.fixedId})") + this::class.java.allDeclaredFields +internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields .filterNot { it.name in IgnoreIdList || "delegate" in it.name || "$" in it.name } .joinToString(", ", "{", "}") { it.isAccessible = true diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt index fd58c9313..1802a0bd6 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt @@ -19,7 +19,7 @@ actual typealias ClientTryGetImageIDPacket = ClientTryGetImageIDPacketJvm * * @author Him188moe */ -@PacketId("03 88") +@PacketId(0x03_88u) class ClientTryGetImageIDPacketJvm( private val botNumber: Long, private val sessionKey: ByteArray, @@ -29,6 +29,10 @@ class ClientTryGetImageIDPacketJvm( override fun encode(builder: BytePacketBuilder) = with(builder) { this.writeRandom(2) + + //一次 body + //00 00 00 00 00 00 00 00 3C 61 3C 48 85 91 81 B9 DF 27 D9 C3 20 43 F7 1C 73 DA 2A 84 74 AC 78 AC CC 38 54 8F AE 06 8C 22 AA AF 2E C1 E4 70 8C 31 63 52 95 F2 6F C3 9A 2D 77 4B F7 7B 4F C4 1A 6D 7A 3F 22 D8 9D B3 48 99 F3 E7 4F D0 2D 31 94 40 ED A7 5C D9 CE 70 B1 F7 B8 1B 3D CA B3 0E BE 86 33 56 B4 E4 30 AD 66 30 C1 C7 15 6A 71 B6 49 DC DC 0E 74 4B CE 12 3F ED + this.writeQQ(botNumber) this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00") diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt index 731341216..6f022f59b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/CaptchaResolverJvm.kt @@ -27,10 +27,10 @@ internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = cap MiraiLogger.logCyan("需要验证码登录, 验证码为 4 字母") try { File(System.getProperty("user.dir") + "/temp/Captcha.png") - .also { withContext(Dispatchers.IO) { it.createNewFile(); it.writeBytes(captcha) } } + .let { withContext(Dispatchers.IO) { it.createNewFile(); it.writeBytes(captcha) } } MiraiLogger.logCyan("若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png") } catch (e: Exception) { - MiraiLogger.logCyan("无法写出验证码文件, 请尝试查看以上字符图片") + MiraiLogger.logCyan("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片") } MiraiLogger.logCyan("若要更换验证码, 请直接回车") readLine()?.takeUnless { it.isEmpty() || it.length != 4 } @@ -44,15 +44,11 @@ private val captchaLock = Mutex() */ @JvmOverloads internal fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String { - /* - * resize Image - * */ val newHeight = (this.height * (outputWidth.toDouble() / this.width)).toInt() val tmp = this.getScaledInstance(outputWidth, newHeight, Image.SCALE_SMOOTH) val image = BufferedImage(outputWidth, newHeight, BufferedImage.TYPE_INT_ARGB) val g2d = image.createGraphics() g2d.drawImage(tmp, 0, 0, null) - fun gray(rgb: Int): Int { val r = rgb and 0xff0000 shr 16 val g = rgb and 0x00ff00 shr 8 diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt index 4b1bf2a7a..82b2874c1 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt @@ -17,7 +17,7 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv actual suspend fun read(buffer: IoBuffer) = withContext(Dispatchers.IO) { try { (channel as ReadableByteChannel).read(buffer) - } catch (e: Exception) { + } catch (e: Throwable) { throw ReadPacketInternalException(e) } } @@ -26,7 +26,7 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv buffer.readDirect { try { channel.send(it, serverAddress) - } catch (e: Exception) { + } catch (e: Throwable) { throw SendPacketInternalException(e) } } diff --git a/mirai-core/src/jvmTest/kotlin/ImageOutputTest.java b/mirai-core/src/jvmTest/kotlin/ImageOutputTest.java deleted file mode 100644 index 988b28841..000000000 --- a/mirai-core/src/jvmTest/kotlin/ImageOutputTest.java +++ /dev/null @@ -1,14 +0,0 @@ -import net.mamoe.mirai.utils.CharImageConverter; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -public class ImageOutputTest { - public static void main(String[] args) throws IOException { - BufferedImage image = ImageIO.read(new File((System.getProperty("user.dir") + "/mirai.png").replace("//","/"))); - CharImageConverter charImageConvertor = new CharImageConverter(image,80); - System.out.println(charImageConvertor.call()); - } -} diff --git a/mirai-debug/src/main/java/PacketDebuger.kt b/mirai-debug/src/main/java/PacketDebuger.kt index a26080e2c..ad063f000 100644 --- a/mirai-debug/src/main/java/PacketDebuger.kt +++ b/mirai-debug/src/main/java/PacketDebuger.kt @@ -65,7 +65,7 @@ object Main { try { dataSent(pk.data) println() - } catch (e: Exception) { + } catch (e: Throwable) { e.printStackTrace() } } @@ -89,12 +89,20 @@ object Main { * 6. 运行到 `mov eax,dword ptr ss:[ebp+10]` * 7. 查看内存, 从 `eax` 开始的 16 bytes 便是 `sessionKey` */ - val sessionKey: ByteArray = "48 C0 11 42 2D FD 8F 36 6E BA BF FD D3 AA B7 AE".hexToBytes() + val sessionKey: ByteArray = "9E A6 16 46 FF 15 FB 73 2F 31 0D 7E CB C4 E6 49".hexToBytes() fun dataReceived(data: ByteArray) { - //println("--------------") - //println("接收数据包") - //println("raw packet = " + data.toUHexString()) + println("--------------") + println("接收数据包") + //println("raw = " + data.toUHexString()) + data.read { + discardExact(3) + val idHex = readInt().toUHexString(" ") + discardExact(7)//4 for qq number, 3 for 0x00 0x00 0x00. 但更可能是应该 discard 8 + println("id=$idHex") + println("解密body=${this.readRemainingBytes().cutTail(1).decryptBy(sessionKey).toUHexString()}") + } + packetReceived(data.read { this.parseServerPacket(data.size) }) } @@ -108,7 +116,7 @@ object Main { is UnknownServerEventPacket -> { println("--------------") - println("未知事件ID=" + packet.idHex) + println("未知事件ID=" + packet.idHexString) println("未知事件: " + packet.input.readBytes().toUHexString()) } @@ -134,7 +142,7 @@ object Main { discardExact(TIMProtocol.fixVer2.hexToBytes().size + 1 + 5 - 3 + 1) val encryptedBody = readRemainingBytes() - println("body = ${encryptedBody.toUHexString()}") + println("解密body = ${encryptedBody.decryptBy(sessionKey).toUHexString()}") encryptedBody.read { @@ -167,4 +175,9 @@ object Main { } } -} \ No newline at end of file +} + +fun main() { + println("00 01 00 23 24 8B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 4E 22 4E 25 4E 26 4E 27 4E 29 4E 2A 4E 2B 4E 2D 4E 2E 4E 2F 4E 30 4E 31 4E 33 4E 35 4E 36 4E 37 4E 38 4E 3F 4E 40 4E 41 4E 42 4E 43 4E 45 4E 49 4E 4B 4E 4F 4E 54 4E 5B 52 0B 52 0F 5D C2 5D C8 65 97 69 9D 69 A9 9D A5 A4 91 A4 93 A4 94 A4 9C A4 B5".hexToBytes().stringOf()) +} + diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt index 4e45cf487..048990d96 100644 --- a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt +++ b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt @@ -1,6 +1,7 @@ package demo1 import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Group import net.mamoe.mirai.event.events.FriendMessageEvent @@ -16,12 +17,14 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.utils.BotAccount import net.mamoe.mirai.utils.Console import net.mamoe.mirai.utils.MiraiLogger +import kotlin.coroutines.coroutineContext import kotlin.system.exitProcess +import kotlin.system.measureTimeMillis suspend fun main() { val bot = Bot(BotAccount(//填写你的账号 - qqNumber = 1994701121, - password = "abcdefg" + qqNumber = 2903772581, + password = "zxc123456" ), Console()) bot.login { @@ -30,11 +33,10 @@ suspend fun main() { }.let { if (it != LoginResult.SUCCESS) { MiraiLogger.logError("Login failed: " + it.name) - exitProcess(0) + bot.close() } } - //提供泛型以监听事件 subscribeOnce { //获取第一个纯文本消息, 获取不到会抛出 NoSuchElementException @@ -97,7 +99,7 @@ suspend fun main() { fun demo2() { subscribeAlways { event -> if (event.message eq "记笔记") { - FriendMessageEvent::class.subscribeUntilFalse { + subscribeUntilFalse { it.reply("你发送了 ${it.message}") it.message eq "停止"