1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-03-30 01:40:29 +08:00

Updated network

This commit is contained in:
Him188moe 2019-09-13 11:28:41 +08:00
parent d19216a65b
commit 4a319bf589
12 changed files with 860 additions and 747 deletions

View File

@ -0,0 +1,428 @@
@file:JvmMultifileClass
@file:JvmName("BotNetworkHandler")
package net.mamoe.mirai.network
import net.mamoe.mirai.Bot
import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.event.events.bot.BotLoginSucceedEvent
import net.mamoe.mirai.event.events.network.BeforePacketSendEvent
import net.mamoe.mirai.event.events.network.PacketSentEvent
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.network.BotNetworkHandler.Login
import net.mamoe.mirai.network.BotNetworkHandler.SocketHandler
import net.mamoe.mirai.network.handler.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
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.CompletableFuture
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import javax.imageio.ImageIO
import kotlin.reflect.KClass
/**
* Mirai 的网络处理器, 它处理所有数据包([Packet])的发送和接收.
* [BotNetworkHandler] 是全程异步和线程安全的.
*
* [BotNetworkHandler] 2 个模块构成:
* - [SocketHandler]: 处理数据包底层的发送([ByteArray])
* - [PacketHandler]: 制作 [Packet] 并传递给 [SocketHandler] 继续处理; 分析来自服务器的数据包并处理
*
* 其中, [PacketHandler] 4 个子模块构成:
* - [DebugHandler] 输出 [Packet.toString]
* - [Login] 处理 touch/login/verification code 相关
* - [MessageHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
* - [ActionHandler] 处理动作相关(踢人/加入群/好友列表等)
*
* A BotNetworkHandler is used to connect with Tencent servers.
*
* @author Him188moe
*/
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
class BotNetworkHandler(private val bot: Bot) : Closeable {
private val socket: SocketHandler = SocketHandler()
fun getSocket(): DataPacketSocket {
return socket
}
lateinit var debugHandler: DebugHandler
lateinit var login: Login
lateinit var messageHandler: MessageHandler
lateinit var actionHandler: ActionHandler
val packetHandlers: PacketHandlerList = PacketHandlerList()
//private | internal
/**
* 尝试登录
*
* @param touchingTimeoutMillis 连接每个服务器的 timeout
*/
@JvmOverloads
internal fun tryLogin(touchingTimeoutMillis: Long = 200): CompletableFuture<LoginState> {
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
val future = CompletableFuture<LoginState>()
fun login() {
this.socket.close()
val ip = ipQueue.poll()
if (ip == null) {
future.complete(LoginState.UNKNOWN)//所有服务器均返回 UNKNOWN
return
}
this@BotNetworkHandler.socket.touch(ip, touchingTimeoutMillis).get().let { state ->
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
login()//超时或未知, 重试连接下一个服务器
} else {
future.complete(state)
}
}
}
login()
return future
}
private lateinit var sessionKey: ByteArray
override fun close() {
this.packetHandlers.forEach {
it.instance.close()
}
this.socket.close()
}
private inner class SocketHandler : Closeable, DataPacketSocket {
override fun distributePacket(packet: ServerPacket) {
try {
packet.decode()
} catch (e: java.lang.Exception) {
e.printStackTrace()
bot.debugPacket(packet)
return
}
if (ServerPacketReceivedEvent(bot, packet).broadcast().isCancelled) {
debugHandler.onPacketReceived(packet)
return
}
login.onPacketReceived(packet)
packetHandlers.forEach {
it.instance.onPacketReceived(packet)
}
}
private var socket: DatagramSocket? = null
internal var serverIP: String = ""
set(value) {
field = value
restartSocket()
}
internal var loginFuture: CompletableFuture<LoginState>? = null
@Synchronized
private fun restartSocket() {
socket?.close()
socket = DatagramSocket(0)
socket!!.connect(InetSocketAddress(serverIP, 8000))
Thread {
while (socket!!.isConnected) {
val packet = DatagramPacket(ByteArray(2048), 2048)
kotlin.runCatching { socket?.receive(packet) }
.onSuccess {
MiraiThreadPool.getInstance().submit {
try {
distributePacket(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
} catch (e: Exception) {
e.printStackTrace()
}
}
}.onFailure {
if (it.message == "Socket closed" || it.message == "socket closed") {
return@Thread
}
it.printStackTrace()
}
}
}.start()
}
/**
* Start network and touch the server
*/
fun touch(serverAddress: String, timeoutMillis: Long): CompletableFuture<LoginState> {
bot.info("Connecting server: $serverAddress")
if (this@BotNetworkHandler::login.isInitialized) {
login.close()
}
login = Login()
this.loginFuture = CompletableFuture()
serverIP = serverAddress
waitForPacket(ServerPacket::class, timeoutMillis) {
loginFuture!!.complete(LoginState.TIMEOUT)
}
sendPacket(ClientTouchPacket(bot.account.qqNumber, serverIP))
return this.loginFuture!!
}
/**
* Not async
*/
@Synchronized
@ExperimentalUnsignedTypes
override fun sendPacket(packet: ClientPacket) {
checkNotNull(socket) { "network closed" }
if (socket!!.isClosed) {
return
}
try {
packet.encodePacket()
if (BeforePacketSendEvent(bot, packet).broadcast().isCancelled) {
return
}
val data = packet.toByteArray()
socket!!.send(DatagramPacket(data, data.size))
bot.cyanL("Packet sent: $packet")
PacketSentEvent(bot, packet).broadcast()
} catch (e: Throwable) {
e.printStackTrace()
}
}
@Suppress("UNCHECKED_CAST")
internal fun <P : ServerPacket> waitForPacket(packetClass: KClass<P>, timeoutMillis: Long, timeout: () -> Unit) {
var got = false
ServerPacketReceivedEvent::class.hookWhile {
if (packetClass.isInstance(it.packet) && it.bot === bot) {
got = true
true
} else {
false
}
}
MiraiThreadPool.getInstance().submit {
val startingTime = System.currentTimeMillis()
while (!got) {
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
timeout.invoke()
return@submit
}
Thread.sleep(10)
}
}
}
override fun close() {
this.socket?.close()
if (this.loginFuture != null) {
if (!this.loginFuture!!.isDone) {
this.loginFuture!!.cancel(true)
}
this.loginFuture = null
}
}
override fun isClosed(): Boolean {
return this.socket?.isClosed ?: true
}
}
/**
* 处理登录过程
*/
inner class Login : Closeable {
private lateinit var token00BA: ByteArray
private lateinit var token0825: ByteArray
private var loginTime: Int = 0
private lateinit var loginIP: String
private var tgtgtKey: ByteArray = getRandomByteArray(16)
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)
}
/**
* 0828_decr_key
*/
private lateinit var sessionResponseDecryptionKey: ByteArray
private var captchaSectionId: Int = 1
private var captchaCache: ByteArray? = byteArrayOf()//每次包只发一部分验证码来
private var heartbeatFuture: ScheduledFuture<*>? = null
fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerTouchResponsePacket -> {
if (packet.serverIP != null) {//redirection
socket.serverIP = packet.serverIP!!
//connect(packet.serverIP!!)
socket.sendPacket(ClientServerRedirectionPacket(packet.serverIP!!, bot.account.qqNumber))
} else {//password submission
this.loginIP = packet.loginIP
this.loginTime = packet.loginTime
this.token0825 = packet.token0825
socket.sendPacket(ClientPasswordSubmissionPacket(bot.account.qqNumber, bot.account.password, packet.loginTime, packet.loginIP, this.tgtgtKey, packet.token0825))
}
}
is ServerLoginResponseFailedPacket -> {
socket.loginFuture?.complete(packet.loginState)
return
}
is ServerVerificationCodeCorrectPacket -> {
this.tgtgtKey = getRandomByteArray(16)
this.token00BA = packet.token00BA
socket.sendPacket(ClientLoginResendPacket3105(bot.account.qqNumber, bot.account.password, this.loginTime, this.loginIP, this.tgtgtKey, this.token0825, this.token00BA))
}
is ServerLoginResponseVerificationCodeInitPacket -> {
//[token00BA]来源之一: 验证码
this.token00BA = packet.token00BA
this.captchaCache = packet.verifyCodePart1
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
this.captchaSectionId = 1
socket.sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, bot.account.qqNumber, this.token0825, this.captchaSectionId++, this.token00BA))
}
}
is ServerVerificationCodeTransmissionPacket -> {
if (packet is ServerVerificationCodeWrongPacket) {
bot.error("验证码错误, 请重新输入")
captchaSectionId = 1
this.captchaCache = byteArrayOf()
}
this.captchaCache = this.captchaCache!! + packet.captchaSectionN
this.token00BA = packet.token00BA
if (packet.transmissionCompleted) {
bot.notice(CharImageUtil.createCharImg(ImageIO.read(this.captchaCache!!.inputStream())))
bot.notice("需要验证码登录, 验证码为 4 字母")
try {
(MiraiServer.getInstance().parentFolder + "VerificationCode.png").writeBytes(this.captchaCache!!)
bot.notice("若看不清字符图片, 请查看 Mirai 根目录下 VerificationCode.png")
} catch (e: Exception) {
bot.notice("无法写出验证码文件, 请尝试查看以上字符图片")
}
bot.notice("若要更换验证码, 请直接回车")
val code = Scanner(System.`in`).nextLine()
if (code.isEmpty() || code.length != 4) {
this.captchaCache = byteArrayOf()
this.captchaSectionId = 1
socket.sendPacket(ClientVerificationCodeRefreshPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825))
} else {
socket.sendPacket(ClientVerificationCodeSubmitPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825, code, packet.verificationToken))
}
} else {
socket.sendPacket(ClientVerificationCodeTransmissionRequestPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825, captchaSectionId++, token00BA))
}
}
is ServerLoginResponseSuccessPacket -> {
this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey
socket.sendPacket(ClientSessionRequestPacket(bot.account.qqNumber, socket.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
}
//是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.tgtgtKey = packet.tgtgtKey
socket.sendPacket(ClientLoginResendPacket3104(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: this.token00BA, packet.tlv0006))
} else {
socket.sendPacket(ClientLoginResendPacket3106(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: token00BA, packet.tlv0006))
}
}
is ServerSessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
socket.sendPacket(ClientHeartbeatPacket(bot.account.qqNumber, sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
BotLoginSucceedEvent(bot).broadcast()
//登录成功后会收到大量上次的消息, 忽略掉
MiraiThreadPool.getInstance().schedule({
messageHandler.ignoreMessage = false
}, 3, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105
socket.loginFuture!!.complete(LoginState.SUCCESS)
}
is ServerEventPacket.Raw -> socket.distributePacket(packet.distribute())
is ServerVerificationCodePacket.Encrypted -> socket.distributePacket(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> socket.distributePacket(packet.decrypt())
is ServerLoginResponseKeyExchangePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.tgtgtKey))
is ServerLoginResponseSuccessPacket.Encrypted -> socket.distributePacket(packet.decrypt(this.tgtgtKey))
is ServerSessionKeyResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt())
is ServerAccountInfoResponsePacket.Encrypted -> socket.distributePacket(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> socket.distributePacket(packet.decrypt(sessionKey))
is ServerLoginSuccessPacket,
is ServerHeartbeatResponsePacket,
is UnknownServerPacket -> {
//ignored
}
else -> {
}
}
}
override fun close() {
this.captchaCache = null
this.heartbeatFuture?.cancel(true)
this.heartbeatFuture = null
}
}
}

View File

@ -0,0 +1,24 @@
package net.mamoe.mirai.network
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.handler.DataPacketSocket
import net.mamoe.mirai.utils.getGTK
/**
* 一次会话. 当登录完成后, 客户端会拿到 sessionKey. 此时建立 session, 开始处理消息等事务
*
* @author Him188moe
*/
class LoginSession(
val bot: Bot,
val sessionKey: ByteArray,
val socket: DataPacketSocket
) {
lateinit var cookies: String
var sKey: String = ""
set(value) {
field = value
gtk = getGTK(value)
}
var gtk: Int = 0
}

View File

@ -1,711 +0,0 @@
@file:JvmMultifileClass
@file:JvmName("BotNetworkHandler")
package net.mamoe.mirai.network
import net.mamoe.mirai.Bot
import net.mamoe.mirai.MiraiServer
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.bot.BotLoginSucceedEvent
import net.mamoe.mirai.event.events.network.BeforePacketSendEvent
import net.mamoe.mirai.event.events.network.PacketSentEvent
import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.BotNetworkHandler.*
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.*
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket
import net.mamoe.mirai.network.packet.login.*
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.*
import java.awt.image.BufferedImage
import java.io.Closeable
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetSocketAddress
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
import javax.imageio.ImageIO
import kotlin.reflect.KClass
/**
* Mirai 的网络处理器, 它处理所有数据包([Packet])的发送和接收.
* [BotNetworkHandler] 是全程异步和线程安全的.
*
* [BotNetworkHandler] 2 个模块构成:
* - [SocketHandler]: 处理数据包底层的发送([ByteArray])
* - [PacketHandler]: 制作 [Packet] 并传递给 [SocketHandler] 继续处理; 分析来自服务器的数据包并处理
*
* 其中, [PacketHandler] 4 个子模块构成:
* - [DebugHandler] 输出 [Packet.toString]
* - [LoginHandler] 处理 touch/login/verification code 相关
* - [MessageHandler] 处理消息相关(群消息/好友消息)([ServerEventPacket])
* - [ActionHandler] 处理动作相关(踢人/加入群/好友列表等)
*
* A BotNetworkHandler is used to connect with Tencent servers.
*
* @author Him188moe
*/
@Suppress("EXPERIMENTAL_API_USAGE")//to simplify code
class BotNetworkHandler(private val bot: Bot) : Closeable {
private val socketHandler: SocketHandler = SocketHandler()
val debugHandler = DebugHandler()
val loginHandler = LoginHandler()
val messageHandler = MessageHandler()
val actionHandler = ActionHandler()
private val packetHandlers: Map<KClass<out PacketHandler>, PacketHandler> = linkedMapOf(
DebugHandler::class to debugHandler,
LoginHandler::class to loginHandler,
MessageHandler::class to messageHandler,
ActionHandler::class to actionHandler
)
/**
* Not async
*/
@ExperimentalUnsignedTypes
fun sendPacket(packet: ClientPacket) {
socketHandler.sendPacket(packet)
}
override fun close() {
this.packetHandlers.values.forEach {
it.close()
}
this.socketHandler.close()
}
//private | internal
/**
* 仅当 [LoginState] [LoginState.UNKNOWN] 且非 [LoginState.TIMEOUT] 才会调用 [loginHook].
* 如果要输入验证码, 那么会以参数 [LoginState.VERIFICATION_CODE] 调用 [loginHandler], 登录完成后再以 [LoginState.SUCCESS] 调用 [loginHandler]
*
* @param touchingTimeoutMillis 连接每个服务器的 timeout
*/
@JvmOverloads
internal fun tryLogin(touchingTimeoutMillis: Long = 200): CompletableFuture<LoginState> {
val ipQueue: LinkedList<String> = LinkedList(Protocol.SERVER_IP)
val future = CompletableFuture<LoginState>()
fun login() {
this.socketHandler.close()
val ip = ipQueue.poll()
if (ip == null) {
future.complete(LoginState.UNKNOWN)//所有服务器均返回 UNKNOWN
return
}
this@BotNetworkHandler.socketHandler.touch(ip, touchingTimeoutMillis).get().let { state ->
if (state == LoginState.UNKNOWN || state == LoginState.TIMEOUT) {
login()
} else {
future.complete(state)
}
}
}
login()
return future
}
/**
* 分配收到的数据包
*/
@ExperimentalUnsignedTypes
internal fun distributePacket(packet: ServerPacket) {
try {
packet.decode()
} catch (e: java.lang.Exception) {
e.printStackTrace()
bot.debug("Packet=$packet")
bot.debug("Packet size=" + packet.input.goto(0).readAllBytes().size)
bot.debug("Packet data=" + packet.input.goto(0).readAllBytes().toUHexString())
return
}
if (ServerPacketReceivedEvent(bot, packet).broadcast().isCancelled) {
debugHandler.onPacketReceived(packet)
return
}
this.packetHandlers.values.forEach {
it.onPacketReceived(packet)
}
}
private inner class SocketHandler : Closeable {
private var socket: DatagramSocket? = null
internal var serverIP: String = ""
set(value) {
field = value
restartSocket()
}
internal var loginFuture: CompletableFuture<LoginState>? = null
@Synchronized
private fun restartSocket() {
socket?.close()
socket = DatagramSocket(0)
socket!!.connect(InetSocketAddress(serverIP, 8000))
Thread {
while (socket!!.isConnected) {
val packet = DatagramPacket(ByteArray(2048), 2048)
kotlin.runCatching { socket?.receive(packet) }
.onSuccess {
MiraiThreadPool.getInstance().submit {
try {
distributePacket(ServerPacket.ofByteArray(packet.data.removeZeroTail()))
} catch (e: Exception) {
e.printStackTrace()
}
}
}.onFailure {
if (it.message == "Socket closed" || it.message == "socket closed") {
return@Thread
}
it.printStackTrace()
}
}
}.start()
}
/**
* Start network and touch the server
*/
internal fun touch(serverAddress: String, timeoutMillis: Long): CompletableFuture<LoginState> {
bot.info("Connecting server: $serverAddress")
this.loginFuture = CompletableFuture()
socketHandler.serverIP = serverAddress
waitForPacket(ServerPacket::class, timeoutMillis) {
loginFuture!!.complete(LoginState.TIMEOUT)
}
sendPacket(ClientTouchPacket(bot.account.qqNumber, socketHandler.serverIP))
return this.loginFuture!!
}
/**
* Not async
*/
@Synchronized
@ExperimentalUnsignedTypes
internal fun sendPacket(packet: ClientPacket) {
checkNotNull(socket) { "network closed" }
if (socket!!.isClosed) {
return
}
try {
packet.encodePacket()
if (BeforePacketSendEvent(bot, packet).broadcast().isCancelled) {
return
}
val data = packet.toByteArray()
socket!!.send(DatagramPacket(data, data.size))
bot cyanL "Packet sent: $packet"
PacketSentEvent(bot, packet).broadcast()
} catch (e: Throwable) {
e.printStackTrace()
}
}
@Suppress("UNCHECKED_CAST")
internal fun <P : ServerPacket> waitForPacket(packetClass: KClass<P>, timeoutMillis: Long, timeout: () -> Unit) {
var got = false
ServerPacketReceivedEvent::class.hookWhile {
if (packetClass.isInstance(it.packet) && it.bot == bot) {
got = true
true
} else {
false
}
}
MiraiThreadPool.getInstance().submit {
val startingTime = System.currentTimeMillis()
while (!got) {
if (System.currentTimeMillis() - startingTime > timeoutMillis) {
timeout.invoke()
return@submit
}
Thread.sleep(10)
}
}
}
override fun close() {
this.socket?.close()
if (this.loginFuture != null) {
if (!this.loginFuture!!.isDone) {
this.loginFuture!!.cancel(true)
}
this.loginFuture = null
}
}
fun isClosed(): Boolean {
return this.socket?.isClosed ?: true
}
}
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) {
if (!packet.javaClass.name.endsWith("Encrypted") && !packet.javaClass.name.endsWith("Raw")) {
bot notice "Packet received: $packet"
}
if (packet is ServerEventPacket) {
sendPacket(ClientMessageResponsePacket(bot.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 = getRandomByteArray(16)
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)
}
/**
* 0828_decr_key
*/
private lateinit var sessionResponseDecryptionKey: ByteArray
private var captchaSectionId: Int = 1
private var captchaCache: ByteArray? = byteArrayOf()//每次包只发一部分验证码来
private var heartbeatFuture: ScheduledFuture<*>? = null
private var sKeyRefresherFuture: ScheduledFuture<*>? = null
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!!, bot.account.qqNumber))
} else {//password submission
this.loginIP = packet.loginIP
this.loginTime = packet.loginTime
this.token0825 = packet.token0825
sendPacket(ClientPasswordSubmissionPacket(bot.account.qqNumber, bot.account.password, packet.loginTime, packet.loginIP, this.tgtgtKey, packet.token0825))
}
}
is ServerLoginResponseFailedPacket -> {
socketHandler.loginFuture?.complete(packet.loginState)
return
}
is ServerVerificationCodeCorrectPacket -> {
this.tgtgtKey = getRandomByteArray(16)
this.token00BA = packet.token00BA
sendPacket(ClientLoginResendPacket3105(bot.account.qqNumber, bot.account.password, this.loginTime, this.loginIP, this.tgtgtKey, this.token0825, this.token00BA))
}
is ServerLoginResponseVerificationCodeInitPacket -> {
//[token00BA]来源之一: 验证码
this.token00BA = packet.token00BA
this.captchaCache = packet.verifyCodePart1
if (packet.unknownBoolean != null && packet.unknownBoolean!!) {
this.captchaSectionId = 1
sendPacket(ClientVerificationCodeTransmissionRequestPacket(1, bot.account.qqNumber, this.token0825, this.captchaSectionId++, this.token00BA))
}
}
is ServerVerificationCodeTransmissionPacket -> {
if (packet is ServerVerificationCodeWrongPacket) {
bot error "验证码错误, 请重新输入"
captchaSectionId = 1
this.captchaCache = byteArrayOf()
}
this.captchaCache = this.captchaCache!! + packet.captchaSectionN
this.token00BA = packet.token00BA
if (packet.transmissionCompleted) {
bot notice (CharImageUtil.createCharImg(ImageIO.read(this.captchaCache!!.inputStream())))
bot notice ("需要验证码登录, 验证码为 4 字母")
try {
(MiraiServer.getInstance().parentFolder + "VerificationCode.png").writeBytes(this.captchaCache!!)
bot notice ("若看不清字符图片, 请查看 Mirai 根目录下 VerificationCode.png")
} catch (e: Exception) {
bot notice "无法写出验证码文件, 请尝试查看以上字符图片"
}
bot notice ("若要更换验证码, 请直接回车")
val code = Scanner(System.`in`).nextLine()
if (code.isEmpty() || code.length != 4) {
this.captchaCache = byteArrayOf()
this.captchaSectionId = 1
sendPacket(ClientVerificationCodeRefreshPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825))
} else {
sendPacket(ClientVerificationCodeSubmitPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825, code, packet.verificationToken))
}
} else {
sendPacket(ClientVerificationCodeTransmissionRequestPacket(packet.packetIdLast + 1, bot.account.qqNumber, token0825, captchaSectionId++, token00BA))
}
}
is ServerLoginResponseSuccessPacket -> {
this.sessionResponseDecryptionKey = packet.sessionResponseDecryptionKey
sendPacket(ClientSessionRequestPacket(bot.account.qqNumber, socketHandler.serverIP, packet.token38, packet.token88, packet.encryptionKey, this.tlv0105))
}
//是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.tgtgtKey = packet.tgtgtKey
sendPacket(ClientLoginResendPacket3104(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: this.token00BA, packet.tlv0006))
} else {
sendPacket(ClientLoginResendPacket3106(bot.account.qqNumber, bot.account.password, loginTime, loginIP, tgtgtKey, token0825, packet.tokenUnknown
?: token00BA, packet.tlv0006))
}
}
is ServerSessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
heartbeatFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientHeartbeatPacket(bot.account.qqNumber, sessionKey))
}, 90000, 90000, TimeUnit.MILLISECONDS)
BotLoginSucceedEvent(bot).broadcast()
//登录成功后会收到大量上次的消息, 忽略掉
MiraiThreadPool.getInstance().schedule({
messageHandler.ignoreMessage = false
}, 3, TimeUnit.SECONDS)
this.tlv0105 = packet.tlv0105
sendPacket(ClientChangeOnlineStatusPacket(bot.account.qqNumber, sessionKey, ClientLoginStatus.ONLINE))
}
is ServerLoginSuccessPacket -> {
socketHandler.loginFuture!!.complete(LoginState.SUCCESS)
sendPacket(ClientSKeyRequestPacket(bot.account.qqNumber, sessionKey))
}
is ServerSKeyResponsePacket -> {
actionHandler.sKey = packet.sKey
actionHandler.cookies = "uin=o" + bot.account.qqNumber + ";skey=" + actionHandler.sKey + ";"
sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
sendPacket(ClientSKeyRefreshmentRequestPacket(bot.account.qqNumber, sessionKey))
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
actionHandler.gtk = getGTK(actionHandler.sKey)
sendPacket(ClientAccountInfoRequestPacket(bot.account.qqNumber, sessionKey))
}
is ServerEventPacket.Raw -> distributePacket(packet.distribute())
is ServerVerificationCodePacket.Encrypted -> distributePacket(packet.decrypt())
is ServerLoginResponseVerificationCodeInitPacket.Encrypted -> distributePacket(packet.decrypt())
is ServerLoginResponseKeyExchangePacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey))
is ServerLoginResponseSuccessPacket.Encrypted -> distributePacket(packet.decrypt(this.tgtgtKey))
is ServerSessionKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(this.sessionResponseDecryptionKey))
is ServerTouchResponsePacket.Encrypted -> distributePacket(packet.decrypt())
is ServerSKeyResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerEventPacket.Raw.Encrypted -> distributePacket(packet.decrypt(sessionKey))
is ServerAccountInfoResponsePacket,
is ServerHeartbeatResponsePacket,
is UnknownServerPacket -> {
//ignored
}
else -> {
}
}
}
override fun close() {
this.captchaCache = null
this.heartbeatFuture?.cancel(true)
this.sKeyRefresherFuture?.cancel(true)
this.heartbeatFuture = null
this.sKeyRefresherFuture = null
}
}
/**
* 处理消息事件, 承担消息发送任务.
*/
inner class MessageHandler : PacketHandler() {
internal var ignoreMessage: Boolean = true
init {
//todo for test
FriendMessageEvent::class.hookWhile {
if (socketHandler.isClosed()) {
return@hookWhile false
}
if (it.message() valueEquals "你好") {
it.qq.sendMessage("你好!")
} else if (it.message().toString().startsWith("复读")) {
it.qq.sendMessage(it.message())
}
return@hookWhile true
}
}
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerGroupUploadFileEventPacket -> {
//todo
}
is ServerFriendMessageEventPacket -> {
if (ignoreMessage) {
return
}
FriendMessageEvent(bot, bot.contacts.getQQ(packet.qq), packet.message).broadcast()
}
is ServerGroupMessageEventPacket -> {
//todo message chain
//GroupMessageEvent(this.bot, bot.contacts.getGroupByNumber(packet.groupNumber), bot.contacts.getQQ(packet.qq), packet.message)
}
is UnknownServerEventPacket -> {
//todo
}
is ServerSendFriendMessageResponsePacket,
is ServerSendGroupMessageResponsePacket -> {
//ignored
}
else -> {
//ignored
}
}
}
fun sendFriendMessage(qq: QQ, message: MessageChain) {
sendPacket(ClientSendFriendMessagePacket(bot.account.qqNumber, qq.number, sessionKey, message))
}
fun sendGroupMessage(group: Group, message: Message): Unit {
TODO()
//sendPacket(ClientSendGroupMessagePacket(group.groupId, bot.account.qqNumber, sessionKey, message))
}
}
/**
* 动作: 获取好友列表, 点赞, 踢人等.
* 处理动作事件, 承担动作任务.
*/
inner class ActionHandler : PacketHandler() {
internal lateinit var cookies: String
internal var sKey: String = ""
set(value) {
field = value
gtk = getGTK(value)
}
internal var gtk: Int = 0
private val addFriendSessions = Collections.synchronizedCollection(mutableListOf<AddFriendSession>())
private val uploadImageSessions = Collections.synchronizedCollection(mutableListOf<UploadImageSession>())
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerCanAddFriendResponsePacket -> {
this.uploadImageSessions.forEach {
it.onPacketReceived(packet)
}
}
is ServerTryUploadGroupImageSuccessPacket -> {
ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
}
is ServerTryUploadGroupImageFailedPacket -> {
}
is ServerTryUploadGroupImageResponsePacket.Encrypted -> distributePacket(packet.decrypt(sessionKey))
else -> {
}
}
}
fun addFriend(qqNumber: Long, message: Supplier<String>) {
addFriend(qqNumber, lazy { message.get() })
}
@JvmSynthetic
fun addFriend(qqNumber: Long, message: Lazy<String> = lazyOf("")): CompletableFuture<AddFriendResult> {
val future = CompletableFuture<AddFriendResult>()
val session = AddFriendSession(qqNumber, future, message)
uploadImageSessions.add(session)
session.sendAddRequest();
return future
}
override fun close() {
}
private inner class UploadImageSession(
private val group: Long,
private val future: CompletableFuture<AddFriendResult>,
private val image: BufferedImage
) : Closeable {
lateinit var id: ByteArray
fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) {
return
}
when (packet) {
is ServerCanAddFriendResponsePacket -> {
if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) {
return
}
when (packet.state) {
ServerCanAddFriendResponsePacket.State.FAILED -> {
future.complete(AddFriendResult.FAILED)
close()
}
ServerCanAddFriendResponsePacket.State.ALREADY_ADDED -> {
future.complete(AddFriendResult.ALREADY_ADDED)
close()
}
ServerCanAddFriendResponsePacket.State.REQUIRE_VERIFICATION -> {
sendPacket(ClientAddFriendPacket(bot.account.qqNumber, qq, sessionKey))
}
ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
}
}
}
}
}
override fun sendRequest() {
}
override fun close() {
uploadImageSessions.remove(this)
}
}
private inner class AddFriendSession(
private val qq: Long,
private val future: CompletableFuture<AddFriendResult>,
private val message: Lazy<String>
) : Closeable {
lateinit var id: ByteArray
fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) {
return
}
when (packet) {
is ServerCanAddFriendResponsePacket -> {
if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) {
return
}
when (packet.state) {
ServerCanAddFriendResponsePacket.State.FAILED -> {
future.complete(AddFriendResult.FAILED)
close()
}
ServerCanAddFriendResponsePacket.State.ALREADY_ADDED -> {
future.complete(AddFriendResult.ALREADY_ADDED)
close()
}
ServerCanAddFriendResponsePacket.State.REQUIRE_VERIFICATION -> {
sendPacket(ClientAddFriendPacket(bot.account.qqNumber, qq, sessionKey))
}
ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
}
}
}
}
}
fun sendAddRequest() {
sendPacket(ClientCanAddFriendPacket(bot.account.qqNumber, qq, sessionKey).also { this.id = it.packetIdLast })
}
override fun close() {
uploadImageSessions.remove(this)
}
}
}
}

View File

@ -0,0 +1,216 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.AddFriendResult
import net.mamoe.mirai.network.packet.action.ClientAddFriendPacket
import net.mamoe.mirai.network.packet.action.ClientCanAddFriendPacket
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageFailedPacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageSuccessPacket
import net.mamoe.mirai.network.packet.login.ClientChangeOnlineStatusPacket
import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.ClientLoginStatus
import net.mamoe.mirai.utils.ImageNetworkUtils
import net.mamoe.mirai.utils.getGTK
import net.mamoe.mirai.utils.toUHexString
import java.awt.image.BufferedImage
import java.io.Closeable
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.function.Supplier
/**
* 动作: 获取好友列表, 点赞, 踢人等.
* 处理动作事件, 承担动作任务.
*/
class ActionHandler(session: LoginSession) : PacketHandler(session) {
private val addFriendSessions = Collections.synchronizedCollection(mutableListOf<AddFriendSession>())
private val uploadImageSessions = Collections.synchronizedCollection(mutableListOf<UploadImageSession>())
private var sKeyRefresherFuture: ScheduledFuture<*>? = null
@ExperimentalUnsignedTypes
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerCanAddFriendResponsePacket -> {
this.uploadImageSessions.forEach {
it.onPacketReceived(packet)
}
}
is ServerTryUploadGroupImageSuccessPacket -> {
ImageNetworkUtils.postImage(packet.uKey.toUHexString(), )
}
is ServerTryUploadGroupImageFailedPacket -> {
}
is ServerTryUploadGroupImageResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerAccountInfoResponsePacket -> {
}
is ServerSKeyResponsePacket.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
is ServerSKeyResponsePacket -> {
session.sKey = packet.sKey
session.cookies = "uin=o" + session.bot.account.qqNumber + ";skey=" + session.sKey + ";"
sKeyRefresherFuture = MiraiThreadPool.getInstance().scheduleWithFixedDelay({
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.qqNumber, session.sessionKey))
}, 1800000, 1800000, TimeUnit.MILLISECONDS)
session.gtk = getGTK(session.sKey)
}
else -> {
}
}
}
@ExperimentalUnsignedTypes
fun addFriend(qqNumber: Long, message: Supplier<String>) {
addFriend(qqNumber, lazy { message.get() })
}
@ExperimentalUnsignedTypes
@JvmSynthetic
fun addFriend(qqNumber: Long, message: Lazy<String> = lazyOf("")): CompletableFuture<AddFriendResult> {
val future = CompletableFuture<AddFriendResult>()
val session = AddFriendSession(qqNumber, future, message)
uploadImageSessions.add(session)
session.sendAddRequest();
return future
}
@ExperimentalUnsignedTypes
fun requestSKey() {
session.socket.sendPacket(ClientSKeyRequestPacket(session.bot.account.qqNumber, session.sessionKey))
}
@ExperimentalUnsignedTypes
fun changeOnlineStatus(status: ClientLoginStatus) {
session.socket.sendPacket(ClientChangeOnlineStatusPacket(session.bot.account.qqNumber, session.sessionKey, status))
}
@ExperimentalUnsignedTypes
fun requestAccountInfo() {
session.socket.sendPacket(ClientAccountInfoRequestPacket(session.bot.account.qqNumber, session.sessionKey))
}
override fun close() {
this.sKeyRefresherFuture?.cancel(true)
this.sKeyRefresherFuture = null
}
private inner class UploadImageSession(
private val group: Long,
private val future: CompletableFuture<AddFriendResult>,
private val image: BufferedImage
) : Closeable {
lateinit var id: ByteArray
@ExperimentalUnsignedTypes
fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) {
return
}
when (packet) {
is ServerCanAddFriendResponsePacket -> {
if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) {
return
}
when (packet.state) {
ServerCanAddFriendResponsePacket.State.FAILED -> {
future.complete(AddFriendResult.FAILED)
close()
}
ServerCanAddFriendResponsePacket.State.ALREADY_ADDED -> {
future.complete(AddFriendResult.ALREADY_ADDED)
close()
}
ServerCanAddFriendResponsePacket.State.REQUIRE_VERIFICATION -> {
session.socket.sendPacket(ClientAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey))
}
ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
}
}
}
}
}
fun sendRequest() {
}
override fun close() {
uploadImageSessions.remove(this)
}
}
private inner class AddFriendSession(
private val qq: Long,
private val future: CompletableFuture<AddFriendResult>,
private val message: Lazy<String>
) : Closeable {
lateinit var id: ByteArray
@ExperimentalUnsignedTypes
fun onPacketReceived(packet: ServerPacket) {
if (!::id.isInitialized) {
return
}
when (packet) {
is ServerCanAddFriendResponsePacket -> {
if (!(packet.idByteArray[2] == id[0] && packet.idByteArray[3] == id[1])) {
return
}
when (packet.state) {
ServerCanAddFriendResponsePacket.State.FAILED -> {
future.complete(AddFriendResult.FAILED)
close()
}
ServerCanAddFriendResponsePacket.State.ALREADY_ADDED -> {
future.complete(AddFriendResult.ALREADY_ADDED)
close()
}
ServerCanAddFriendResponsePacket.State.REQUIRE_VERIFICATION -> {
session.socket.sendPacket(ClientAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey))
}
ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
}
}
}
}
}
@ExperimentalUnsignedTypes
fun sendAddRequest() {
session.socket.sendPacket(ClientCanAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey).also { this.id = it.packetIdLast })
}
override fun close() {
uploadImageSessions.remove(this)
}
}
}

View File

@ -1,14 +0,0 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
/**
* @author Him188moe
*/
data class BotSession(
val bot: Bot,
val sessionKey: ByteArray,
val networkHandler: BotNetworkHandler
) {
}

View File

@ -0,0 +1,20 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.network.packet.ClientPacket
import net.mamoe.mirai.network.packet.ServerPacket
import java.io.Closeable
/**
* @author Him188moe
*/
interface DataPacketSocket : Closeable {
fun distributePacket(packet: ServerPacket)
@ExperimentalUnsignedTypes
fun sendPacket(packet: ClientPacket)
fun isClosed(): Boolean
override fun close()
}

View File

@ -0,0 +1,24 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.ClientMessageResponsePacket
import net.mamoe.mirai.network.packet.ServerEventPacket
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.utils.notice
/**
* Kind of [PacketHandler] that prints all packets received in the format of hex byte array.
*/
sealed class DebugHandler(session: LoginSession) : PacketHandler(session) {
@ExperimentalUnsignedTypes
override fun onPacketReceived(packet: ServerPacket) {
if (!packet.javaClass.name.endsWith("Encrypted") && !packet.javaClass.name.endsWith("Raw")) {
session.bot notice "Packet received: $packet"
}
if (packet is ServerEventPacket) {
session.socket.sendPacket(ClientMessageResponsePacket(session.bot.account.qqNumber, packet.packetId, session.sessionKey, packet.eventIdentity))
}
}
}

View File

@ -0,0 +1,79 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.event.events.qq.FriendMessageEvent
import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.network.packet.action.ClientSendFriendMessagePacket
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
/**
* 处理消息事件, 承担消息发送任务.
*/
class MessageHandler(session: LoginSession) : PacketHandler(session) {
internal var ignoreMessage: Boolean = true
init {
//todo for test
FriendMessageEvent::class.hookWhile {
if (session.socket.isClosed()) {
return@hookWhile false
}
if (it.message() valueEquals "你好") {
it.qq.sendMessage("你好!")
} else if (it.message().toString().startsWith("复读")) {
it.qq.sendMessage(it.message())
}
return@hookWhile true
}
}
override fun onPacketReceived(packet: ServerPacket) {
when (packet) {
is ServerGroupUploadFileEventPacket -> {
//todo
}
is ServerFriendMessageEventPacket -> {
if (ignoreMessage) {
return
}
FriendMessageEvent(session.bot, session.bot.contacts.getQQ(packet.qq), packet.message).broadcast()
}
is ServerGroupMessageEventPacket -> {
//todo message chain
//GroupMessageEvent(this.bot, bot.contacts.getGroupByNumber(packet.groupNumber), bot.contacts.getQQ(packet.qq), packet.message)
}
is UnknownServerEventPacket -> {
//todo
}
is ServerSendFriendMessageResponsePacket,
is ServerSendGroupMessageResponsePacket -> {
//ignored
}
else -> {
//ignored
}
}
}
@ExperimentalUnsignedTypes
fun sendFriendMessage(qq: QQ, message: MessageChain) {
session.socket.sendPacket(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message))
}
fun sendGroupMessage(group: Group, message: Message): Unit {
TODO()
//sendPacket(ClientSendGroupMessagePacket(group.groupId, bot.account.qqNumber, sessionKey, message))
}
}

View File

@ -0,0 +1,43 @@
package net.mamoe.mirai.network.handler
import net.mamoe.mirai.network.LoginSession
import net.mamoe.mirai.network.packet.ServerPacket
import java.io.Closeable
import java.util.*
import kotlin.NoSuchElementException
/**
* 数据包(接受/发送)处理器
*/
abstract class PacketHandler(
val session: LoginSession
) : Closeable {
abstract fun onPacketReceived(packet: ServerPacket)
override fun close() {
}
}
class PacketHandlerNode<T : PacketHandler>(
val clazz: Class<T>,
val instance: T
)
infix fun PacketHandler.to(handler: PacketHandler): PacketHandlerNode<PacketHandler> {
return PacketHandlerNode(handler.javaClass, handler)
}
class PacketHandlerList : LinkedList<PacketHandlerNode<*>>() {
fun <T : PacketHandler> get(clazz: Class<T>): T {
this.forEach {
if (it.clazz == clazz) {
@Suppress("UNCHECKED_CAST")
return@get it.instance as T
}
}
throw NoSuchElementException()
}
}

View File

@ -6,6 +6,8 @@ import java.io.DataInputStream
/**
* SKey 用于 http api
*
* @author Him188moe
*/
@ExperimentalUnsignedTypes

View File

@ -34,11 +34,6 @@ enum class LoginState {
*/
TAKEN_BACK,
/**
* 需要验证码登录
*/
VERIFICATION_CODE,
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
*/

View File

@ -1,6 +1,8 @@
package net.mamoe.mirai.utils
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto
import java.text.SimpleDateFormat
import java.util.*
@ -11,20 +13,20 @@ import java.util.*
* @author NaturalHG
*/
object MiraiLogger {
infix fun log(o: Any?) = info(o)
infix fun println(o: Any?) = info(o)
infix fun info(o: Any?) = this.print(o.toString(), LoggerTextFormat.RESET)
fun log(o: Any?) = info(o)
fun println(o: Any?) = info(o)
fun info(o: Any?) = this.print(o.toString(), LoggerTextFormat.RESET)
infix fun error(o: Any?) = this.print(o.toString(), LoggerTextFormat.RED)
fun error(o: Any?) = this.print(o.toString(), LoggerTextFormat.RED)
infix fun notice(o: Any?) = this.print(o.toString(), LoggerTextFormat.LIGHT_BLUE)
fun notice(o: Any?) = this.print(o.toString(), LoggerTextFormat.LIGHT_BLUE)
infix fun success(o: Any?) = this.print(o.toString(), LoggerTextFormat.GREEN)
fun success(o: Any?) = this.print(o.toString(), LoggerTextFormat.GREEN)
infix fun debug(o: Any?) = this.print(o.toString(), LoggerTextFormat.YELLOW)
fun debug(o: Any?) = this.print(o.toString(), LoggerTextFormat.YELLOW)
infix fun catching(e: Throwable) {
fun catching(e: Throwable) {
e.printStackTrace()
/*
this.print(e.message)
@ -39,21 +41,26 @@ object MiraiLogger {
}
}
infix fun Bot.log(o: Any?) = info(o)
infix fun Bot.println(o: Any?) = info(o)
infix fun Bot.info(o: Any?) = print(this, o.toString(), LoggerTextFormat.RESET)
fun Bot.log(o: Any?) = info(o)
fun Bot.println(o: Any?) = info(o)
fun Bot.info(o: Any?) = print(this, o.toString(), LoggerTextFormat.RESET)
infix fun Bot.error(o: Any?) = print(this, o.toString(), LoggerTextFormat.RED)
fun Bot.error(o: Any?) = print(this, o.toString(), LoggerTextFormat.RED)
infix fun Bot.notice(o: Any?) = print(this, o.toString(), LoggerTextFormat.LIGHT_BLUE)
fun Bot.notice(o: Any?) = print(this, o.toString(), LoggerTextFormat.LIGHT_BLUE)
infix fun Bot.purple(o: Any?) = print(this, o.toString(), LoggerTextFormat.PURPLE)
fun Bot.purple(o: Any?) = print(this, o.toString(), LoggerTextFormat.PURPLE)
infix fun Bot.cyanL(o: Any?) = print(this, o.toString(), LoggerTextFormat.LIGHT_CYAN)
fun Bot.cyanL(o: Any?) = print(this, o.toString(), LoggerTextFormat.LIGHT_CYAN)
fun Bot.success(o: Any?) = print(this, o.toString(), LoggerTextFormat.GREEN)
infix fun Bot.success(o: Any?) = print(this, o.toString(), LoggerTextFormat.GREEN)
fun Bot.debug(o: Any?) = print(this, o.toString(), LoggerTextFormat.YELLOW)
infix fun Bot.debug(o: Any?) = print(this, o.toString(), LoggerTextFormat.YELLOW)
fun Bot.debugPacket(packet: ServerPacket) {
debug("Packet=$packet")
debug("Packet size=" + packet.input.goto(0).readAllBytes().size)
debug("Packet data=" + packet.input.goto(0).readAllBytes().toUHexString())
}
@Synchronized