Commit e129719d authored by Him188moe's avatar Him188moe

Updated robot & network structure

parent c353e8e8
...@@ -10,7 +10,7 @@ class Group(robot: Robot, number: Long) : Contact(robot, number), Closeable { ...@@ -10,7 +10,7 @@ class Group(robot: Robot, number: Long) : Contact(robot, number), Closeable {
val members = ContactList<QQ>() val members = ContactList<QQ>()
override fun sendMessage(message: Message) { override fun sendMessage(message: Message) {
robot.network.packetSystem.sendGroupMessage(this, message) robot.network.messageHandler.sendGroupMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override fun sendXMLMessage(message: String) {
......
...@@ -12,7 +12,7 @@ import net.mamoe.mirai.message.defaults.At ...@@ -12,7 +12,7 @@ import net.mamoe.mirai.message.defaults.At
*/ */
class QQ(robot: Robot, number: Long) : Contact(robot, number) { class QQ(robot: Robot, number: Long) : Contact(robot, number) {
override fun sendMessage(message: Message) { override fun sendMessage(message: Message) {
robot.network.packetSystem.sendFriendMessage(this, message) robot.network.messageHandler.sendFriendMessage(this, message)
} }
override fun sendXMLMessage(message: String) { override fun sendXMLMessage(message: String) {
......
...@@ -7,88 +7,86 @@ import java.util.stream.Collectors ...@@ -7,88 +7,86 @@ import java.util.stream.Collectors
/** /**
* @author Him188moe * @author Him188moe
*/ */
interface Protocol { object Protocol {
companion object { val SERVER_IP: List<String> = object : ArrayList<String>() {
val SERVER_IP: ArrayList<String> = object : ArrayList<String>() { init {
init { add("183.60.56.29")
add("183.60.56.29")
arrayOf( arrayOf(
"sz2.tencent.com", "sz2.tencent.com",
"sz3.tencent.com", "sz3.tencent.com",
"sz4.tencent.com", "sz4.tencent.com",
"sz5.tencent.com", "sz5.tencent.com",
"sz6.tencent.com", "sz6.tencent.com",
"sz8.tencent.com", "sz8.tencent.com",
"sz9.tencent.com" "sz9.tencent.com"
).forEach { this.add(InetAddress.getByName(it).hostAddress) } ).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 key00BA = "C1 9C B8 C8 7B 8C 81 BA 9E 9E 7A 89 E1 7A EC 94"
const val ver = "37 13" const val key00BAFix = "69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"
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 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 * 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"; 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 * 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"; 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 * fix_0836_1
*/ */
const val key0836 = "EF 4A 36 6A 16 A8 E6 3D 2E EA BD 1F 98 C1 3C DA" 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 @ExperimentalUnsignedTypes
fun hexToBytes(hex: String): ByteArray { fun hexToBytes(hex: String): ByteArray {
hex.hashCode().let { id -> hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) { if (hexToByteArrayCacheMap.containsKey(id)) {
return hexToByteArrayCacheMap[id]!!.clone() return hexToByteArrayCacheMap[id]!!.clone()
} else { } else {
hexToUBytes(hex).toByteArray().let { hexToUBytes(hex).toByteArray().let {
hexToByteArrayCacheMap[id] = it.clone(); hexToByteArrayCacheMap[id] = it.clone();
return it return it
}
} }
} }
} }
}
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
fun hexToUBytes(hex: String): UByteArray = Arrays fun hexToUBytes(hex: String): UByteArray = Arrays
.stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) .stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
.map { value -> value.trim { it <= ' ' } } .map { value -> value.trim { it <= ' ' } }
.map { s -> s.toUByte(16) } .map { s -> s.toUByte(16) }
.collect(Collectors.toList()).toUByteArray() .collect(Collectors.toList()).toUByteArray()
}
} }
...@@ -11,15 +11,15 @@ import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePack ...@@ -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.action.ServerSendGroupMessageResponsePacket
import net.mamoe.mirai.network.packet.login.* import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.ClientLoginStatus import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.getGTK
import net.mamoe.mirai.utils.lazyEncode
import java.io.Closeable import java.io.Closeable
import java.net.DatagramPacket import java.net.DatagramPacket
import java.net.DatagramSocket import java.net.DatagramSocket
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.reflect.KClass
/** /**
* A RobotNetworkHandler is used to connect with Tencent servers. * A RobotNetworkHandler is used to connect with Tencent servers.
...@@ -28,329 +28,421 @@ import java.util.concurrent.TimeUnit ...@@ -28,329 +28,421 @@ import java.util.concurrent.TimeUnit
*/ */
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code @Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
internal class RobotNetworkHandler(private val robot: Robot) : Closeable { 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 = "" private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = mapOf(
set(value) { DebugHandler::class to debugHandler,
serverAddress = InetSocketAddress(value, 8000) LoginHandler::class to loginHandler,
field = value MessageHandler::class to messageHandler,
ActionHandler::class to actionHandler
)
restartSocket()
}
private lateinit var serverAddress: InetSocketAddress
private var closed: Boolean = false 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 @ExperimentalUnsignedTypes
private var gtk: Int = 0 fun sendPacket(packet: ClientPacket) {
private var ignoreMessage: Boolean = false socketHandler.sendPacket(packet)
private var loginState: LoginState? = null
set(value) {
field = value
if (value != null) {
loginHook?.invoke(value)
}
}
private var loginHook: ((LoginState) -> Unit)? = null
init {
tlv0105 = 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)
}
} }
/** override fun close() {
* Try to login to server this.packetHandlers.values.forEach {
*/ it.close()
internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) { }
//"14.116.136.106", this.socketHandler.close()
tryLogin()
} }
/**
* Try to login to server
*/
private fun tryLogin(serverAddress: String, loginHook: ((LoginState) -> Unit)? = null) {
touch(serverAddress, loginHook)
}
/** //private | internal
* 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 fun restartSocket() { internal fun tryLogin(loginHook: ((LoginState) -> Unit)? = null) {
socket.close() val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
socket = DatagramSocket((15314 + Math.random() * 100).toInt()) fun login(): Boolean {
socket.connect(this.serverAddress) val ip = ipQueue.poll()
val zeroByte: Byte = 0 return if (ip != null) {
Thread { this@RobotNetworkHandler.socketHandler.touch(ip) { state ->
while (true) { if (state == LoginState.UNKNOWN) {
val dp1 = DatagramPacket(ByteArray(2048), 2048) login()
try { } else {
socket.receive(dp1) loginHook?.invoke(state)
} catch (e: Exception) {
if (e.message == "socket closed") {
if (!closed) {
restartSocket()
}
return@Thread
}
}
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()
} }
} }
} true
}.start() } else false
}
login()
} }
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
internal fun onPacketReceived(packet: ServerPacket) { internal fun onPacketReceived(packet: ServerPacket) {
packet.decode() this.packetHandlers.values.forEach {
MiraiLogger info "Packet received: $packet" it.onPacketReceived(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!! private inner class SocketHandler : Closeable {
//connect(packet.serverIP!!) private lateinit var socket: DatagramSocket
sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, this.robot.account.qqNumber))
} else {//password submission internal var serverIP: String = ""
this.loginIP = packet.loginIP set(value) {
this.loginTime = packet.loginTime serverAddress = InetSocketAddress(value, 8000)
this.token0825 = packet.token0825 field = value
this.tgtgtKey = packet.tgtgtKey
sendPacket(ClientPasswordSubmissionPacket(this.robot.account.qqNumber, this.robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
}
}
is ServerLoginResponseFailedPacket -> { restartSocket()
this.loginState = packet.loginState
MiraiLogger error "Login failed: " + packet.loginState.toString()
return
} }
is ServerLoginResponseVerificationCodeInitPacket -> { private var loginHook: ((LoginState) -> Unit)? = null
//[token00BA]来源之一: 验证码 internal var loginState: LoginState? = null
this.token00BA = packet.token00BA set(value) {
this.verificationCodeCache = packet.verifyCodePart1 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()
}
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
this.verificationCodeSequence = 1
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
} }
}.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))
}
is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么 /**
this.tgtgtKey = packet.tgtgtKeyUpdate * Not async
this.token00BA = packet.token00BA */
sendPacket(ClientLoginResendPacket3105(this.robot.account.qqNumber, this.robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA)) @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()
} }
}
is ServerVerificationCodeTransmissionPacket -> { override fun close() {
this.verificationCodeSequence++ this.socket.close()
this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN this.loginState = null
this.loginHook = null
}
}
this.verificationToken = packet.verificationToken
this.verificationCodeCacheCount++
this.token00BA = packet.token00BA private lateinit var sessionKey: ByteArray
abstract inner class PacketHandler : Closeable {
abstract fun onPacketReceived(packet: ServerPacket)
//todo 看易语言 count 和 sequence 是怎样变化的 override fun close() {
}
}
if (packet.transmissionCompleted) { /**
this.verificationCodeCache * Kind of [PacketHandler] that prints all packets received in the format of hex byte array.
TODO("验证码好了") */
} else { inner class DebugHandler : PacketHandler() {
sendPacket(ClientVerificationCodeTransmissionRequestPacket(this.verificationCodeCacheCount, this.robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA)) 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))
} }
}
}
is ServerLoginResponseSuccessPacket -> { /**
this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey * 处理登录过程
sendPacket(ClientSessionRequestPacket(this.robot.account.qqNumber, this.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105)) */
} 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)
}
//是ClientPasswordSubmissionPacket之后服务器回复的 /**
is ServerLoginResponseResendPacket -> { * 0828_decr_key
//if (packet.tokenUnknown != null) { */
//this.token00BA = packet.token00BA!! private lateinit var sessionResponseDecryptionKey: ByteArray
//println("token00BA changed!!! to " + token00BA.toUByteArray())
//} private var verificationCodeSequence: Int = 0//这两个验证码使用
if (packet.flag == ServerLoginResponseResendPacket.Flag.`08 36 31 03`) { private var verificationCodeCache: ByteArray? = null//每次包只发一部分验证码来
this.tgtgtKey = packet.tgtgtKey private var verificationCodeCacheCount: Int = 1//
sendPacket(ClientLoginResendPacket3104( private lateinit var verificationToken: ByteArray
this.robot.account.qqNumber,
this.robot.account.password,
this.loginTime, private var heartbeatFuture: ScheduledFuture<*>? = null
this.loginIP, private var sKeyRefresherFuture: ScheduledFuture<*>? = null
this.tgtgtKey!!,
this.token0825, override fun onPacketReceived(packet: ServerPacket) {
when (packet.tokenUnknown != null) { when (packet) {
true -> packet.tokenUnknown!! is ServerTouchResponsePacket -> {
false -> this.token00BA if (packet.serverIP != null) {//redirection
}, socketHandler.serverIP = packet.serverIP!!
packet._0836_tlv0006_encr //connect(packet.serverIP!!)
)) sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, robot.account.qqNumber))
} else { } else {//password submission
sendPacket(ClientLoginResendPacket3106( this.loginIP = packet.loginIP
this.robot.account.qqNumber, this.loginTime = packet.loginTime
this.robot.account.password, this.token0825 = packet.token0825
this.loginTime, this.tgtgtKey = packet.tgtgtKey
this.loginIP, sendPacket(ClientPasswordSubmissionPacket(robot.account.qqNumber, robot.account.password, packet.loginTime, packet.loginIP, packet.tgtgtKey, packet.token0825))
this.tgtgtKey!!, }
this.token0825,
when (packet.tokenUnknown != null) {
true -> packet.tokenUnknown!!
false -> this.token00BA
},
packet._0836_tlv0006_encr
))
} }
}
is ServerLoginResponseFailedPacket -> {
socketHandler.loginState = packet.loginState
MiraiLogger error "Login failed: " + packet.loginState.toString()
return
}
is ServerSessionKeyResponsePacket -> { is ServerLoginResponseVerificationCodeInitPacket -> {
this.sessionKey = packet.sessionKey //[token00BA]来源之一: 验证码
MiraiThreadPool.getInstance().scheduleWithFixedDelay({ this.token00BA = packet.token00BA
sendPacket(ClientHeartbeatPacket(this.robot.account.qqNumber, this.sessionKey)) this.verificationCodeCache = packet.verifyCodePart1
}, 90000, 90000, TimeUnit.MILLISECONDS)
RobotLoginSucceedEvent(robot).broadcast()
MiraiThreadPool.getInstance().schedule({
ignoreMessage = false
}, 2, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105 if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
sendPacket(ClientChangeOnlineStatusPacket(this.robot.account.qqNumber, this.sessionKey, ClientLoginStatus.ONLINE)) this.verificationCodeSequence = 1
} sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, robot.account.qqNumber, this.token0825, this.verificationCodeSequence, this.token00BA))
}
}
is ServerLoginSuccessPacket -> { is ServerVerificationCodeRepeatPacket -> {//todo 这个名字正确么
loginState = LoginState.SUCCEED this.tgtgtKey = packet.tgtgtKeyUpdate
sendPacket(ClientSKeyRequestPacket(this.robot.account.qqNumber, this.sessionKey)) this.token00BA = packet.token00BA
} sendPacket(ClientLoginResendPacket3105(robot.account.qqNumber, robot.account.password, this.loginTime, this.loginIP, this.tgtgtKey!!, this.token0825, this.token00BA))
}
is ServerSKeyResponsePacket -> { is ServerVerificationCodeTransmissionPacket -> {
this.sKey = packet.sKey this.verificationCodeSequence++
this.cookies = "uin=o" + this.robot.account.qqNumber + ";skey=" + this.sKey + ";" this.verificationCodeCache = this.verificationCodeCache!! + packet.verificationCodePartN
MiraiThreadPool.getInstance().scheduleWithFixedDelay({ this.verificationToken = packet.verificationToken
sendPacket(ClientSKeyRefreshmentRequestPacket(this.robot.account.qqNumber, this.sessionKey)) this.verificationCodeCacheCount++
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
this.gtk = getGTK(sKey) this.token00BA = packet.token00BA
sendPacket(ClientAccountInfoRequestPacket(this.robot.account.qqNumber, this.sessionKey))
}
is ServerHeartbeatResponsePacket -> {
} //todo 看易语言 count 和 sequence 是怎样变化的
is ServerAccountInfoResponsePacket -> { 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))
}
is ServerFriendMessageEventPacket -> { //是ClientPasswordSubmissionPacket之后服务器回复的
if (ignoreMessage) { is ServerLoginResponseResendPacket -> {
return //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
))
}
} }
FriendMessageEvent(this.robot, this.robot.contacts.getQQ(packet.qq), packet.message) is ServerSessionKeyResponsePacket -> {
} sessionKey = packet.sessionKey
heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(robot.account.qqNumber, sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
is ServerGroupMessageEventPacket -> { RobotLoginSucceedEvent(robot).broadcast()
//todo message chain
//GroupMessageEvent(this.robot, this.robot.contacts.getGroupByNumber(packet.groupNumber), this.robot.contacts.getQQ(packet.qq), packet.message)
}
is UnknownServerEventPacket -> { //登录成功后会收到大量上次的消息, 忽略掉
//unknown message event MiraiThreadPool.getInstance().schedule({
} (packetHandlers[MessageHandler::class] as MessageHandler).ignoreMessage = false
}, 2, TimeUnit.SECONDS)
is UnknownServerPacket -> { 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 ServerGroupUploadFileEventPacket -> { 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 ServerEventPacket.Raw -> onPacketReceived(packet.distribute())
is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt()) is ServerVerificationCodePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt()) is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) is ServerLoginResponseResendPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!)) is ServerLoginResponseSuccessPacket.Encrypted -> onPacketReceived(packet.decrypt(this.tgtgtKey!!))
is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey)) is ServerSessionKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt()) is ServerTouchResponsePacket.Encrypted -> onPacketReceived(packet.decrypt())
is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey)) is ServerSKeyResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey)) is ServerAccountInfoResponsePacket.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(this.sessionKey)) is ServerEventPacket.Raw.Encrypted -> onPacketReceived(packet.decrypt(sessionKey))
is ServerSendFriendMessageResponsePacket, is ServerAccountInfoResponsePacket,
is ServerSendGroupMessageResponsePacket -> { is ServerHeartbeatResponsePacket,
is UnknownServerPacket -> {
//ignored
}
else -> {
}
} }
}
else -> throw IllegalArgumentException(packet.toString()) override fun close() {
this.verificationCodeCache = null
this.tgtgtKey = null
this.heartbeatFuture?.cancel(true)
this.sKeyRefresherFuture?.cancel(true)
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) { fun sendFriendMessage(qq: QQ, message: Message) {
TODO() TODO()
//sendPacket(ClientSendFriendMessagePacket(robot.account.qqNumber, qq.number, sessionKey, message)) //sendPacket(ClientSendFriendMessagePacket(robot.account.qqNumber, qq.number, sessionKey, message))
...@@ -360,31 +452,23 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable { ...@@ -360,31 +452,23 @@ internal class RobotNetworkHandler(private val robot: Robot) : Closeable {
TODO() TODO()
//sendPacket(ClientSendGroupMessagePacket(group.groupId, robot.account.qqNumber, sessionKey, message)) //sendPacket(ClientSendGroupMessagePacket(group.groupId, robot.account.qqNumber, sessionKey, message))
} }
} }
/** /**
* Not async * 动作: 获取好友列表, 点赞, 踢人等.
* 处理动作事件, 承担动作任务.
*/ */
@ExperimentalUnsignedTypes inner class ActionHandler : PacketHandler() {
fun sendPacket(packet: ClientPacket) { internal lateinit var cookies: String
try { internal lateinit var sKey: String
packet.encode() internal var gtk: Int = 0
packet.writeHex(Protocol.tail)
override fun onPacketReceived(packet: ServerPacket) {
val data = packet.toByteArray()
socket.send(DatagramPacket(data, data.size))
MiraiLogger info "Packet sent: $packet"
} catch (e: Throwable) {
e.printStackTrace()
} }
}
override fun close() { override fun close() {
this.socket.close()
this.loginState = null }
this.loginHook = null
this.verificationCodeCache = null
this.tgtgtKey = null
} }
} }
\ No newline at end of file
...@@ -17,6 +17,7 @@ class ClientAccountInfoRequestPacket( ...@@ -17,6 +17,7 @@ class ClientAccountInfoRequestPacket(
) : ClientPacket() { ) : ClientPacket() {
override fun encode() { override fun encode() {
this.writeRandom(2)//part of packet id this.writeRandom(2)//part of packet id
this.writeQQ(qq) this.writeQQ(qq)
this.writeHex(Protocol.fixVer2) this.writeHex(Protocol.fixVer2)
this.encryptAndWrite(sessionKey) { this.encryptAndWrite(sessionKey) {
......
...@@ -53,11 +53,13 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -53,11 +53,13 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
551, 487 -> LoginState.DEVICE_LOCK 551, 487 -> LoginState.DEVICE_LOCK
359 -> LoginState.TAKEN_BACK 359 -> LoginState.TAKEN_BACK
else -> LoginState.UNKNOWN
/*
//unknown //unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)") 63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Illegal package data or 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) }, stream)
} }
...@@ -112,17 +114,6 @@ abstract class ServerPacket(val input: DataInputStream) : Packet { ...@@ -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 @ExperimentalUnsignedTypes
fun DataInputStream.readIP(): String { fun DataInputStream.readIP(): String {
var buff = "" var buff = ""
......
...@@ -14,5 +14,7 @@ enum class LoginState { ...@@ -14,5 +14,7 @@ enum class LoginState {
DEVICE_LOCK,//设备锁 DEVICE_LOCK,//设备锁
TAKEN_BACK,//被回收 TAKEN_BACK,//被回收
// VERIFICATION_CODE,//需要验证码 // VERIFICATION_CODE,//需要验证码
// SUCCEED,
UNKNOWN,
} }
\ No newline at end of file
...@@ -11,9 +11,8 @@ import java.util.Random; ...@@ -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 * @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/ */
public final class TEA { public final class TEA {
public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.shareKey)); public static final TEA CRYPTOR_SHARE_KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.shareKey));
public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key0825)); public static final TEA CRYPTOR_0825KEY = new TEA(Protocol.INSTANCE.hexToBytes(Protocol.key0825));
public static final TEA CRYPTOR_00BAKEY = new TEA(Protocol.Companion.hexToBytes(Protocol.key00BA));
private static final long UINT32_MASK = 0xffffffffL; private static final long UINT32_MASK = 0xffffffffL;
private final long[] mKey; private final long[] mKey;
......
...@@ -79,7 +79,7 @@ operator fun File.plus(child: String): File = File(this, child) ...@@ -79,7 +79,7 @@ operator fun File.plus(child: String): File = File(this, child)
private const val GTK_BASE_VALUE: Int = 5381 private const val GTK_BASE_VALUE: Int = 5381
fun getGTK(sKey: String): Int { internal fun getGTK(sKey: String): Int {
var value = GTK_BASE_VALUE var value = GTK_BASE_VALUE
for (c in sKey.toCharArray()) { for (c in sKey.toCharArray()) {
value += (value shl 5) + c.toInt() value += (value shl 5) + c.toInt()
...@@ -89,7 +89,7 @@ fun getGTK(sKey: String): Int { ...@@ -89,7 +89,7 @@ fun getGTK(sKey: String): Int {
return value 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> { ...@@ -122,3 +122,13 @@ fun Any.getAllDeclaredFields(): List<Field> {
return list 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)
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment