mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-30 19:00:33 +08:00
Rewrite packets
This commit is contained in:
parent
61895b5c8e
commit
09046ae686
@ -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<Socket : DataPacketSocket> : 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()
|
||||
class HandlerClosedException : CancellationException("handler closed")
|
@ -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 <reified P : Pk> 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))
|
||||
|
@ -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] 为常量
|
||||
|
@ -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<AddFriendResult>
|
||||
//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<AddFriendResult>,
|
||||
private val message: Lazy<String>
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val UninitializedPacketId: UShort = 0u
|
@ -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()
|
||||
}
|
||||
|
||||
private fun BytePacketBuilder.writePacketId() {
|
||||
writeUShort(this@ClientPacket.id)
|
||||
writeUShort(sequenceId)
|
||||
}
|
||||
}
|
||||
|
||||
private val UninitializedByteReadPacket = ByteReadPacket(IoBuffer.Empty, IoBuffer.EmptyPool)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
@ -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<PacketId>().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
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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 : ServerPacket> P.setId(idHex: String): P {
|
||||
this.idHex = idHex
|
||||
fun <S : ServerPacket> S.applyId(id: UShort): S {
|
||||
this.id = id
|
||||
return this
|
||||
}
|
||||
|
||||
fun <S : ServerPacket> 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())
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
@ -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)
|
@ -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")
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -25,6 +25,10 @@ internal object PacketNameFormatter {
|
||||
|
||||
private object IgnoreIdList : List<String> by listOf(
|
||||
"idHex",
|
||||
"id",
|
||||
"packetId",
|
||||
"sequenceIdInternal",
|
||||
"sequenceId",
|
||||
"fixedId",
|
||||
"idByteArray",
|
||||
"encoded",
|
||||
@ -36,8 +40,7 @@ private object IgnoreIdList : List<String> 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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -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<FriendMessageEvent> {
|
||||
//获取第一个纯文本消息, 获取不到会抛出 NoSuchElementException
|
||||
@ -97,7 +99,7 @@ suspend fun main() {
|
||||
fun demo2() {
|
||||
subscribeAlways<FriendMessageEvent> { event ->
|
||||
if (event.message eq "记笔记") {
|
||||
FriendMessageEvent::class.subscribeUntilFalse {
|
||||
subscribeUntilFalse<FriendMessageEvent> {
|
||||
it.reply("你发送了 ${it.message}")
|
||||
|
||||
it.message eq "停止"
|
||||
|
Loading…
Reference in New Issue
Block a user