Commit 4a319bf5 authored by Him188moe's avatar Him188moe

Updated network

parent d19216a6
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
}
\ No newline at end of file
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)
}
}
}
\ No newline at end of file
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
) {
}
\ No newline at end of file
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()
}
\ No newline at end of file
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))
}
}
}
\ No newline at end of file
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))
}
}
\ No newline at end of file
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()
}
}
...@@ -6,6 +6,8 @@ import java.io.DataInputStream ...@@ -6,6 +6,8 @@ import java.io.DataInputStream
/** /**
* SKey 用于 http api
*
* @author Him188moe * @author Him188moe
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
......
...@@ -34,11 +34,6 @@ enum class LoginState { ...@@ -34,11 +34,6 @@ enum class LoginState {
*/ */
TAKEN_BACK, TAKEN_BACK,
/**
* 需要验证码登录
*/
VERIFICATION_CODE,
/** /**
* 未知. 更换服务器或等几分钟再登录可能解决. * 未知. 更换服务器或等几分钟再登录可能解决.
*/ */
......
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import net.mamoe.mirai.Bot 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.text.SimpleDateFormat
import java.util.* import java.util.*
...@@ -11,20 +13,20 @@ import java.util.* ...@@ -11,20 +13,20 @@ import java.util.*
* @author NaturalHG * @author NaturalHG
*/ */
object MiraiLogger { object MiraiLogger {
infix fun log(o: Any?) = info(o) fun log(o: Any?) = info(o)
infix fun println(o: Any?) = info(o) fun println(o: Any?) = info(o)
infix fun info(o: Any?) = this.print(o.toString(), LoggerTextFormat.RESET) 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() e.printStackTrace()
/* /*
this.print(e.message) this.print(e.message)
...@@ -39,21 +41,26 @@ object MiraiLogger { ...@@ -39,21 +41,26 @@ object MiraiLogger {
} }
} }
infix fun Bot.log(o: Any?) = info(o) fun Bot.log(o: Any?) = info(o)
infix fun Bot.println(o: Any?) = info(o) fun Bot.println(o: Any?) = info(o)
infix fun Bot.info(o: Any?) = print(this, o.toString(), LoggerTextFormat.RESET) 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 @Synchronized
......
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