Updated robot & network structure

This commit is contained in:
Him188moe 2019-09-05 20:21:35 +08:00
parent c353e8e8f9
commit e129719d4b
9 changed files with 486 additions and 401 deletions

View File

@ -10,7 +10,7 @@ class Group(robot: Robot, number: Long) : Contact(robot, number), Closeable {
val members = ContactList<QQ>()
override fun sendMessage(message: Message) {
robot.network.packetSystem.sendGroupMessage(this, message)
robot.network.messageHandler.sendGroupMessage(this, message)
}
override fun sendXMLMessage(message: String) {

View File

@ -12,7 +12,7 @@ import net.mamoe.mirai.message.defaults.At
*/
class QQ(robot: Robot, number: Long) : Contact(robot, number) {
override fun sendMessage(message: Message) {
robot.network.packetSystem.sendFriendMessage(this, message)
robot.network.messageHandler.sendFriendMessage(this, message)
}
override fun sendXMLMessage(message: String) {

View File

@ -7,88 +7,86 @@ import java.util.stream.Collectors
/**
* @author Him188moe
*/
interface Protocol {
companion object {
val SERVER_IP: ArrayList<String> = object : ArrayList<String>() {
init {
add("183.60.56.29")
object Protocol {
val SERVER_IP: List<String> = object : ArrayList<String>() {
init {
add("183.60.56.29")
arrayOf(
"sz2.tencent.com",
"sz3.tencent.com",
"sz4.tencent.com",
"sz5.tencent.com",
"sz6.tencent.com",
"sz8.tencent.com",
"sz9.tencent.com"
).forEach { this.add(InetAddress.getByName(it).hostAddress) }
arrayOf(
"sz2.tencent.com",
"sz3.tencent.com",
"sz4.tencent.com",
"sz5.tencent.com",
"sz6.tencent.com",
"sz8.tencent.com",
"sz9.tencent.com"
).forEach { this.add(InetAddress.getByName(it).hostAddress) }
}
}
}
get() = Collections.unmodifiableList(field)
const val head = "02"
const val ver = "37 13"
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00"
const val tail = "03"
/**
* _fixVer
*/
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
/**
* 0825data1
*/
const val constantData0 = "00 18 00 16 00 01 "
/**
* 0825data2
*/
const val constantData1 = "00 00 04 53 00 00 00 01 00 00 15 85 "
const val key0825 = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
const val publicKey = "02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
const val fix0836 = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
const val head = "02"
const val ver = "37 13"
const val fixVer = "03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00"
const val tail = "03"
/**
* _fixVer
*/
const val fixVer2 = "02 00 00 00 01 01 01 00 00 68 20"
/**
* 0825data1
*/
const val constantData0 = "00 18 00 16 00 01 "
/**
* 0825data2
*/
const val constantData1 = "00 00 04 53 00 00 00 01 00 00 15 85 "
const val key0825 = "A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D"
const val redirectionKey = "A8 F2 14 5F 58 12 60 AF 07 63 97 D6 76 B2 1A 3B"
const val publicKey = "02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"
const val shareKey = "1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"
const val fix0836 = "06 A9 12 97 B7 F8 76 25 AF AF D3 EA B4 C8 BC E7 "
const val key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
const val key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
const val encryptKey = "“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”"
const val encryptKey = "“BA 42 FF 01 CF B4 FF D2 12 F0 6E A7 1B 7C B3 08”"
/**
* 0836_622_fix2
*/
const val passwordSubmissionKey2 = "00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B";
/**
* 0836_622_fix1
*/
const val passwordSubmissionKey1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19";
/**
* fix_0836_1
*/
const val key0836 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
/**
* 0836_622_fix2
*/
const val passwordSubmissionKey2 = "00 15 00 30 00 01 01 27 9B C7 F5 00 10 65 03 FD 8B 00 00 00 00 00 00 00 00 00 00 00 00 02 90 49 55 33 00 10 15 74 C4 89 85 7A 19 F5 5E A9 C9 A3 5E 8A 5A 9B";
/**
* 0836_622_fix1
*/
const val passwordSubmissionKey1 = "03 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 01 01 03 00 19";
/**
* fix_0836_1
*/
const val key0836 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA"
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
private val hexToByteArrayCacheMap: MutableMap<Int, ByteArray> = mutableMapOf()
@ExperimentalUnsignedTypes
fun hexToBytes(hex: String): ByteArray {
hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) {
return hexToByteArrayCacheMap[id]!!.clone()
} else {
hexToUBytes(hex).toByteArray().let {
hexToByteArrayCacheMap[id] = it.clone();
return it
}
@ExperimentalUnsignedTypes
fun hexToBytes(hex: String): ByteArray {
hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) {
return hexToByteArrayCacheMap[id]!!.clone()
} else {
hexToUBytes(hex).toByteArray().let {
hexToByteArrayCacheMap[id] = it.clone();
return it
}
}
}
@ExperimentalUnsignedTypes
fun hexToUBytes(hex: String): UByteArray = Arrays
.stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
.map { value -> value.trim { it <= ' ' } }
.map { s -> s.toUByte(16) }
.collect(Collectors.toList()).toUByteArray()
}
@ExperimentalUnsignedTypes
fun hexToUBytes(hex: String): UByteArray = Arrays
.stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
.map { value -> value.trim { it <= ' ' } }
.map { s -> s.toUByte(16) }
.collect(Collectors.toList()).toUByteArray()
}

View File

@ -11,15 +11,15 @@ import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePack
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.ClientLoginStatus
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.getGTK
import net.mamoe.mirai.utils.lazyEncode
import net.mamoe.mirai.utils.*
import java.io.Closeable
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
/**
* A RobotNetworkHandler is used to connect with Tencent servers.
@ -28,329 +28,421 @@ import java.util.concurrent.TimeUnit
*/
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
private val socketHandler: SocketHandler = SocketHandler()
private var socket: DatagramSocket = DatagramSocket((15314 + Math.random() * 100).toInt())
val debugHandler = DebugHandler()
val loginHandler = LoginHandler()
val messageHandler = MessageHandler()
val actionHandler = ActionHandler()
private var serverIP: String = ""
set(value) {
serverAddress = InetSocketAddress(value, 8000)
field = value
private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = mapOf(
DebugHandler::class to debugHandler,
LoginHandler::class to loginHandler,
MessageHandler::class to messageHandler,
ActionHandler::class to actionHandler
)
restartSocket()
}
private lateinit var serverAddress: InetSocketAddress
private var closed: Boolean = false
private lateinit var token00BA: ByteArray //这些数据全部是login用的
private lateinit var token0825: ByteArray
private var loginTime: Int = 0
private lateinit var loginIP: String
private var tgtgtKey: ByteArray? = null
private var tlv0105: ByteArray
/**
* 0828_decr_key
*/
private lateinit var sessionResponseDecryptionKey: ByteArray
private var verificationCodeSequence: Int = 0//这两个验证码使用
private var verificationCodeCache: ByteArray? = null//每次包只发一部分验证码来
private var verificationCodeCacheCount: Int = 1//
private lateinit var verificationToken: ByteArray
private lateinit var sessionKey: ByteArray//这两个是登录成功后得到的
private lateinit var sKey: String
/**
* Used to access web API(for friends list etc.)
* Not async
*/
private lateinit var cookies: String
private var gtk: Int = 0
private var ignoreMessage: Boolean = false
@ExperimentalUnsignedTypes
fun sendPacket(packet: ClientPacket) {
socketHandler.sendPacket(packet)
}
private var loginState: LoginState? = null
set(value) {
field = value
if (value != null) {
loginHook?.invoke(value)
override fun close() {
this.packetHandlers.values.forEach {
it.close()
}
this.socketHandler.close()
}
//private | internal
internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) {
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
fun login(): Boolean {
val ip = ipQueue.poll()
return if (ip != null) {
this@RobotNetworkHandler.socketHandler.touch(ip) { state ->
if (state == LoginState.UNKNOWN) {
login()
} else {
loginHook?.invoke(state)
}
}
true
} else false
}
login()
}
@ExperimentalUnsignedTypes
internal fun onPacketReceived(packet: ServerPacket) {
this.packetHandlers.values.forEach {
it.onPacketReceived(packet)
}
}
private inner class SocketHandler : Closeable {
private lateinit var socket: DatagramSocket
internal var serverIP: String = ""
set(value) {
serverAddress = InetSocketAddress(value, 8000)
field = value
restartSocket()
}
private var loginHook: ((LoginState) -> Unit)? = null
internal var loginState: LoginState? = null
set(value) {
field = value
if (value != null && value != LoginState.UNKNOWN) {
loginHook?.invoke(value)
}
}
private lateinit var serverAddress: InetSocketAddress
private fun restartSocket() {
socket = DatagramSocket((15314 + Math.random() * 100).toInt())
socket.close()
socket.connect(this.serverAddress)
Thread {
while (socket.isConnected) {
val packet = DatagramPacket(ByteArray(2048), 2048)
kotlin
.runCatching { socket.receive(packet) }
.onSuccess {
MiraiThreadPool.getInstance().submit {
try {
onPacketReceived(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
} catch (e: Exception) {
e.printStackTrace()
}
}
}.onFailure {
if (it.message == "socket closed") {
if (!closed) {
restartSocket()
}
return@Thread
}
it.printStackTrace()
}
}
}.start()
}
/**
* Start network and touch the server
*/
internal fun touch(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
socketHandler.serverIP = serverAddress
if (loginHook != null) {
this.loginHook = loginHook
}
sendPacket(ClientTouchPacket(robot.account.qqNumber, socketHandler.serverIP))
}
/**
* Not async
*/
@ExperimentalUnsignedTypes
internal fun sendPacket(packet: ClientPacket) {
try {
packet.encode()
packet.writeHex(Protocol.tail)
val data = packet.toByteArray()
socket.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: $packet"
} catch (e: Throwable) {
e.printStackTrace()
}
}
private var loginHook: ((LoginState) -> Unit)? = null
override fun close() {
this.socket.close()
this.loginState = null
this.loginHook = null
}
}
init {
tlv0105 = lazyEncode {
private lateinit var sessionKey: ByteArray
abstract inner class PacketHandler : Closeable {
abstract fun onPacketReceived(packet: ServerPacket)
override fun close() {
}
}
/**
* Kind of [PacketHandler] that prints all packets received in the format of hex byte array.
*/
inner class DebugHandler : PacketHandler() {
override fun onPacketReceived(packet: ServerPacket) {
packet.decode()
MiraiLogger info "Packet received: $packet"
if (packet is ServerEventPacket) {
sendPacket(ClientMessageResponsePacket(robot.account.qqNumber, packet.packetId, sessionKey, packet.eventIdentity))
}
}
}
/**
* 处理登录过程
*/
inner class LoginHandler : PacketHandler() {
private lateinit var token00BA: ByteArray
private lateinit var token0825: ByteArray
private var loginTime: Int = 0
private lateinit var loginIP: String
private var tgtgtKey: ByteArray? = null
private var tlv0105: ByteArray = lazyEncode {
it.writeHex("01 05 00 30")
it.writeHex("00 01 01 02 00 14 01 01 00 10")
it.writeRandom(16)
it.writeHex("00 14 01 02 00 10")
it.writeRandom(16)
}
}
/**
* Try to login to server
*/
internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) {
//"14.116.136.106",
tryLogin()
}
/**
* 0828_decr_key
*/
private lateinit var sessionResponseDecryptionKey: ByteArray
/**
* Try to login to server
*/
private fun tryLogin(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
private var verificationCodeSequence: Int = 0//这两个验证码使用
private var verificationCodeCache: ByteArray? = null//每次包只发一部分验证码来
private var verificationCodeCacheCount: Int = 1//
private lateinit var verificationToken: ByteArray
touch(serverAddress, loginHook)
}
/**
* Start network
*/
private fun touch(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
serverIP = serverAddress
if (loginHook != null) {
this.loginHook = loginHook
}
this.sendPacket(ClientTouchPacket(this.robot.account.qqNumber, this.serverIP))
}
private var heartbeatFuture: ScheduledFuture<*>? = null
private var sKeyRefresherFuture: ScheduledFuture<*>? = null
private fun restartSocket() {
socket.close()
socket = DatagramSocket((15314 + Math.random() * 100).toInt())
socket.connect(this.serverAddress)
val zeroByte: Byte = 0
Thread {
while (true) {
val dp1 = DatagramPacket(ByteArray(2048), 2048)
try {
socket.receive(dp1)
} catch (e: Exception) {
if (e.message == "socket closed") {
if (!closed) {
restartSocket()
}
return@Thread
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerTouchResponsePacket -> {
if (packet.serverIP != null) {//redirection
socketHandler.serverIP = packet.serverIP!!
//connect(packet.serverIP!!)
sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, robot.account.qqNumber))
} else {//password submission
this.loginIP = packet.loginIP
this.loginTime = packet.loginTime
this.token0825 = packet.token0825
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientPasswordSubmissionPacket(robot.account.qqNumber, robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
}
}
MiraiThreadPool.getInstance().submit {
var i = dp1.data.size - 1
while (dp1.data[i] == zeroByte) {
--i
}
try {
onPacketReceived(ServerPacket.ofByteArray(dp1.data.copyOfRange(0, i + 1)))
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}.start()
}
@ExperimentalUnsignedTypes
internal fun onPacketReceived(packet: ServerPacket) {
packet.decode()
MiraiLogger info "Packet received: $packet"
if (packet is ServerEventPacket) {
sendPacket(ClientMessageResponsePacket(this.robot.account.qqNumber, packet.packetId, this.sessionKey, packet.eventIdentity))
}
when (packet) {
is ServerTouchResponsePacket -> {
if (packet.serverIP != null) {//redirection
serverIP = packet.serverIP!!
//connect(packet.serverIP!!)
sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, this.robot.account.qqNumber))
} else {//password submission
this.loginIP = packet.loginIP
this.loginTime = packet.loginTime
this.token0825 = packet.token0825
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientPasswordSubmissionPacket(this.robot.account.qqNumber, this.robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
}
}
is ServerLoginResponseFailedPacket -> {
this.loginState = packet.loginState
MiraiLogger error "Login failed: " + packet.loginState.toString()
return
}
is ServerLoginResponseVerificationCodeInitPacket -> {
//[token00BA]来源之一: 验证码
this.token00BA = packet.token00BA
this.verificationCodeCache = packet.verifyCodePart1
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
this.verificationCodeSequence = 1
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
}
}
is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么
this.tgtgtKey = packet.tgtgtKeyUpdate
this.token00BA = packet.token00BA
sendPacket(ClientLoginResendPacket3105(this.robot.account.qqNumber, this.robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA))
}
is ServerVerificationCodeTransmissionPacket -> {
this.verificationCodeSequence++
this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
this.verificationToken = packet.verificationToken
this.verificationCodeCacheCount++
this.token00BA = packet.token00BA
//todo 看易语言 count 和 sequence 是怎样变化的
if (packet.transmissionCompleted) {
this.verificationCodeCache
TODO("验证码好了")
} else {
sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
}
}
is ServerLoginResponseSuccessPacket -> {
this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey
sendPacket(ClientSessionRequestPacket(this.robot.account.qqNumber, this.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
}
//是ClientPasswordSubmissionPacket之后服务器回复的
is ServerLoginResponseResendPacket -> {
//if (packet.tokenUnknown != null) {
//this.token00BA = packet.token00BA!!
//println("token00BA changed!!! to " + token00BA.toUByteArray())
//}
if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) {
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientLoginResendPacket3104(
this.robot.account.qqNumber,
this.robot.account.password,
this.loginTime,
this.loginIP,
this.tgtgtKey!!,
this.token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
} else {
sendPacket(ClientLoginResendPacket3106(
this.robot.account.qqNumber,
this.robot.account.password,
this.loginTime,
this.loginIP,
this.tgtgtKey!!,
this.token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
}
}
is ServerSessionKeyResponsePacket -> {
this.sessionKey = packet.sessionKey
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(this.robot.account.qqNumber, this.sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
RobotLoginSucceedEvent(robot).broadcast()
MiraiThreadPool.getInstance().schedule({
ignoreMessage = false
}, 2, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105
sendPacket(ClientChangeOnlineStatusPacket(this.robot.account.qqNumber, this.sessionKey, ClientLoginStatus.ONLINE))
}
is ServerLoginSuccessPacket -> {
loginState = LoginState.SUCCEED
sendPacket(ClientSKeyRequestPacket(this.robot.account.qqNumber, this.sessionKey))
}
is ServerSKeyResponsePacket -> {
this.sKey = packet.sKey
this.cookies = "uin=o" + this.robot.account.qqNumber + ";skey=" + this.sKey + ";"
MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientSKeyRefreshmentRequestPacket(this.robot.account.qqNumber, this.sessionKey))
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
this.gtk = getGTK(sKey)
sendPacket(ClientAccountInfoRequestPacket(this.robot.account.qqNumber, this.sessionKey))
}
is ServerHeartbeatResponsePacket -> {
}
is ServerAccountInfoResponsePacket -> {
}
is ServerFriendMessageEventPacket -> {
if (ignoreMessage) {
is ServerLoginResponseFailedPacket -> {
socketHandler.loginState = packet.loginState
MiraiLogger error "Login failed: " + packet.loginState.toString()
return
}
FriendMessageEvent(this.robot, this.robot.contacts.getQQ(packet.qq), packet.message)
is ServerLoginResponseVerificationCodeInitPacket -> {
//[token00BA]来源之一: 验证码
this.token00BA = packet.token00BA
this.verificationCodeCache = packet.verifyCodePart1
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
this.verificationCodeSequence = 1
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
}
}
is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么
this.tgtgtKey = packet.tgtgtKeyUpdate
this.token00BA = packet.token00BA
sendPacket(ClientLoginResendPacket3105(robot.account.qqNumber, robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA))
}
is ServerVerificationCodeTransmissionPacket -> {
this.verificationCodeSequence++
this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
this.verificationToken = packet.verificationToken
this.verificationCodeCacheCount++
this.token00BA = packet.token00BA
//todo 看易语言 count 和 sequence 是怎样变化的
if (packet.transmissionCompleted) {
this.verificationCodeCache
TODO("验证码好了")
} else {
sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
}
}
is ServerLoginResponseSuccessPacket -> {
this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey
sendPacket(ClientSessionRequestPacket(robot.account.qqNumber, socketHandler.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
}
//是ClientPasswordSubmissionPacket之后服务器回复的
is ServerLoginResponseResendPacket -> {
//if (packet.tokenUnknown != null) {
//this.token00BA = packet.token00BA!!
//println("token00BA changed!!! to " + token00BA.toUByteArray())
//}
if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) {
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientLoginResendPacket3104(
robot.account.qqNumber,
robot.account.password,
this.loginTime,
this.loginIP,
this.tgtgtKey!!,
this.token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
} else {
sendPacket(ClientLoginResendPacket3106(
robot.account.qqNumber,
robot.account.password,
this.loginTime,
this.loginIP,
this.tgtgtKey!!,
this.token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
}
}
is ServerSessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(robot.account.qqNumber, sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
RobotLoginSucceedEvent(robot).broadcast()
//登录成功后会收到大量上次的消息, 忽略掉
MiraiThreadPool.getInstance().schedule({
(packetHandlers[MessageHandler::class] as MessageHandler).ignoreMessage = false
}, 2, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105
sendPacket(ClientChangeOnlineStatusPacket(robot.account.qqNumber, sessionKey, ClientLoginStatus.ONLINE))
}
is ServerLoginSuccessPacket -> {
socketHandler.loginState = LoginState.SUCCEED
sendPacket(ClientSKeyRequestPacket(robot.account.qqNumber, sessionKey))
}
is ServerSKeyResponsePacket -> {
val actionHandler = packetHandlers[ActionHandler::class] as ActionHandler
actionHandler.sKey = packet.sKey
actionHandler.cookies = "uin=o" + robot.account.qqNumber + ";skey=" + actionHandler.sKey + ";"
sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientSKeyRefreshmentRequestPacket(robot.account.qqNumber, sessionKey))
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
actionHandler.gtk = getGTK(actionHandler.sKey)
sendPacket(ClientAccountInfoRequestPacket(robot.account.qqNumber, sessionKey))
}
is ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket,
is ServerHeartbeatResponsePacket,
is UnknownServerPacket -> {
//ignored
}
else -> {
}
}
}
is ServerGroupMessageEventPacket -> {
//todo message chain
//GroupMessageEvent(this.robot, this.robot.contacts.getGroupByNumber(packet.groupNumber), this.robot.contacts.getQQ(packet.qq), packet.message)
}
override fun close() {
this.verificationCodeCache = null
this.tgtgtKey = null
is UnknownServerEventPacket -> {
//unknown message event
}
this.heartbeatFuture?.cancel(true)
this.sKeyRefresherFuture?.cancel(true)
is UnknownServerPacket -> {
}
is ServerGroupUploadFileEventPacket -> {
}
is ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey))
is ServerSendFriendMessageResponsePacket,
is ServerSendGroupMessageResponsePacket -> {
}
else -> throw IllegalArgumentException(packet.toString())
this.heartbeatFuture = null
this.sKeyRefresherFuture = null
}
}
internal val packetSystem: PacketSystem = PacketSystem()
/**
* 处理消息事件, 承担消息发送任务.
*/
inner class MessageHandler : PacketHandler() {
internal var ignoreMessage: Boolean = false
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerGroupUploadFileEventPacket -> {
//todo
}
is ServerFriendMessageEventPacket -> {
if (ignoreMessage) {
return
}
FriendMessageEvent(robot, robot.contacts.getQQ(packet.qq), packet.message)
}
is ServerGroupMessageEventPacket -> {
//todo message chain
//GroupMessageEvent(this.robot, robot.contacts.getGroupByNumber(packet.groupNumber), robot.contacts.getQQ(packet.qq), packet.message)
}
is UnknownServerEventPacket,
is ServerSendFriendMessageResponsePacket,
is ServerSendGroupMessageResponsePacket -> {
//ignored
}
else -> {
//ignored
}
}
}
inner class PacketSystem {
fun sendFriendMessage(qq: QQ, message: Message) {
TODO()
//sendPacket(ClientSendFriendMessagePacket(robot.account.qqNumber, qq.number, sessionKey, message))
@ -360,31 +452,23 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
TODO()
//sendPacket(ClientSendGroupMessagePacket(group.groupId, robot.account.qqNumber, sessionKey, message))
}
}
/**
* Not async
* 动作: 获取好友列表, 点赞, 踢人等.
* 处理动作事件, 承担动作任务.
*/
@ExperimentalUnsignedTypes
fun sendPacket(packet: ClientPacket) {
try {
packet.encode()
packet.writeHex(Protocol.tail)
inner class ActionHandler : PacketHandler() {
internal lateinit var cookies: String
internal lateinit var sKey: String
internal var gtk: Int = 0
override fun onPacketReceived(packet: ServerPacket) {
}
override fun close() {
val data = packet.toByteArray()
socket.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: $packet"
} catch (e: Throwable) {
e.printStackTrace()
}
}
override fun close() {
this.socket.close()
this.loginState = null
this.loginHook = null
this.verificationCodeCache = null
this.tgtgtKey = null
}
}
}

View File

@ -17,6 +17,7 @@ class ClientAccountInfoRequestPacket(
) : ClientPacket() {
override fun encode() {
this.writeRandom(2)//part of packet id
this.writeQQ(qq)
this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) {

View File

@ -53,11 +53,13 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
551, 487 -> LoginState.DEVICE_LOCK
359 -> LoginState.TAKEN_BACK
else -> LoginState.UNKNOWN
/*
//unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data or Unknown error)")//包数据有误
else -> throw IllegalArgumentException(bytes.size.toString())
else -> throw IllegalArgumentException(bytes.size.toString())*/
}, stream)
}
@ -112,17 +114,6 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
}
fun DataInputStream.readUntil(byte: Byte): ByteArray {
var buff = byteArrayOf()
var b: Byte
b = readByte()
while (b != byte) {
buff += b
b = readByte()
}
return buff
}
@ExperimentalUnsignedTypes
fun DataInputStream.readIP(): String {
var buff = ""

View File

@ -14,5 +14,7 @@ enum class LoginState {
DEVICE_LOCK,//设备锁
TAKEN_BACK,//被回收
// VERIFICATION_CODE,//需要验证码
// SUCCEED,
UNKNOWN,
}

View File

@ -11,9 +11,8 @@ import java.util.Random;
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/
public final class TEA {
public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.shareKey));
public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key0825));
public static final TEA CRYPTOR_00BAKEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key00BA));
public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.shareKey));
public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.key0825));
private static final long UINT32_MASK = 0xffffffffL;
private final long[] mKey;

View File

@ -79,7 +79,7 @@ operator fun File.plus(child: String): File = File(this, child)
private const val GTK_BASE_VALUE: Int = 5381
fun getGTK(sKey: String): Int {
internal fun getGTK(sKey: String): Int {
var value = GTK_BASE_VALUE
for (c in sKey.toCharArray()) {
value += (value shl 5) + c.toInt()
@ -89,7 +89,7 @@ fun getGTK(sKey: String): Int {
return value
}
fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
internal fun getCrc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
/**
@ -122,3 +122,13 @@ fun Any.getAllDeclaredFields(): List<Field> {
return list
}
private const val ZERO_BYTE: Byte = 0
fun ByteArray.removeZeroTail(): ByteArray {
var i = this.size - 1
while (this[i] == ZERO_BYTE) {
--i
}
return this.copyOfRange(0, i + 1)
}