expectPacket(handlerTemporary: TemporaryPacketHandler.() -> Unit): CompletableFuture {
+ val future = CompletableFuture()
+ this.bot.network.addHandler(TemporaryPacketHandler(P::class, future, this).also(handlerTemporary))
+ return future
+ }
+
+ /**
+ * 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
+ * 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
+ * 由于包名可能过长, 可使用 `DataPacketSocket.expectPacket(PacketProcessor)` 替代.
+ *
+ * 实现方法:
+ * ```kotlin
+ * session.expectPacket(ClientPacketXXX(...)) {//it: ServerPacketXXX
+ *
+ * }
+ * ```
+ *
+ * @param P 期待的包
+ * @param toSend 将要发送的包
+ * @param handler 处理期待的包
+ * @return future. 可进行超时处理
+ *
+ * Kotlin DSL: 仅 Kotlin 使用.
+ */
+ @JvmSynthetic
+ inline fun expectPacket(toSend: ClientPacket, noinline handler: (P) -> Unit): CompletableFuture {
+ val future = CompletableFuture()
+ this.bot.network.addHandler(TemporaryPacketHandler(P::class, future, this).also {
+ it.toSend { toSend }
+ it.expect(handler)
+ })
+ return future
+ }
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
index 06e558b35..187d7327c 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/Protocol.kt
@@ -1,3 +1,5 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
package net.mamoe.mirai.network
import java.net.InetAddress
@@ -72,7 +74,7 @@ object Protocol {
private val hexToByteArrayCacheMap: MutableMap = mutableMapOf()
- @ExperimentalUnsignedTypes
+
fun hexToBytes(hex: String): ByteArray {
hex.hashCode().let { id ->
if (hexToByteArrayCacheMap.containsKey(id)) {
@@ -86,7 +88,7 @@ object Protocol {
}
}
- @ExperimentalUnsignedTypes
+
fun hexToUBytes(hex: String): UByteArray = Arrays
.stream(hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
.map { value -> value.trim { it <= ' ' } }
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt
deleted file mode 100644
index 2ae1745b3..000000000
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/RobotNetworkHandler.kt
+++ /dev/null
@@ -1,645 +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.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 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, 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 {
- val ipQueue: LinkedList = LinkedList(Protocol.SERVER_IP)
- val future = CompletableFuture()
-
- 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? = 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 {
- 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 waitForPacket(packetClass: KClass
, 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())
-
- override fun onPacketReceived(packet: ServerPacket) {
- when (packet) {
- is ServerCanAddFriendResponsePacket -> {
- this.addFriendSessions.forEach {
- it.onPacketReceived(packet)
- }
- }
- else -> {
- }
- }
- }
-
- fun addFriend(qqNumber: Long, message: Supplier) {
- addFriend(qqNumber, lazy { message.get() })
- }
-
- @JvmSynthetic
- fun addFriend(qqNumber: Long, message: Lazy = lazyOf("")): CompletableFuture {
- val future = CompletableFuture()
- val session = AddFriendSession(qqNumber, future, message)
- addFriendSessions.add(session)
- session.sendAddRequest();
- return future
- }
-
- override fun close() {
-
- }
-
- private inner class AddFriendSession(
- private val qq: Long,
- private val future: CompletableFuture,
- private val message: Lazy
- ) : 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() {
- addFriendSessions.remove(this)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/handler/ActionPacketHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/ActionPacketHandler.kt
new file mode 100644
index 000000000..bd68f3323
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/ActionPacketHandler.kt
@@ -0,0 +1,213 @@
+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.task.MiraiThreadPool
+import net.mamoe.mirai.utils.getGTK
+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
+
+/**
+ * 动作: 获取好友列表, 点赞, 踢人等.
+ * 处理动作事件, 承担动作任务.
+ *
+ * @author Him188moe
+ */
+class ActionPacketHandler(session: LoginSession) : PacketHandler(session) {
+ private val addFriendSessions = Collections.synchronizedCollection(mutableListOf())
+ private val uploadImageSessions = Collections.synchronizedCollection(mutableListOf())
+
+ private var sKeyRefresherFuture: ScheduledFuture<*>? = null
+
+
+ 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.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)
+ }
+
+ is ServerEventPacket.Raw.Encrypted -> session.socket.distributePacket(packet.decrypt(session.sessionKey))
+ is ServerEventPacket.Raw -> session.socket.distributePacket(packet.distribute())
+
+ else -> {
+ }
+ }
+ }
+
+
+ fun addFriend(qqNumber: Long, message: Supplier) {
+ addFriend(qqNumber, lazy { message.get() })
+ }
+
+
+ @JvmSynthetic
+ fun addFriend(qqNumber: Long, message: Lazy = lazyOf("")): CompletableFuture {
+ val future = CompletableFuture()
+ val session = AddFriendSession(qqNumber, future, message)
+ // uploadImageSessions.add(session)
+ session.sendAddRequest();
+ return future
+ }
+
+
+ fun requestSKey() {
+ session.socket.sendPacket(ClientSKeyRequestPacket(session.bot.account.qqNumber, session.sessionKey))
+ }
+
+
+ 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,
+ 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 -> {
+ // 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,
+ private val message: Lazy
+ ) : 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 -> {
+ session.socket.sendPacket(ClientAddFriendPacket(session.bot.account.qqNumber, qq, session.sessionKey))
+ }
+
+ ServerCanAddFriendResponsePacket.State.NOT_REQUIRE_VERIFICATION -> {
+
+ }
+ }
+ }
+
+
+ }
+ }
+
+
+ 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
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/handler/DataPacketSocket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/DataPacketSocket.kt
new file mode 100644
index 000000000..4eb19c20c
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/DataPacketSocket.kt
@@ -0,0 +1,53 @@
+package net.mamoe.mirai.network.handler
+
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
+import net.mamoe.mirai.network.BotNetworkHandlerImpl
+import net.mamoe.mirai.network.LoginSession
+import net.mamoe.mirai.network.packet.ClientPacket
+import net.mamoe.mirai.network.packet.ServerPacket
+import net.mamoe.mirai.task.MiraiThreadPool
+import java.io.Closeable
+import java.util.concurrent.Future
+
+/**
+ * 网络接口.
+ * 发包 / 处理包.
+ * 仅可通过 [BotNetworkHandlerImpl.socket] 得到实例.
+ *
+ * @author Him188moe
+ */
+interface DataPacketSocket : Closeable {
+ fun getOwner(): Bot
+
+ /**
+ * 分发数据包给 [PacketHandler]
+ */
+ fun distributePacket(packet: ServerPacket)
+
+ /**
+ * 发送一个数据包(非异步).
+ *
+ * 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
+ *
+ * @see [LoginSession.expectPacket] kotlin DSL
+ */
+ fun sendPacket(packet: ClientPacket)
+
+ /**
+ * 发送一个数据包(异步).
+ *
+ * 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
+ *
+ * @see [LoginSession.expectPacket] kotlin DSL
+ */
+ fun sendPacketAsync(packet: ClientPacket): Future<*> {
+ return MiraiThreadPool.getInstance().submit {
+ sendPacket(packet)
+ }
+ }
+
+ fun isClosed(): Boolean
+
+ override fun close()
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/handler/MessagePacketHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/MessagePacketHandler.kt
new file mode 100644
index 000000000..728bd20c3
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/MessagePacketHandler.kt
@@ -0,0 +1,91 @@
+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.defaults.MessageChain
+import net.mamoe.mirai.message.defaults.PlainText
+import net.mamoe.mirai.message.defaults.UnsolvedImage
+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.ClientSendGroupMessagePacket
+import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
+import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
+import java.io.File
+
+/**
+ * 处理消息事件, 承担消息发送任务.
+ *
+ * @author Him188moe
+ */
+@Suppress("EXPERIMENTAL_API_USAGE")
+class MessagePacketHandler(session: LoginSession) : PacketHandler(session) {
+ internal var ignoreMessage: Boolean = true
+
+ init {
+ //todo for test
+ FriendMessageEvent::class.hookWhile {
+ if (session.socket.isClosed()) {
+ return@hookWhile false
+ }
+ when {
+ it.message() valueEquals "你好" -> it.qq.sendMessage("你好!")
+ it.message().toString().startsWith("复读") -> it.qq.sendMessage(it.message())
+ it.message().toString().startsWith("发群") -> {
+ it.message().list.toMutableList().let { messages ->
+ messages.removeAt(0)
+ sendGroupMessage(Group(session.bot, 580266363), MessageChain(messages))
+ }
+ }
+ it.message() valueEquals "发图片" -> sendGroupMessage(Group(session.bot, 580266363), PlainText("test") + UnsolvedImage(File("C:\\Users\\Him18\\Desktop\\faceImage_1559564477775.jpg")).also { image ->
+ image.upload(session, it.qq).get()
+ })
+ }
+
+ 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
+ }
+ }
+ }
+
+ fun sendFriendMessage(qq: QQ, message: MessageChain) {
+ session.socket.sendPacketAsync(ClientSendFriendMessagePacket(session.bot.account.qqNumber, qq.number, session.sessionKey, message))
+ }
+
+ fun sendGroupMessage(group: Group, message: MessageChain) {
+ session.socket.sendPacket(ClientSendGroupMessagePacket(group.groupId, session.bot.account.qqNumber, session.sessionKey, message))
+ }
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/handler/PacketHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/PacketHandler.kt
new file mode 100644
index 000000000..1ff85ce95
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/PacketHandler.kt
@@ -0,0 +1,42 @@
+package net.mamoe.mirai.network.handler
+
+import net.mamoe.mirai.network.LoginSession
+import net.mamoe.mirai.network.packet.ServerPacket
+import net.mamoe.mirai.utils.MiraiSynchronizedLinkedList
+import java.io.Closeable
+
+/**
+ * 数据包(接受/发送)处理器
+ */
+abstract class PacketHandler(
+ val session: LoginSession
+) : Closeable {
+ abstract fun onPacketReceived(packet: ServerPacket)
+
+ override fun close() {
+
+ }
+}
+
+class PacketHandlerNode(
+ val clazz: Class,
+ val instance: T
+)
+
+fun PacketHandler.asNode(): PacketHandlerNode {
+ return PacketHandlerNode(this.javaClass, this)
+}
+
+class PacketHandlerList : MiraiSynchronizedLinkedList>() {
+
+ fun get(clazz: Class): T {
+ this.forEach {
+ if (it.clazz == clazz) {
+ @Suppress("UNCHECKED_CAST")
+ return@get it.instance as T
+ }
+ }
+
+ throw NoSuchElementException()
+ }
+}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/handler/TemporaryPacketHandler.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/TemporaryPacketHandler.kt
new file mode 100644
index 000000000..3e6b06d95
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/handler/TemporaryPacketHandler.kt
@@ -0,0 +1,49 @@
+package net.mamoe.mirai.network.handler
+
+import net.mamoe.mirai.network.LoginSession
+import net.mamoe.mirai.network.packet.ClientPacket
+import net.mamoe.mirai.network.packet.ServerPacket
+import java.util.concurrent.CompletableFuture
+import kotlin.reflect.KClass
+
+/**
+ * 临时数据包处理器
+ *
+ * @see LoginSession.expectPacket
+ */
+open class TemporaryPacketHandler(
+ private val expectationClass: KClass
,
+ private val future: CompletableFuture,
+ private val fromSession: LoginSession
+) {
+ private lateinit var toSend: ClientPacket
+
+ private lateinit var expect: (P) -> Unit
+
+
+ lateinit var session: LoginSession//无需覆盖
+
+ fun toSend(packet: () -> ClientPacket) {
+ this.toSend = packet()
+ }
+
+
+ fun expect(handler: (P) -> Unit) {
+ this.expect = handler
+ }
+
+ fun send(session: LoginSession) {
+ this.session = session
+ session.socket.sendPacket(toSend)
+ }
+
+ fun onPacketReceived(session: LoginSession, packet: ServerPacket): Boolean {
+ if (expectationClass.isInstance(packet) && session === this.fromSession) {
+ @Suppress("UNCHECKED_CAST")
+ expect(packet as P)
+ future.complete(Unit)
+ return true
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
index e1d671231..d0d048630 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ClientPacket.kt
@@ -1,3 +1,5 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
+
package net.mamoe.mirai.network.packet
import lombok.Getter
@@ -12,12 +14,12 @@ import java.security.MessageDigest
/**
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
@Getter
val idHex: String
- var encoded: Boolean = false
+ private var encoded: Boolean = false
init {
val annotation = this.javaClass.getAnnotation(PacketId::class.java)
@@ -84,7 +86,6 @@ abstract class ClientPacket : ByteArrayDataOutputStream(), Packet {
}
-@ExperimentalUnsignedTypes
@Throws(IOException::class)
fun DataOutputStream.writeIP(ip: String) {
for (s in ip.trim().split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
@@ -97,14 +98,13 @@ fun DataOutputStream.writeTime() {
this.writeInt(System.currentTimeMillis().toInt())
}
-@ExperimentalUnsignedTypes
@Throws(IOException::class)
-fun DataOutputStream.writeHex(hex: String) {
- for (s in hex.trim().split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
+fun DataOutputStream.writeHex(uHex: String) {
+ for (s in uHex.trim().split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
if (s.isEmpty()) {
continue
}
- this.writeByte(s.toUByte(16).toByte().toInt())
+ this.writeByte(s.toUByte(16).toInt())
}
}
@@ -113,15 +113,13 @@ fun DataOutputStream.encryptAndWrite(byteArray: ByteArray, key: ByteArray) {
}
fun DataOutputStream.encryptAndWrite(key: ByteArray, encoder: (ByteArrayDataOutputStream) -> Unit) {
- this.write(TEA.encrypt(ByteArrayDataOutputStream().let { encoder(it); it.toByteArray() }, key))
+ this.write(TEA.encrypt(ByteArrayDataOutputStream().also(encoder).toByteArray(), key))
}
-@ExperimentalUnsignedTypes
fun DataOutputStream.encryptAndWrite(keyHex: String, encoder: (ByteArrayDataOutputStream) -> Unit) {
this.encryptAndWrite(keyHex.hexToBytes(), encoder)
}
-@ExperimentalUnsignedTypes
@Throws(IOException::class)
fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray) {
ByteArrayDataOutputStream().let {
@@ -146,12 +144,10 @@ fun DataOutputStream.writeTLV0006(qq: Long, password: String, loginTime: Int, lo
}
}
-@ExperimentalUnsignedTypes
-@TestedSuccessfully
+@Tested
fun DataOutputStream.writeCRC32() = writeCRC32(getRandomByteArray(16))
-@ExperimentalUnsignedTypes
fun DataOutputStream.writeCRC32(key: ByteArray) {
key.let {
write(it)//key
@@ -159,8 +155,8 @@ fun DataOutputStream.writeCRC32(key: ByteArray) {
}
}
-@ExperimentalUnsignedTypes
-@TestedSuccessfully
+
+@Tested
fun DataOutputStream.writeDeviceName(random: Boolean = false) {
val deviceName: String = if (random) {
String(getRandomByteArray(10))
@@ -185,7 +181,7 @@ fun Int.toByteArray(): ByteArray = byteArrayOf(
/**
* 255u -> 00 00 00 FF
*/
-@ExperimentalUnsignedTypes
+
fun UInt.toByteArray(): ByteArray = byteArrayOf(
(this.shr(24) and 255u).toByte(),
(this.shr(16) and 255u).toByte(),
@@ -193,24 +189,14 @@ fun UInt.toByteArray(): ByteArray = byteArrayOf(
(this.shr(0) and 255u).toByte()
)
-/**
- * 255 -> FF 00 00 00
- */
-fun Int.toLByteArray(): ByteArray = byteArrayOf(
- (this.ushr(0) and 0xFF).toByte(),
- (this.ushr(8) and 0xFF).toByte(),
- (this.ushr(16) and 0xFF).toByte(),
- (this.ushr(24) and 0xFF).toByte()
-)
-@ExperimentalUnsignedTypes
-fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUByteArray().toUHexString(separator)
+fun Int.toUHexString(separator: String = " "): String = this.toByteArray().toUHexString(separator)
internal fun md5(str: String): ByteArray = MessageDigest.getInstance("MD5").digest(str.toByteArray())
internal fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
-@ExperimentalUnsignedTypes
+
@Throws(IOException::class)
fun DataOutputStream.writeZero(count: Int) {
repeat(count) {
@@ -225,33 +211,26 @@ fun DataOutputStream.writeRandom(length: Int) {
}
}
-@ExperimentalUnsignedTypes
+
@Throws(IOException::class)
fun DataOutputStream.writeQQ(qq: Long) {
this.write(qq.toUInt().toByteArray())
}
-@ExperimentalUnsignedTypes
@Throws(IOException::class)
fun DataOutputStream.writeGroup(groupIdOrGroupNumber: Long) {
this.write(groupIdOrGroupNumber.toUInt().toByteArray())
}
-fun DataOutputStream.writeVarByteArray(byteArray: ByteArray) {
+fun DataOutputStream.writeLVByteArray(byteArray: ByteArray) {
this.writeShort(byteArray.size)
this.write(byteArray)
}
-fun DataOutputStream.writeVarString(str: String) {
- this.writeVarByteArray(str.toByteArray())
+fun DataOutputStream.writeLVString(str: String) {
+ this.writeLVByteArray(str.toByteArray())
}
-fun DataOutputStream.writeVarShort(short: Int) {
- this.writeByte(0x02)
- this.writeShort(short)
-}
-
-fun DataOutputStream.writeVarInt(int: Int) {
- this.writeByte(0x04)
- this.writeInt(int)
+fun DataOutputStream.writeLVHex(hex: String) {
+ this.writeLVByteArray(hex.hexToBytes())
}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
index c208c07f0..d431672f9 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/GradeInfo.kt
@@ -9,7 +9,7 @@ import java.io.DataInputStream
*
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("00 5C")
class ClientAccountInfoRequestPacket(
private val qq: Long,
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt
index 9fcd36304..8a1192842 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Heartbeat.kt
@@ -7,7 +7,7 @@ import java.io.IOException
/**
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("00 58")
class ClientHeartbeatPacket(
private val qq: Long,
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
index 30f8c16b3..a9e61c8ab 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/SKey.kt
@@ -6,9 +6,11 @@ import java.io.DataInputStream
/**
+ * SKey 用于 http api
+ *
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("00 1D")
class ClientSKeyRequestPacket(
private val qq: Long,
@@ -29,7 +31,7 @@ class ClientSKeyRequestPacket(
* @author Him188moe
*/
@PacketId("00 1D")
-@ExperimentalUnsignedTypes
+
class ClientSKeyRefreshmentRequestPacket(
private val qq: Long,
private val sessionKey: ByteArray
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
index 7bb280023..7b779d718 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerEvent.kt
@@ -1,3 +1,5 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.message.FaceID
@@ -19,10 +21,10 @@ import java.util.zip.GZIPInputStream
*
* @author Him188moe
*/
-open class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
+abstract class ServerEventPacket(input: DataInputStream, val packetId: ByteArray, val eventIdentity: ByteArray) : ServerPacket(input) {
@PacketId("00 17")
class Raw(input: DataInputStream, private val packetId: ByteArray) : ServerPacket(input) {
- @ExperimentalUnsignedTypes
+
fun distribute(): ServerEventPacket {
val eventIdentity = this.input.readNBytes(16)
val type = this.input.goto(18).readNBytes(2)
@@ -80,6 +82,7 @@ class ServerGroupUploadFileEventPacket(input: DataInputStream, packetId: ByteArr
}//todo test
}
+@Suppress("EXPERIMENTAL_API_USAGE")
class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray, eventIdentity: ByteArray) : ServerEventPacket(input, packetId, eventIdentity) {
var groupNumber: Long = 0
var qq: Long = 0
@@ -100,16 +103,17 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
OTHER,
}
- @ExperimentalUnsignedTypes
+
override fun decode() {
+ println(this.input.goto(0).readAllBytes().toUHexString())
groupNumber = this.input.goto(51).readInt().toLong()
- qq = this.input.goto(56).readLong().toUInt().toLong()
+ qq = this.input.goto(56).readLong()
val fontLength = this.input.goto(108).readShort()
//println(this.input.goto(110 + fontLength).readNBytesAt(2).toUHexString())//always 00 00
messageType = when (val id = this.input.goto(110 + fontLength + 2).readByte().toInt()) {
0x13 -> MessageType.NORMAL
- 0xE -> MessageType.XML
+ 0x0E -> MessageType.XML
0x06 -> MessageType.AT
@@ -119,7 +123,7 @@ class ServerGroupMessageEventPacket(input: DataInputStream, packetId: ByteArray,
0x19 -> MessageType.ANONYMOUS
else -> {
- MiraiLogger debug ("ServerGroupMessageEventPacket id=$id")
+ MiraiLogger.debug("ServerGroupMessageEventPacket id=$id")
MessageType.OTHER
}
}
@@ -188,7 +192,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
var qq: Long = 0
lateinit var message: MessageChain
- @ExperimentalUnsignedTypes
+
override fun decode() {
input.goto(0)
println()
@@ -222,16 +226,16 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
//00 01 AF 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 F0
//00 01 0C 0B 00 08 00 01 00 04 52 CC F5 D0 FF 00 02 14 4D
- val id1 = FaceID.ofId(readVarNumber().toInt())//可能这个是id, 也可能下面那个
+ val id1 = FaceID.ofId(readLVNumber().toInt())//可能这个是id, 也可能下面那个
this.skip(this.readByte().toLong())
- this.readVarNumber()//某id?
+ this.readLVNumber()//某id?
return Face(id1)
}
0x06 -> {
this.skip(sectionLength - 37 - 1)
val imageId = String(this.readNBytes(36))
this.skip(1)//0x41
- return Image(imageId)
+ return Image("{$imageId}.jpg")//todo 如何确定文件后缀??
}
else -> null
}
@@ -291,8 +295,7 @@ B1 89 BE 09 8F 00 1A E5 00 0B 03 A2 09 90 BB 7A 1F 40 00 A6 00 00 00 20 00 05 00
* 告知服务器已经收到数据
*/
@PacketId("")//随后写入
-@ExperimentalUnsignedTypes
-class ClientMessageResponsePacket(
+class ClientEventResponsePacket(
private val qq: Long,
private val packetIdFromServer: ByteArray,//4bytes
private val sessionKey: ByteArray,
@@ -329,7 +332,7 @@ class ServerFriendMessageEventPacket(input: DataInputStream, packetId: ByteArray
lateinit var message: String
- @ExperimentalUnsignedTypes
+
override fun decode() {
//start at Sep1.0:27
qq = input.readIntAt(0)
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
index eaed7769f..580664e0f 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/ServerPacket.kt
@@ -1,12 +1,23 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
+//to simplify code
+
package net.mamoe.mirai.network.packet
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.events.network.ServerPacketReceivedEvent
+import net.mamoe.mirai.event.hookWhile
import net.mamoe.mirai.network.packet.PacketNameFormatter.adjustName
import net.mamoe.mirai.network.packet.action.ServerCanAddFriendResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendFriendMessageResponsePacket
import net.mamoe.mirai.network.packet.action.ServerSendGroupMessageResponsePacket
+import net.mamoe.mirai.network.packet.image.ServerTryUploadGroupImageResponsePacket
import net.mamoe.mirai.network.packet.login.*
+import net.mamoe.mirai.task.MiraiThreadPool
import net.mamoe.mirai.utils.*
import java.io.DataInputStream
+import java.io.EOFException
+import kotlin.reflect.KClass
/**
* @author Him188moe
@@ -45,7 +56,6 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
companion object {
- @ExperimentalUnsignedTypes
fun ofByteArray(bytes: ByteArray): ServerPacket {
val stream = bytes.dataInputStream()
@@ -72,7 +82,7 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
println(bytes.size)
return ServerLoginResponseFailedPacket(when (bytes.size) {
- 63, 319, 135, 351 -> LoginState.WRONG_PASSWORD//这四个其中一个也是被冻结
+ 63, 319, 135, 351 -> LoginState.WRONG_PASSWORD//这四个其中一个是被冻结
//135 -> LoginState.RETYPE_PASSWORD
279 -> LoginState.BLOCKED
263 -> LoginState.UNKNOWN_QQ_NUMBER
@@ -111,6 +121,8 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
"00 A7" -> ServerCanAddFriendResponsePacket(stream)
+ "03 88" -> ServerTryUploadGroupImageResponsePacket.Encrypted(stream)
+
else -> throw IllegalArgumentException(idHex)
}
}.apply { this.idHex = idHex }
@@ -118,9 +130,8 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
}
- @ExperimentalUnsignedTypes
override fun toString(): String {
- return adjustName(this.javaClass.simpleName + "(${this.getFixedId()})") + this.getAllDeclaredFields().filterNot { it.name == "idHex" || it.name == "encoded" }.joinToString(", ", "{", "}") {
+ return adjustName(this.javaClass.simpleName + "(${this.getFixedId()})") + this.getAllDeclaredFields().filterNot { it.name == "idHex" || it.name == "idByteArray" || it.name == "encoded" }.joinToString(", ", "{", "}") {
it.trySetAccessible(); it.name + "=" + it.get(this).let { value ->
when (value) {
is ByteArray -> value.toUHexString()
@@ -145,38 +156,37 @@ abstract class ServerPacket(val input: DataInputStream) : Packet {
return decryptAsByteArray(key).dataInputStream()
}
- @ExperimentalUnsignedTypes
+
fun decryptBy(keyHex: String): DataInputStream {
return this.decryptBy(keyHex.hexToBytes())
}
fun decryptBy(key1: ByteArray, key2: ByteArray): DataInputStream {
- return TEA.decrypt(this.decryptAsByteArray(key1), key2).dataInputStream();
+ return TEA.decrypt(this.decryptAsByteArray(key1), key2).dataInputStream()
}
- @ExperimentalUnsignedTypes
+
fun decryptBy(key1: String, key2: ByteArray): DataInputStream {
return this.decryptBy(key1.hexToBytes(), key2)
}
- @ExperimentalUnsignedTypes
+
fun decryptBy(key1: ByteArray, key2: String): DataInputStream {
return this.decryptBy(key1, key2.hexToBytes())
}
- @ExperimentalUnsignedTypes
+
fun decryptBy(keyHex1: String, keyHex2: String): DataInputStream {
return this.decryptBy(keyHex1.hexToBytes(), keyHex2.hexToBytes())
}
- private fun decryptAsByteArray(key: ByteArray): ByteArray {
+ fun decryptAsByteArray(key: ByteArray): ByteArray {
input.goto(14)
return TEA.decrypt(input.readAllBytes().cutTail(1), key)
}
}
-@ExperimentalUnsignedTypes
fun DataInputStream.readIP(): String {
var buff = ""
for (i in 0..3) {
@@ -207,7 +217,7 @@ fun ByteArray.dataInputStream(): DataInputStream = DataInputStream(this.inputStr
*/
infix fun DataInputStream.goto(position: N): DataInputStream {
this.reset()
- this.skip(position.toLong());
+ this.skip(position.toLong())
return this
}
@@ -221,7 +231,7 @@ fun DataInputStream.readNBytes(length: N): ByteArray {
}
-fun DataInputStream.readVarNumber(): Number {
+fun DataInputStream.readLVNumber(): Number {
return when (this.readShort().toInt()) {
1 -> this.readByte()
2 -> this.readShort()
@@ -238,23 +248,92 @@ fun DataInputStream.readNBytesIn(range: IntRange): ByteArray {
fun DataInputStream.readIntAt(position: N): Int {
this.goto(position)
- return this.readInt();
+ return this.readInt()
}
-@ExperimentalUnsignedTypes
+
fun DataInputStream.readUIntAt(position: N): UInt {
this.goto(position)
- return this.readNBytes(4).toUInt();
+ return this.readNBytes(4).toUInt()
}
fun DataInputStream.readByteAt(position: N): Byte {
this.goto(position)
- return this.readByte();
+ return this.readByte()
}
fun DataInputStream.readShortAt(position: N): Short {
this.goto(position)
- return this.readShort();
+ return this.readShort()
}
+
+@JvmSynthetic
+fun DataInputStream.gotoWhere(matcher: UByteArray): DataInputStream {
+ return this.gotoWhere(matcher.toByteArray())
+}
+
+/**
+ * 去往下一个含这些连续字节的位置
+ */
+@Throws(EOFException::class)
+fun DataInputStream.gotoWhere(matcher: ByteArray): DataInputStream {
+ require(matcher.isNotEmpty())
+
+ loop@
+ do {
+ val byte = this.readByte()
+ if (byte == matcher[0]) {
+ //todo mark here
+ for (i in 1 until matcher.size) {
+ val b = this.readByte()
+ if (b != matcher[i]) {
+ continue@loop //todo goto mark
+ }
+ return this
+ }
+ }
+ } while (true)
+}
+
+
+@Suppress("UNCHECKED_CAST")
+internal fun Bot.waitForPacket(packetClass: KClass
, timeoutMillis: Long = Long.MAX_VALUE, timeout: () -> Unit = {}) {
+ var got = false
+ ServerPacketReceivedEvent::class.hookWhile {
+ if (packetClass.isInstance(it.packet) && it.bot === this) {
+ 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)
+ }
+ }
+}
+
+/*
+@Throws(EOFException::class)
+fun DataInputStream.gotoWhere(matcher: ByteArray) {
+ require(matcher.isNotEmpty())
+ do {
+ val byte = this.readByte()
+ if (byte == matcher[0]) {
+ for (i in 1 until matcher.size){
+
+ }
+ }
+ } while (true)
+}*/
+
fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt
index 2484adfb8..f51c2d824 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Session.kt
@@ -1,9 +1,7 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
-import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
-import net.mamoe.mirai.utils.getRandomByteArray
import net.mamoe.mirai.utils.lazyEncode
import java.io.DataInputStream
import java.net.InetAddress
@@ -11,64 +9,67 @@ import java.net.InetAddress
/**
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("08 28 04 34")
class ClientSessionRequestPacket(
private val qq: Long,
private val serverIp: String,
private val token38: ByteArray,
private val token88: ByteArray,
- private val encryptionKey: ByteArray,
- private val tlv0105: ByteArray
+ private val encryptionKey: ByteArray
) : ClientPacket() {
override fun encode() {
this.writeQQ(qq)
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
this.writeHex("00 38")
this.write(token38)
- this.write(TEA.encrypt(object : ByteArrayDataOutputStream() {
- override fun toByteArray(): ByteArray {
- this.writeHex("00 07 00 88")
- this.write(token88)
- this.writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00")
- this.writeIP(serverIp)
- this.writeHex("1F 40 00 00 00 00 00 15 00 30 00 01")//fix1
- this.writeHex("01 92 A5 D2 59 00 10 54 2D CF 9B 60 BF BB EC 0D D4 81 CE 36 87 DE 35 02 AE 6D ED DC 00 10 ")
- this.writeHex(Protocol.fix0836)
- this.writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00")
- this.writeHex(Protocol.constantData1)
- this.writeHex(Protocol.constantData2)
- this.writeQQ(qq)
- this.writeHex("00 00 00 00 00 1F 00 22 00 01")
- this.writeHex("1A 68 73 66 E4 BA 79 92 CC C2 D4 EC 14 7C 8B AF 43 B0 62 FB 65 58 A9 EB 37 55 1D 26 13 A8 E5 3D")//device ID
- this.write(tlv0105)
- this.writeHex("01 0B 00 85 00 02")
- this.writeHex("B9 ED EF D7 CD E5 47 96 7A B5 28 34 CA 93 6B 5C")//fix2
- this.write(getRandomByteArray(1))
- this.writeHex("10 00 00 00 00 00 00 00 02")
+ this.encryptAndWrite(encryptionKey) {
+ it.writeHex("00 07 00 88")
+ it.write(token88)
+ it.writeHex("00 0C 00 16 00 02 00 00 00 00 00 00 00 00 00 00")
+ it.writeIP(serverIp)
+ it.writeHex("1F 40 00 00 00 00 00 15 00 30 00 01")//fix1
+ it.writeHex("01 92 A5 D2 59 00 10 54 2D CF 9B 60 BF BB EC 0D D4 81 CE 36 87 DE 35 02 AE 6D ED DC 00 10 ")
+ it.writeHex(Protocol.fix0836)
+ it.writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00")
+ it.writeHex(Protocol.constantData1)
+ it.writeHex(Protocol.constantData2)
+ it.writeQQ(qq)
+ it.writeHex("00 00 00 00 00 1F 00 22 00 01")
+ it.writeHex("1A 68 73 66 E4 BA 79 92 CC C2 D4 EC 14 7C 8B AF 43 B0 62 FB 65 58 A9 EB 37 55 1D 26 13 A8 E5 3D")//device ID
- //fix3
- this.writeHex("00 63 3E 00 63 02 04 03 06 02 00 04 00 52 D9 00 00 00 00 A9 58 3E 6D 6D 49 AA F6 A6 D9 33 0A E7 7E 36 84 03 01 00 00 68 20 15 8B 00 00 01 02 00 00 03 00 07 DF 00 0A 00 0C 00 01 00 04 00 03 00 04 20 5C 00")
- this.write(getRandomByteArray(32))//md5 32
- this.writeHex("68")
+ //tlv0106
+ 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)
- this.writeHex("00 00 00 00 00 2D 00 06 00 01")
- this.writeIP(InetAddress.getLocalHost().hostAddress)
+ it.writeHex("01 0B 00 85 00 02")
+ it.writeHex("B9 ED EF D7 CD E5 47 96 7A B5 28 34 CA 93 6B 5C")//fix2
+ it.writeRandom(1)
+ it.writeHex("10 00 00 00 00 00 00 00 02")
- return super.toByteArray()
- }
- }.toByteArray(), encryptionKey))
+ //fix3
+ it.writeHex("00 63 3E 00 63 02 04 03 06 02 00 04 00 52 D9 00 00 00 00 A9 58 3E 6D 6D 49 AA F6 A6 D9 33 0A E7 7E 36 84 03 01 00 00 68 20 15 8B 00 00 01 02 00 00 03 00 07 DF 00 0A 00 0C 00 01 00 04 00 03 00 04 20 5C 00")
+ it.writeRandom(32)//md5 32
+ it.writeHex("68")
+
+ it.writeHex("00 00 00 00 00 2D 00 06 00 01")
+ it.writeIP(InetAddress.getLocalHost().hostAddress)
+ }
}
}
/**
* @author Him188moe
*/
+@PacketId("08 28 04 34")
class ServerSessionKeyResponsePacket(inputStream: DataInputStream, private val dataLength: Int) : ServerPacket(inputStream) {
lateinit var sessionKey: ByteArray
lateinit var tlv0105: ByteArray
- @ExperimentalUnsignedTypes
+
override fun decode() {
when (dataLength) {
407 -> {
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
index c201eac7b..1b9945670 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/Touch.kt
@@ -1,6 +1,7 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
+import net.mamoe.mirai.network.packet.login.ClientPasswordSubmissionPacket
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
import net.mamoe.mirai.utils.hexToBytes
@@ -11,11 +12,12 @@ import java.io.IOException
/**
* A packet received when logging in, used to redirect server address
*
- * @see net.mamoe.mirai.network.packet.client.login.ClientServerRedirectionPacket
- * @see net.mamoe.mirai.network.packet.client.login.ClientPasswordSubmissionPacket
+ * @see ClientServerRedirectionPacket
+ * @see ClientPasswordSubmissionPacket
*
* @author Him188moe
*/
+@Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
@PacketId("08 25 31 01")
class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inputStream) {
var serverIP: String? = null
@@ -29,7 +31,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
TYPE_08_25_31_02,
}
- @ExperimentalUnsignedTypes
+
override fun decode() {
when (val id = input.readByte().toUByte().toInt()) {
0xFE -> {
@@ -52,7 +54,7 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
}
class Encrypted(private val type: Type, inputStream: DataInputStream) : ServerPacket(inputStream) {
- @ExperimentalUnsignedTypes
+
fun decrypt(): ServerTouchResponsePacket = ServerTouchResponsePacket(decryptBy(when (type) {
Type.TYPE_08_25_31_02 -> Protocol.redirectionKey.hexToBytes()
Type.TYPE_08_25_31_01 -> Protocol.key0825.hexToBytes()
@@ -65,10 +67,10 @@ class ServerTouchResponsePacket(inputStream: DataInputStream) : ServerPacket(inp
*
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("08 25 31 01")
class ClientTouchPacket(private val qq: Long, private val serverIp: String) : ClientPacket() {
- @ExperimentalUnsignedTypes
+
@Throws(IOException::class)
override fun encode() {
this.writeQQ(qq)
@@ -80,7 +82,7 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl
it.writeHex(Protocol.constantData2)
it.writeQQ(qq)
it.writeHex("00 00 00 00 03 09 00 08 00 01")
- it.writeIP(serverIp);
+ it.writeIP(serverIp)
it.writeHex("00 02 00 36 00 12 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 14 00 1D 01 02 00 19")
it.writeHex(Protocol.publicKey)
}
@@ -92,10 +94,10 @@ class ClientTouchPacket(private val qq: Long, private val serverIp: String) : Cl
*
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("08 25 31 02")
class ClientServerRedirectionPacket(private val serverIP: String, private val qq: Long) : ClientPacket() {
- @ExperimentalUnsignedTypes
+
override fun encode() {
this.writeQQ(qq)
this.writeHex(Protocol.fixVer)
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
index 74e6813a2..484a19e42 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/VerificationCode.kt
@@ -1,13 +1,15 @@
package net.mamoe.mirai.network.packet
import net.mamoe.mirai.network.Protocol
-import net.mamoe.mirai.utils.*
+import net.mamoe.mirai.utils.TEA
+import net.mamoe.mirai.utils.Tested
+import net.mamoe.mirai.utils.hexToBytes
import java.io.DataInputStream
/**
* 客户端请求验证码图片数据的第几部分
*/
-@ExperimentalUnsignedTypes
+
@PacketId("00 BA 31")
class ClientVerificationCodeTransmissionRequestPacket(
private val packetId: Int,
@@ -16,11 +18,8 @@ class ClientVerificationCodeTransmissionRequestPacket(
private val verificationSequence: Int,
private val token00BA: ByteArray
) : ClientPacket() {
- @TestedSuccessfully
+ @Tested
override fun encode() {
- MiraiLogger debug "packetId=$packetId"
- MiraiLogger debug "verificationSequence=$verificationSequence"
-
this.writeByte(packetId)//part of packet id
this.writeQQ(qq)
@@ -47,7 +46,7 @@ class ClientVerificationCodeTransmissionRequestPacket(
* 提交验证码
*/
@PacketId("00 BA 32")
-@ExperimentalUnsignedTypes
+
class ClientVerificationCodeSubmitPacket(
private val packetIdLast: Int,
private val qq: Long,
@@ -90,39 +89,11 @@ class ClientVerificationCodeSubmitPacket(
}
}
-@ExperimentalUnsignedTypes
-fun main() {
- val token0825 = "6E AF F9 2C 20 2B DE 21 B6 13 6F 26 43 F4 04 7B 1F 88 08 4E 8E BE E5 D1 3F E7 93 DE DD E0 6E 38 65 C7 C7 D3 20 7D AC 73 AD F9 85 F9 CC 2A 2C 26 C6 B1 5B FD 34 3F D4 F2".hexToBytes()
- val verificationCode = "AAAA"
- val verificationToken = "84 2D 1D 9D 07 04 34 80 17 9E 3F 58 02 20 9A 1C 22 D0 73 7D 8A 90 1B 2F F8 E6 79 A6 84 2F 98 F5 1E 66 3D 9A 24 59 18 34 42 BD 45 DA E1 22 2D BC 2D 36 80 86 AD 44 C2 94".hexToBytes()
-//00 02 00 00 08 04 01 E0 00 00 04 53 00 00 00 01 00 00 15 85 01 00 38 6E AF F9 2C 20 2B DE 21 B6 13 6F 26 43 F4 04 7B 1F 88 08 4E 8E BE E5 D1 3F E7 93 DE DD E0 6E 38 65 C7 C7 D3 20 7D AC 73 AD F9 85 F9 CC 2A 2C 26 C6 B1 5B FD 34 3F D4 F2 01 03 00 19 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 14 00 05 00 00 00 00 00 04 58 51 4E 44 00 38 84 2D 1D 9D 07 04 34 80 17 9E 3F 58 02 20 9A 1C 22 D0 73 7D 8A 90 1B 2F F8 E6 79 A6 84 2F 98 F5 1E 66 3D 9A 24 59 18 34 42 BD 45 DA E1 22 2D BC 2D 36 80 86 AD 44 C2 94 00 10 69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A
- ByteArrayDataOutputStream().let {
- it.writeHex("00 02 00 00 08 04 01 E0")
- it.writeHex(Protocol.constantData2)
- it.writeHex("01 00 38")
- it.write(token0825)
- it.writeHex("01 03")
-
- it.writeShort(25)
- it.writeHex(Protocol.publicKey)
-
- it.writeHex("14 00 05 00 00 00 00 00 04")
- it.write(verificationCode.substring(0..3).toByteArray())
- it.writeHex("00 38")
- it.write(verificationToken)
-
- it.writeHex("00 10")
- it.writeHex(Protocol.key00BAFix)
-
- println(it.toByteArray().toUHexString())
- }
-}
-
/**
* 刷新验证码
*/
@PacketId("00 BA 31")
-@ExperimentalUnsignedTypes
+
class ClientVerificationCodeRefreshPacket(
private val packetIdLast: Int,
private val qq: Long,
@@ -152,7 +123,7 @@ class ClientVerificationCodeRefreshPacket(
}
/**
- * 验证码输入错误
+ * 验证码输入错误, 同时也会给一部分验证码
*/
@PacketId("00 BA 32")
class ServerVerificationCodeWrongPacket(input: DataInputStream, dataSize: Int, packetId: ByteArray) : ServerVerificationCodeTransmissionPacket(input, dataSize, packetId)
@@ -171,7 +142,7 @@ open class ServerVerificationCodeTransmissionPacket(input: DataInputStream, priv
lateinit var token00BA: ByteArray//40 bytes
var packetIdLast: Int = 0
- @ExperimentalUnsignedTypes
+
override fun decode() {
this.verificationToken = this.input.readNBytesAt(10, 56)
@@ -210,7 +181,7 @@ class ServerVerificationCodeCorrectPacket(input: DataInputStream) : ServerVerifi
lateinit var token00BA: ByteArray//56 bytes
- @ExperimentalUnsignedTypes
+
override fun decode() {
token00BA = this.input.readNBytesAt(10, 56)
}
@@ -220,7 +191,7 @@ abstract class ServerVerificationCodePacket(input: DataInputStream) : ServerPack
@PacketId("00 BA")
class Encrypted(input: DataInputStream, private val id: String) : ServerPacket(input) {
- @ExperimentalUnsignedTypes
+
fun decrypt(): ServerVerificationCodePacket {
this.input goto 14
val data = TEA.decrypt(this.input.readAllBytes().cutTail(1), Protocol.key00BA.hexToBytes())
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/AddContact.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/AddContact.kt
index 2508db00a..c19724dba 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/AddContact.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/AddContact.kt
@@ -1,3 +1,5 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
+
package net.mamoe.mirai.network.packet.action
import net.mamoe.mirai.network.Protocol
@@ -13,7 +15,7 @@ import java.util.*
* @author Him188moe
*/
@PacketId("00 A7")
-@ExperimentalUnsignedTypes
+
class ClientCanAddFriendPacket(
val bot: Long,
val qq: Long,
@@ -47,7 +49,7 @@ class ServerCanAddFriendResponsePacket(input: DataInputStream) : ServerPacket(in
FAILED,
}
- @ExperimentalUnsignedTypes
+
override fun decode() {
val data = input.goto(0).readAllBytes()
if (data.size == 99) {
@@ -78,7 +80,7 @@ class ServerCanAddFriendResponsePacket(input: DataInputStream) : ServerPacket(in
* 请求添加好友
*/
@PacketId("00 AE")
-@ExperimentalUnsignedTypes
+
class ClientAddFriendPacket(
val bot: Long,
val qq: Long,
@@ -111,7 +113,7 @@ class ServerAddGroupResponsePacket(input: DataInputStream) : ServerAddContactRes
/**
* 添加好友/群的回复
*/
-open class ServerAddContactResponsePacket(input: DataInputStream) : ServerPacket(input) {
+abstract class ServerAddContactResponsePacket(input: DataInputStream) : ServerPacket(input) {
class Raw(input: DataInputStream) : ServerPacket(input) {
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt
index 509726ffd..d60aeacb1 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendFriendMessagePacket.kt
@@ -10,7 +10,7 @@ import java.io.DataInputStream
* @author Him188moe
*/
@PacketId("00 CD")
-@ExperimentalUnsignedTypes
+
class ClientSendFriendMessagePacket(
private val botQQ: Long,
private val targetQQ: Long,
@@ -19,6 +19,7 @@ class ClientSendFriendMessagePacket(
) : ClientPacket() {
override fun encode() {
this.writeRandom(2)//part of packet id
+
this.writeQQ(botQQ)
this.writeHex(Protocol.fixVer2)
@@ -40,7 +41,6 @@ class ClientSendFriendMessagePacket(
it.writeHex(Protocol.friendMessageConst1)//... 85 E9 BB 91
it.writeZero(2)
-
it.write(message.toByteArray())
/*
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt
index 17f84f988..bc480103d 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/action/ClientSendGroupMessagePacket.kt
@@ -1,5 +1,6 @@
package net.mamoe.mirai.network.packet.action
+import net.mamoe.mirai.message.defaults.MessageChain
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.toUHexString
@@ -9,12 +10,11 @@ import java.io.DataInputStream
* @author Him188moe
*/
@PacketId("00 02")
-@ExperimentalUnsignedTypes
class ClientSendGroupMessagePacket(
private val groupId: Long,//不是 number
private val botQQ: Long,
private val sessionKey: ByteArray,
- private val message: String
+ private val message: MessageChain
) : ClientPacket() {
override fun encode() {
this.writeRandom(2)//part of packet id
@@ -25,20 +25,24 @@ class ClientSendGroupMessagePacket(
val bytes = message.toByteArray()
it.writeByte(0x2A)
it.writeGroup(groupId)
- it.writeShort(56 + bytes.size)
- it.writeHex("00 01 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
+ it.writeShort(50 + bytes.size)
+ it.writeHex("00 01 01")
+ it.writeHex("00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")
+
it.writeTime()
it.writeRandom(4)
- it.writeHex("Protocol.messageConst1")
+ it.writeHex("00 00 00 00 09 00 86")
+ it.writeHex(Protocol.friendMessageConst1)
it.writeZero(2)
//messages
- it.writeByte(0x01)
+ it.write(bytes)
+ /*it.writeByte(0x01)
it.writeShort(bytes.size + 3)
it.writeByte(0x01)
it.writeShort(bytes.size)
- it.write(bytes)
+ it.write(bytes)*/
println(it.toByteArray().toUHexString())
}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/ClientGetGroupImageIDPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/ClientGetGroupImageIDPacket.kt
deleted file mode 100644
index fec7478db..000000000
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/ClientGetGroupImageIDPacket.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.mamoe.mirai.network.packet.image
-
-import net.mamoe.mirai.network.packet.*
-import net.mamoe.mirai.utils.writeProtoInt
-import java.awt.image.BufferedImage
-
-/**
- * 查询群消息的 image id.
- * That is, 查询服务器上是否有这个图片, 有就返回 id, 没有就需要上传
- *
- * @author Him188moe
- */
-@PacketId("03 88")
-@ExperimentalUnsignedTypes
-class ClientGetGroupImageIDPacket(
- val bot: Long,
- val sessionKey: ByteArray,
- val group: Long,
- val image: BufferedImage
-) : ClientPacket() {
- override fun encode() {
- this.writeRandom(2)
-
- this.writeQQ(bot)
- this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
- this.encryptAndWrite(sessionKey) {
- it.writeHex("00 00 00 07 00 00 00 5E 08 01 12 03 98 01 01 10 01 1A")
- it.writeHex("5A")
- it.writeHex("08")
- it.writeProtoInt(group)
- it.writeHex("08")
- it.writeProtoInt(image.height)
- }
- }
-}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/UploadGroupImage.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/UploadGroupImage.kt
new file mode 100644
index 000000000..839f9c8a9
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/image/UploadGroupImage.kt
@@ -0,0 +1,132 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
+
+package net.mamoe.mirai.network.packet.image
+
+import net.mamoe.mirai.network.packet.*
+import net.mamoe.mirai.utils.toByteArray
+import net.mamoe.mirai.utils.writeUVarInt
+import java.awt.image.BufferedImage
+import java.io.DataInputStream
+
+/**
+ * 请求上传图片. 将发送图片的 md5, size.
+ * 服务器返回以下之一:
+ * - 服务器已经存有这个图片 [ServerTryUploadGroupImageFailedPacket]
+ * - 服务器未存有, 返回一个 key 用于客户端上传 [ServerTryUploadGroupImageSuccessPacket]
+ *
+ * @author Him188moe
+ */
+@PacketId("03 88")
+class ClientTryGetImageIDPacket(
+ private val botNumber: Long,
+ private val sessionKey: ByteArray,
+ private val groupNumberOrQQNumber: Long,//todo 为什么还要有qq number呢? bot不就是了么
+ private val image: BufferedImage
+) : ClientPacket() {
+ override fun encode() {
+ this.writeRandom(2)
+
+ this.writeQQ(botNumber)
+ this.writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
+
+ val byteArray = image.toByteArray()
+ this.encryptAndWrite(sessionKey) {
+ it.writeZero(3)
+
+ it.writeHex("07 00")
+
+ it.writeZero(2)
+
+ it.writeHex("5E")
+ it.writeHex("08")
+ it.writeHex("01 12 03 98 01 01 10 01")
+
+ it.writeHex("1A")
+ it.writeHex("5A")
+
+ it.writeHex("08")
+ it.writeUVarInt(groupNumberOrQQNumber)
+
+ it.writeHex("10")
+ it.writeUVarInt(botNumber)
+
+ it.writeHex("18 00")
+
+ it.writeHex("22")
+ it.writeHex("10")
+ it.write(md5(byteArray))
+
+ it.writeHex("28")
+ it.writeUVarInt(byteArray.size.toUInt())
+
+ it.writeHex("32")
+ it.writeHex("1A")
+ it.writeHex("37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00")
+
+ it.writeHex("38 01")
+
+ it.writeHex("48 01")
+
+ it.writeHex("50")
+ it.writeUVarInt(image.width.toUInt())
+ it.writeHex("58")
+ it.writeUVarInt(image.height.toUInt())
+
+ it.writeHex("60 04")
+
+ it.writeHex("6A")
+ it.writeHex("05")
+ it.writeHex("32 36 36 35 36")
+
+ it.writeHex("70 00")
+
+ it.writeHex("78 03")
+
+ it.writeHex("80 01")
+
+ it.writeHex("00")
+ }
+ }
+}
+
+abstract class ServerTryUploadGroupImageResponsePacket(input: DataInputStream) : ServerPacket(input) {
+
+ class Encrypted(input: DataInputStream) : ServerPacket(input) {
+ fun decrypt(sessionKey: ByteArray): ServerTryUploadGroupImageResponsePacket {
+ val data = this.decryptAsByteArray(sessionKey)
+ println(data.size)
+ println(data.size)
+ if (data.size == 209) {
+ return ServerTryUploadGroupImageSuccessPacket(data.dataInputStream()).setId(this.idHex)
+ }
+
+ return ServerTryUploadGroupImageFailedPacket(data.dataInputStream())
+ }
+ }
+}
+
+/**
+ * 服务器未存有图片, 返回一个 key 用于客户端上传
+ */
+class ServerTryUploadGroupImageSuccessPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) {
+ lateinit var uKey: ByteArray
+
+
+ override fun decode() {
+ uKey = this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u)).readNBytes(128)
+ }
+}
+
+/**
+ * 服务器已经存有这个图片
+ */
+class ServerTryUploadGroupImageFailedPacket(input: DataInputStream) : ServerTryUploadGroupImageResponsePacket(input) {
+ override fun decode() {
+
+ }
+}
+
+fun main() {
+
+ println(0xff)
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt
index d817bce50..3d48d48e6 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientChangeOnlineStatusPacket.kt
@@ -9,7 +9,7 @@ import net.mamoe.mirai.utils.ClientLoginStatus
*
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
@PacketId("00 EC")
class ClientChangeOnlineStatusPacket(
private val qq: Long,
@@ -29,4 +29,6 @@ class ClientChangeOnlineStatusPacket(
it.writeHex("00 01 00 01 00 04 00 00 00 00")
}
}
-}
\ No newline at end of file
+}
+
+
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
index ccd4edbe1..cb6807a56 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ClientLogin.kt
@@ -4,7 +4,7 @@ import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.*
import net.mamoe.mirai.utils.ByteArrayDataOutputStream
import net.mamoe.mirai.utils.TEA
-import net.mamoe.mirai.utils.TestedSuccessfully
+import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.hexToBytes
import java.io.DataOutputStream
@@ -14,8 +14,8 @@ import java.io.DataOutputStream
* @author Him188moe
*/
@PacketId("08 36 31 03")
-@ExperimentalUnsignedTypes
-@TestedSuccessfully
+
+@Tested
class ClientPasswordSubmissionPacket(
private val qq: Long,
private val password: String,
@@ -24,7 +24,7 @@ class ClientPasswordSubmissionPacket(
private val tgtgtKey: ByteArray,
private val token0825: ByteArray
) : ClientPacket() {
- @ExperimentalUnsignedTypes
+
override fun encode() {
this.writeQQ(qq)
this.writeHex(Protocol.passwordSubmissionKey1)
@@ -40,21 +40,21 @@ class ClientPasswordSubmissionPacket(
}
@PacketId("08 36 31 04")
-@ExperimentalUnsignedTypes
+
class ClientLoginResendPacket3104(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv0006: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv0006)
@PacketId("08 36 31 05")
-@ExperimentalUnsignedTypes
+
class ClientLoginResendPacket3105(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, null)
@PacketId("08 36 31 06")
-@ExperimentalUnsignedTypes
+
class ClientLoginResendPacket3106(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, token00BA: ByteArray, tlv0006: ByteArray? = null)
: ClientLoginResendPacket(qq, password, loginTime, loginIP, tgtgtKey, token0825, token00BA, tlv0006)
-@ExperimentalUnsignedTypes
+
open class ClientLoginResendPacket internal constructor(
val qq: Long,
val password: String,
@@ -93,7 +93,7 @@ open class ClientLoginResendPacket internal constructor(
/**
* @author Him188moe
*/
-@ExperimentalUnsignedTypes
+
private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: Int, loginIP: String, tgtgtKey: ByteArray, token0825: ByteArray, tlv0006: ByteArray? = null) {
//this.writeInt(System.currentTimeMillis().toInt())
@@ -130,7 +130,7 @@ private fun DataOutputStream.writePart1(qq: Long, password: String, loginTime: I
this.writeHex("60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6")//key
}
-@ExperimentalUnsignedTypes
+
private fun DataOutputStream.writePart2() {
this.writeHex("03 12")//tag
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt
index 5f3f8b66f..f5ccba7d6 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/LoginState.kt
@@ -34,11 +34,6 @@ enum class LoginState {
*/
TAKEN_BACK,
- /**
- * 需要验证码登录
- */
- VERIFICATION_CODE,
-
/**
* 未知. 更换服务器或等几分钟再登录可能解决.
*/
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseKeyExchangePacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseKeyExchangePacket.kt
index 9470bc0b6..e92a3828e 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseKeyExchangePacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseKeyExchangePacket.kt
@@ -4,7 +4,7 @@ import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto
-import net.mamoe.mirai.utils.TestedSuccessfully
+import net.mamoe.mirai.utils.Tested
import java.io.DataInputStream
/**
@@ -23,7 +23,7 @@ class ServerLoginResponseKeyExchangePacket(input: DataInputStream, val flag: Fla
var tokenUnknown: ByteArray? = null
lateinit var tgtgtKey: ByteArray//16bytes
- @TestedSuccessfully
+ @Tested
override fun decode() {
this.input.skip(5)
tgtgtKey = this.input.readNBytes(16)//22
@@ -46,8 +46,8 @@ class ServerLoginResponseKeyExchangePacket(input: DataInputStream, val flag: Fla
}
class Encrypted(input: DataInputStream, private val flag: Flag) : ServerPacket(input) {
- @ExperimentalUnsignedTypes
- @TestedSuccessfully
+
+ @Tested
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseKeyExchangePacket {
return ServerLoginResponseKeyExchangePacket(this.decryptBy(Protocol.shareKey, tgtgtKey), flag).setId(this.idHex)
}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
index 438b1259d..8bcbe894f 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponsePasswordVerifiedPacket.kt
@@ -1,3 +1,5 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
@@ -5,7 +7,7 @@ import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.goto
import net.mamoe.mirai.network.packet.readNBytesAt
import net.mamoe.mirai.network.packet.readString
-import net.mamoe.mirai.utils.TestedSuccessfully
+import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.toUHexString
import java.io.DataInputStream
@@ -21,8 +23,8 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
lateinit var encryptionKey: ByteArray
- @TestedSuccessfully
- @ExperimentalUnsignedTypes
+ @Tested
+
override fun decode() {
this.input.skip(7)//8
this.encryptionKey = this.input.readNBytes(16)//24
@@ -52,7 +54,7 @@ class ServerLoginResponseSuccessPacket(input: DataInputStream) : ServerPacket(in
class Encrypted(input: DataInputStream) : ServerPacket(input) {
- @ExperimentalUnsignedTypes
+
fun decrypt(tgtgtKey: ByteArray): ServerLoginResponseSuccessPacket {
input goto 14
return ServerLoginResponseSuccessPacket(this.decryptBy(Protocol.shareKey, tgtgtKey)).setId(this.idHex)
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
index 9625e38b1..314391011 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginResponseVerificationCodeInitPacket.kt
@@ -1,10 +1,12 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
package net.mamoe.mirai.network.packet.login
import net.mamoe.mirai.network.Protocol
import net.mamoe.mirai.network.packet.ServerPacket
import net.mamoe.mirai.network.packet.dataInputStream
import net.mamoe.mirai.network.packet.goto
-import net.mamoe.mirai.utils.TestedSuccessfully
+import net.mamoe.mirai.utils.Tested
import net.mamoe.mirai.utils.hexToUBytes
import java.io.DataInputStream
@@ -20,8 +22,8 @@ class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, priv
var unknownBoolean: Boolean? = null
- @TestedSuccessfully
- @ExperimentalUnsignedTypes
+ @Tested
+
override fun decode() {
val verifyCodeLength = this.input.goto(78).readShort()//2bytes
this.verifyCodePart1 = this.input.readNBytes(verifyCodeLength.toInt())
@@ -39,7 +41,7 @@ class ServerLoginResponseVerificationCodeInitPacket(input: DataInputStream, priv
}
- @ExperimentalUnsignedTypes
+
fun decrypt(): ServerLoginResponseVerificationCodeInitPacket {
this.input goto 14
val data = this.decryptBy(Protocol.shareKey).goto(0).readAllBytes()
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt
index 4b5d092d0..e7c916e5c 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/network/packet/login/ServerLoginSuccessPacket.kt
@@ -1,5 +1,6 @@
package net.mamoe.mirai.network.packet.login
+import net.mamoe.mirai.network.packet.PacketId
import net.mamoe.mirai.network.packet.ServerPacket
import java.io.DataInputStream
@@ -8,4 +9,5 @@ import java.io.DataInputStream
*
* @author Him188moe
*/
+@PacketId("00 EC")
class ServerLoginSuccessPacket(input: DataInputStream) : ServerPacket(input)
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java
index 6cfde9c52..38c367dcd 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/ContactList.java
@@ -1,10 +1,9 @@
package net.mamoe.mirai.utils;
import net.mamoe.mirai.contact.Contact;
-import net.mamoe.mirai.utils.config.MiraiSynchronizedLinkedListMap;
/**
* @author Him188moe
*/
-public class ContactList extends MiraiSynchronizedLinkedListMap {
+public class ContactList extends MiraiSynchronizedLinkedHashMap {
}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
index dbb54d36a..be23a8fcd 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/ImageNetworkUtils.java
@@ -1,36 +1,31 @@
package net.mamoe.mirai.utils;
-import org.apache.commons.httpclient.util.HttpURLConnection;
-import org.jsoup.Connection;
-import org.jsoup.Jsoup;
import java.io.IOException;
-import java.io.InputStream;
+import java.net.HttpURLConnection;
import java.net.URL;
+/**
+ * @author NaturalHG
+ */
public class ImageNetworkUtils {
- public static void postImage(String ukey, int fileSize, String g_uin,String groupCode, byte[] img){
+ public static boolean postImage(String uKeyHex, int fileSize, long qqNumber, long groupCode, byte[] img) throws IOException {
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
- StringBuilder builder = new StringBuilder("http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc");
- builder.append("&ukey=")
- .append(ukey.trim())
- .append("&filezise=").append(fileSize)
- .append("&range=").append("0")
- .append("&uin=").append(g_uin)
- .append("&groupcode=").append(groupCode);
- try {
- HttpURLConnection conn = (HttpURLConnection) new URL(builder.toString()).openConnection();
- conn.setRequestProperty("User-agent","QQClient");
- conn.setRequestProperty("Content-length","" + fileSize);
- conn.setRequestMethod("POST");
- conn.getOutputStream().write(img);
+ String builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" + "&ukey=" +
+ uKeyHex.replace(" ", "") +
+ "&filezise=" + fileSize +
+ "&range=" + "0" +
+ "&uin=" + qqNumber +
+ "&groupcode=" + groupCode;
+ HttpURLConnection conn = (HttpURLConnection) new URL(builder).openConnection();
+ conn.setRequestProperty("User-agent", "QQClient");
+ conn.setRequestProperty("Content-length", "" + fileSize);
+ conn.setRequestMethod("POST");
+ conn.setDoOutput(true);
+ conn.getOutputStream().write(img);
- conn.connect();
- System.out.println(conn.getResponseCode());
-
- } catch (IOException e) {
- e.printStackTrace();
- }
+ conn.connect();
+ return conn.getResponseCode() == 200;
}
}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
index 349817f16..04b245e70 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiLogger.kt
@@ -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
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiSynchronizedLinkedListMap.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedHashMap.java
similarity index 89%
rename from mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiSynchronizedLinkedListMap.java
rename to mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedHashMap.java
index 05dacd407..2c9b0673a 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiSynchronizedLinkedListMap.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedHashMap.java
@@ -1,4 +1,4 @@
-package net.mamoe.mirai.utils.config;
+package net.mamoe.mirai.utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -16,15 +16,15 @@ import java.util.function.Function;
*
* @author NaturalHG
*/
-public class MiraiSynchronizedLinkedListMap extends AbstractMap {
+public class MiraiSynchronizedLinkedHashMap extends AbstractMap {
- public MiraiSynchronizedLinkedListMap(){
+ public MiraiSynchronizedLinkedHashMap() {
this.sortedMap = Collections.synchronizedMap(new LinkedHashMap<>());
}
protected final Map sortedMap;
- public MiraiSynchronizedLinkedListMap(LinkedHashMap map){
+ public MiraiSynchronizedLinkedHashMap(LinkedHashMap map) {
this.sortedMap = Collections.synchronizedMap(map);
}
@@ -155,13 +155,13 @@ public class MiraiSynchronizedLinkedListMap extends AbstractMap {
return this.sortedMap.merge(key,value,remappingFunction);
}
- public boolean equals(MiraiSynchronizedLinkedListMap o) {
+ public boolean equals(MiraiSynchronizedLinkedHashMap o) {
return this.sortedMap.equals(o.sortedMap);
}
@Override
public boolean equals(Object o) {
- return o instanceof MiraiSynchronizedLinkedListMap?this.equals((MiraiSynchronizedLinkedListMap)o):super.equals(o);
+ return o instanceof MiraiSynchronizedLinkedHashMap ? this.equals((MiraiSynchronizedLinkedHashMap) o) : super.equals(o);
}
}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedList.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedList.java
new file mode 100644
index 000000000..c88aefc87
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/MiraiSynchronizedLinkedList.java
@@ -0,0 +1,195 @@
+package net.mamoe.mirai.utils;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
+
+/**
+ * @author Him188moe
+ */
+public class MiraiSynchronizedLinkedList extends AbstractList {
+ @SuppressWarnings("WeakerAccess")
+ protected final List syncList;
+
+ public MiraiSynchronizedLinkedList() {
+ this.syncList = Collections.synchronizedList(new LinkedList<>());
+ }
+
+ public MiraiSynchronizedLinkedList(Collection collection) {
+ this.syncList = Collections.synchronizedList(new LinkedList<>(collection));
+ }
+
+
+ @Override
+ public E get(int index) {
+ return this.syncList.get(index);
+ }
+
+ @Override
+ public void forEach(Consumer super E> action) {
+ this.syncList.forEach(action);
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return this.syncList.spliterator();
+ }
+
+ @Override
+ public Stream stream() {
+ return this.syncList.stream();
+ }
+
+ @Override
+ public Stream parallelStream() {
+ return this.syncList.parallelStream();
+ }
+
+ @Override
+ public int size() {
+ return this.syncList.size();
+ }
+
+ @SuppressWarnings("SuspiciousToArrayCall")
+ @Override
+ public T[] toArray(IntFunction generator) {
+ return this.syncList.toArray(generator);
+ }
+
+ @Override
+ public boolean removeIf(Predicate super E> filter) {
+ return this.syncList.removeIf(filter);
+ }
+
+ @Override
+ public void replaceAll(UnaryOperator operator) {
+ this.syncList.replaceAll(operator);
+ }
+
+ @Override
+ public void sort(Comparator super E> c) {
+ this.syncList.sort(c);
+ }
+
+ @Override
+ public boolean add(E e) {
+ return this.syncList.add(e);
+ }
+
+ @Override
+ public E set(int index, E element) {
+ return this.syncList.set(index, element);
+ }
+
+ @Override
+ public void add(int index, E element) {
+ this.syncList.add(index, element);
+ }
+
+ @Override
+ public E remove(int index) {
+ return this.syncList.remove(index);
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return this.syncList.indexOf(o);
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return this.syncList.lastIndexOf(o);
+ }
+
+ @Override
+ public void clear() {
+ this.syncList.clear();
+ }
+
+ @Override
+ public boolean addAll(int index, Collection extends E> c) {
+ return this.syncList.addAll(index, c);
+ }
+
+ @NotNull
+ @Override
+ public Iterator iterator() {
+ return this.syncList.iterator();
+ }
+
+ @NotNull
+ @Override
+ public ListIterator listIterator() {
+ return this.syncList.listIterator();
+ }
+
+ @NotNull
+ @Override
+ public ListIterator listIterator(int index) {
+ return this.syncList.listIterator(index);
+ }
+
+ @NotNull
+ @Override
+ public List subList(int fromIndex, int toIndex) {
+ return this.syncList.subList(fromIndex, toIndex);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.syncList.hashCode();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.syncList.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return this.syncList.contains(o);
+ }
+
+ @NotNull
+ @Override
+ public Object[] toArray() {
+ return this.syncList.toArray();
+ }
+
+ @SuppressWarnings("SuspiciousToArrayCall")
+ @NotNull
+ @Override
+ public T[] toArray(@NotNull T[] a) {
+ return this.syncList.toArray(a);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return this.syncList.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(@NotNull Collection> c) {
+ return this.syncList.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(@NotNull Collection extends E> c) {
+ return this.syncList.addAll(c);
+ }
+
+ @Override
+ public boolean removeAll(@NotNull Collection> c) {
+ return this.syncList.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(@NotNull Collection> c) {
+ return this.syncList.retainAll(c);
+ }
+}
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/ProtocolBuff.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/ProtocolBuff.kt
deleted file mode 100644
index 16f23cc26..000000000
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/ProtocolBuff.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package net.mamoe.mirai.utils
-
-import java.io.DataOutputStream
-
-/**
- * Google ProtocolBuff 的一些算法实现
- *
- * @author Him188moe
- */
-
-
-/**
- * 128(10000000) -> 0x7F (10000000_10000001)
- *
- * TODO improve
- */
-@ExperimentalUnsignedTypes
-fun DataOutputStream.writeProtoFixedInt(int: Long) {
- if (int == 0xFFL) {
- this.writeShort(0x80_01)//unsigned//1000000010000001
- return
- }
- this.writeByte((int.rem(0xFF) + 0xFF).toInt())
- this.writeByte((int / 0xFF).toInt())
-}
-
-/**
- * 127(1111111(7)) -> 0x7F (11111111(8))
- *
- * TODO improve
- */
-@ExperimentalUnsignedTypes
-fun DataOutputStream.writeProtoInt(int: Long) {
- if (int < 0xFF) {
- this.writeByte((int and 0xFF).toInt())//10000000
- return
- }
- this.writeProtoFixedInt(int)
-}
-
-
-@ExperimentalUnsignedTypes
-fun main() {
- println()
- println(lazyEncode {
- it.writeProtoInt(128)
- }.toUHexString())
-}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
index d48de305a..852e96cb0 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/TEA.kt
@@ -1,277 +1,269 @@
package net.mamoe.mirai.utils
-import net.mamoe.mirai.network.Protocol
import java.nio.ByteBuffer
import java.util.*
import kotlin.experimental.and
import kotlin.experimental.xor
-/**
- * @author Him188moe
- */
/**
* TEA 加密
*
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/
-class TEA(private val key: ByteArray) {
+object TEA {
+ private const val UINT32_MASK = 0xffffffffL
+ private fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray {
+ val mRandom = Random()
+ lateinit var mOutput: ByteArray
+ lateinit var mInBlock: ByteArray
+ var mIndexPos: Int
+ lateinit var mIV: ByteArray
+ var mOutPos = 0
+ var mPreOutPos = 0
+ var isFirstBlock = true
- companion object {
- val CRYPTOR_SHARE_KEY = TEA(Protocol.hexToBytes(Protocol.shareKey))
- val CRYPTOR_0825KEY = TEA(Protocol.hexToBytes(Protocol.key0825))
+ val mKey = LongArray(4)
- private val UINT32_MASK = 0xffffffffL
+ for (i in 0..3) {
+ mKey[i] = pack(key, i * 4, 4)
+ }
- fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray {
- val mRandom = Random()
- lateinit var mOutput: ByteArray
- lateinit var mInBlock: ByteArray
- var mIndexPos: Int
- lateinit var mIV: ByteArray
- var mOutPos = 0
- var mPreOutPos = 0
- var isFirstBlock: Boolean = true
+ fun rand(): Int {
+ return mRandom.nextInt()
+ }
- val mKey = LongArray(4)
+ fun encode(bytes: ByteArray): ByteArray {
+ var v0 = pack(bytes, 0, 4)
+ var v1 = pack(bytes, 4, 4)
+ var sum: Long = 0
+ val delta = 0x9e3779b9L
+ for (i in 0..15) {
+ sum = sum + delta and UINT32_MASK
+ v0 += (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
+ v0 = v0 and UINT32_MASK
+ v1 += (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
+ v1 = v1 and UINT32_MASK
+ }
+ return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
+ }
- for (i in 0..3) {
- mKey[i] = pack(key, i * 4, 4)
+ fun decode(bytes: ByteArray, offset: Int): ByteArray {
+ var v0 = pack(bytes, offset, 4)
+ var v1 = pack(bytes, offset + 4, 4)
+ val delta = 0x9e3779b9L
+ var sum = delta shl 4 and UINT32_MASK
+ for (i in 0..15) {
+ v1 -= (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
+ v1 = v1 and UINT32_MASK
+ v0 -= (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
+ v0 = v0 and UINT32_MASK
+ sum = sum - delta and UINT32_MASK
+ }
+ return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
+ }
+
+ fun encodeOneBlock() {
+ mIndexPos = 0
+ while (mIndexPos < 8) {
+ mInBlock[mIndexPos] = if (isFirstBlock)
+ mInBlock[mIndexPos]
+ else
+ (mInBlock[mIndexPos] xor mOutput[mPreOutPos + mIndexPos])
+ mIndexPos++
}
- fun rand(): Int {
- return mRandom.nextInt()
+ System.arraycopy(encode(mInBlock), 0, mOutput, mOutPos, 8)
+ mIndexPos = 0
+ while (mIndexPos < 8) {
+ val outPos = mOutPos + mIndexPos
+ mOutput[outPos] = (mOutput[outPos] xor mIV[mIndexPos])
+ mIndexPos++
}
+ System.arraycopy(mInBlock, 0, mIV, 0, 8)
+ mPreOutPos = mOutPos
+ mOutPos += 8
+ mIndexPos = 0
+ isFirstBlock = false
+ }
- fun encode(bytes: ByteArray): ByteArray {
- var v0 = pack(bytes, 0, 4)
- var v1 = pack(bytes, 4, 4)
- var sum: Long = 0
- val delta = 0x9e3779b9L
- for (i in 0..15) {
- sum = sum + delta and UINT32_MASK
- v0 += (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
- v0 = v0 and UINT32_MASK
- v1 += (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
- v1 = v1 and UINT32_MASK
- }
- return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
- }
-
- fun decode(bytes: ByteArray, offset: Int): ByteArray {
- var v0 = pack(bytes, offset, 4)
- var v1 = pack(bytes, offset + 4, 4)
- val delta = 0x9e3779b9L
- var sum = delta shl 4 and UINT32_MASK
- for (i in 0..15) {
- v1 -= (v0 shl 4) + mKey[2] xor v0 + sum xor v0.ushr(5) + mKey[3]
- v1 = v1 and UINT32_MASK
- v0 -= (v1 shl 4) + mKey[0] xor v1 + sum xor v1.ushr(5) + mKey[1]
- v0 = v0 and UINT32_MASK
- sum = sum - delta and UINT32_MASK
- }
- return ByteBuffer.allocate(8).putInt(v0.toInt()).putInt(v1.toInt()).array()
- }
-
- fun encodeOneBlock() {
- mIndexPos = 0
- while (mIndexPos < 8) {
- mInBlock[mIndexPos] = if (isFirstBlock)
- mInBlock[mIndexPos]
- else
- (mInBlock[mIndexPos] xor mOutput[mPreOutPos + mIndexPos])
+ fun decodeOneBlock(ciphertext: ByteArray, offset: Int, len: Int): Boolean {
+ mIndexPos = 0
+ while (mIndexPos < 8) {
+ if (mOutPos + mIndexPos < len) {
+ mIV[mIndexPos] = (mIV[mIndexPos] xor ciphertext[mOutPos + offset + mIndexPos])
mIndexPos++
+ continue
}
-
- System.arraycopy(encode(mInBlock), 0, mOutput, mOutPos, 8)
- mIndexPos = 0
- while (mIndexPos < 8) {
- val out_pos = mOutPos + mIndexPos
- mOutput[out_pos] = (mOutput[out_pos] xor mIV[mIndexPos])
- mIndexPos++
- }
- System.arraycopy(mInBlock, 0, mIV, 0, 8)
- mPreOutPos = mOutPos
- mOutPos += 8
- mIndexPos = 0
- isFirstBlock = false
- }
-
- fun decodeOneBlock(ciphertext: ByteArray, offset: Int, len: Int): Boolean {
- mIndexPos = 0
- while (mIndexPos < 8) {
- if (mOutPos + mIndexPos < len) {
- mIV[mIndexPos] = (mIV[mIndexPos] xor ciphertext[mOutPos + offset + mIndexPos])
- mIndexPos++
- continue
- }
- return true
- }
-
- mIV = decode(mIV, 0)
- mOutPos += 8
- mIndexPos = 0
return true
-
}
- fun encrypt(plaintext: ByteArray, offset: Int, len: Int): ByteArray {
- var len = len;
- var offset = offset;
- mInBlock = ByteArray(8)
- mIV = ByteArray(8)
- mOutPos = 0
- mPreOutPos = 0
- isFirstBlock = true
- mIndexPos = (len + 10) % 8
- if (mIndexPos != 0) {
- mIndexPos = 8 - mIndexPos
- }
- mOutput = ByteArray(mIndexPos + len + 10)
- mInBlock[0] = (rand() and 0xf8 or mIndexPos).toByte()
- for (i in 1..mIndexPos) {
- mInBlock[i] = (rand() and 0xff).toByte()
- }
- ++mIndexPos
- for (i in 0..7) {
- mIV[i] = 0
- }
+ mIV = decode(mIV, 0)
+ mOutPos += 8
+ mIndexPos = 0
+ return true
- var g = 0
- while (g < 2) {
- if (mIndexPos < 8) {
- mInBlock[mIndexPos++] = (rand() and 0xff).toByte()
- ++g
- }
- if (mIndexPos == 8) {
- encodeOneBlock()
- }
- }
+ }
- while (len > 0) {
- if (mIndexPos < 8) {
- mInBlock[mIndexPos++] = plaintext[offset++]
- }
- if (mIndexPos == 8) {
- encodeOneBlock()
- }
- len--
- }
- g = 0
- while (g < 7) {
- if (mIndexPos < 8) {
- mInBlock[mIndexPos++] = 0.toByte()
- }
- if (mIndexPos == 8) {
- encodeOneBlock()
- }
- g++
- }
- return mOutput
+ @Suppress("NAME_SHADOWING")
+ fun encrypt(plaintext: ByteArray, offset: Int, len: Int): ByteArray {
+ var len = len
+ var offset = offset
+ mInBlock = ByteArray(8)
+ mIV = ByteArray(8)
+ mOutPos = 0
+ mPreOutPos = 0
+ isFirstBlock = true
+ mIndexPos = (len + 10) % 8
+ if (mIndexPos != 0) {
+ mIndexPos = 8 - mIndexPos
+ }
+ mOutput = ByteArray(mIndexPos + len + 10)
+ mInBlock[0] = (rand() and 0xf8 or mIndexPos).toByte()
+ for (i in 1..mIndexPos) {
+ mInBlock[i] = (rand() and 0xff).toByte()
+ }
+ ++mIndexPos
+ for (i in 0..7) {
+ mIV[i] = 0
}
- fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray? {
- require(!(len % 8 != 0 || len < 16)) { "must len % 8 == 0 && len >= 16" }
- mIV = decode(cipherText, offset)
- mIndexPos = (mIV[0] and 7).toInt()
- var plen = len - mIndexPos - 10
- isFirstBlock = true
- if (plen < 0) {
- return null
+ var g = 0
+ while (g < 2) {
+ if (mIndexPos < 8) {
+ mInBlock[mIndexPos++] = (rand() and 0xff).toByte()
+ ++g
}
- mOutput = ByteArray(plen)
- mPreOutPos = 0
- mOutPos = 8
- ++mIndexPos
- var g = 0
- while (g < 2) {
- if (mIndexPos < 8) {
- ++mIndexPos
- ++g
- }
- if (mIndexPos == 8) {
- isFirstBlock = false
- if (!decodeOneBlock(cipherText, offset, len)) {
- throw RuntimeException("Unable to decode")
- }
- }
+ if (mIndexPos == 8) {
+ encodeOneBlock()
}
+ }
- var outpos = 0
- while (plen != 0) {
- if (mIndexPos < 8) {
- mOutput[outpos++] = if (isFirstBlock)
- mIV[mIndexPos]
- else
- (cipherText[mPreOutPos + offset + mIndexPos] xor mIV[mIndexPos])
+ while (len > 0) {
+ if (mIndexPos < 8) {
+ mInBlock[mIndexPos++] = plaintext[offset++]
+ }
+ if (mIndexPos == 8) {
+ encodeOneBlock()
+ }
+ len--
+ }
+ g = 0
+ while (g < 7) {
+ if (mIndexPos < 8) {
+ mInBlock[mIndexPos++] = 0.toByte()
+ }
+ if (mIndexPos == 8) {
+ encodeOneBlock()
+ }
+ g++
+ }
+ return mOutput
+ }
+
+ fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray? {
+ require(!(len % 8 != 0 || len < 16)) { "must len % 8 == 0 && len >= 16" }
+ mIV = decode(cipherText, offset)
+ mIndexPos = (mIV[0] and 7).toInt()
+ var plen = len - mIndexPos - 10
+ isFirstBlock = true
+ if (plen < 0) {
+ return null
+ }
+ mOutput = ByteArray(plen)
+ mPreOutPos = 0
+ mOutPos = 8
+ ++mIndexPos
+ var g = 0
+ while (g < 2) {
+ if (mIndexPos < 8) {
+ ++mIndexPos
+ ++g
+ }
+ if (mIndexPos == 8) {
+ isFirstBlock = false
+ if (!decodeOneBlock(cipherText, offset, len)) {
+ throw RuntimeException("Unable to decode")
+ }
+ }
+ }
+
+ var outpos = 0
+ while (plen != 0) {
+ if (mIndexPos < 8) {
+ mOutput[outpos++] = if (isFirstBlock)
+ mIV[mIndexPos]
+ else
+ (cipherText[mPreOutPos + offset + mIndexPos] xor mIV[mIndexPos])
+ ++mIndexPos
+ }
+ if (mIndexPos == 8) {
+ mPreOutPos = mOutPos - 8
+ isFirstBlock = false
+ if (!decodeOneBlock(cipherText, offset, len)) {
+ throw RuntimeException("Unable to decode")
+ }
+ }
+ plen--
+ }
+ g = 0
+ while (g < 7) {
+ if (mIndexPos < 8) {
+ if (cipherText[mPreOutPos + offset + mIndexPos].xor(mIV[mIndexPos]).toInt() != 0) {
+ throw RuntimeException()
+ } else {
++mIndexPos
}
- if (mIndexPos == 8) {
- mPreOutPos = mOutPos - 8
- isFirstBlock = false
- if (!decodeOneBlock(cipherText, offset, len)) {
- throw RuntimeException("Unable to decode")
- }
- }
- plen--
}
- g = 0
- while (g < 7) {
- if (mIndexPos < 8) {
- if (cipherText[mPreOutPos + offset + mIndexPos].xor(mIV[mIndexPos]).toInt() != 0) {
- throw RuntimeException()
- } else {
- ++mIndexPos
- }
+
+ if (mIndexPos == 8) {
+ mPreOutPos = mOutPos
+ if (!decodeOneBlock(cipherText, offset, len)) {
+ throw RuntimeException("Unable to decode")
}
-
- if (mIndexPos == 8) {
- mPreOutPos = mOutPos
- if (!decodeOneBlock(cipherText, offset, len)) {
- throw RuntimeException("Unable to decode")
- }
- }
- g++
}
- return mOutput
+ g++
}
+ return mOutput
+ }
- return if (encrypt) {
- encrypt(data, 0, data.size)
- } else {
- try {
- return decrypt(data, 0, data.size)!!
- } catch (e: Exception) {
- println("Source: " + data.toUHexString(" "))
- println("Key: " + key.toUHexString(" "))
- throw e
- }
+ return if (encrypt) {
+ encrypt(data, 0, data.size)
+ } else {
+ try {
+ return decrypt(data, 0, data.size)!!
+ } catch (e: Exception) {
+ println("Source: " + data.toUHexString(" "))
+ println("Key: " + key.toUHexString(" "))
+ throw e
}
}
-
- fun encrypt(source: ByteArray, key: ByteArray): ByteArray {
- return doOption(source, key, true)
- }
-
- fun encrypt(source: ByteArray, keyHex: String): ByteArray {
- return encrypt(source, keyHex.hexToBytes())
- }
-
- fun decrypt(source: ByteArray, key: ByteArray): ByteArray {
- return doOption(source, key, false)
- }
-
- fun decrypt(source: ByteArray, keyHex: String): ByteArray {
- return decrypt(source, keyHex.hexToBytes())
- }
-
- private fun pack(bytes: ByteArray, offset: Int, len: Int): Long {
- var result: Long = 0
- val max_offset = if (len > 8) offset + 8 else offset + len
- for (index in offset until max_offset) {
- result = result shl 8 or (bytes[index].toLong() and 0xffL)
- }
- return result shr 32 or (result and UINT32_MASK)
- }
+ }
+
+ fun encrypt(source: ByteArray, key: ByteArray): ByteArray {
+ return doOption(source, key, true)
+ }
+
+ @Suppress("unused")
+ fun encrypt(source: ByteArray, keyHex: String): ByteArray {
+ return encrypt(source, keyHex.hexToBytes())
+ }
+
+ fun decrypt(source: ByteArray, key: ByteArray): ByteArray {
+ return doOption(source, key, false)
+ }
+
+ fun decrypt(source: ByteArray, keyHex: String): ByteArray {
+ return decrypt(source, keyHex.hexToBytes())
+ }
+
+ @Suppress("SameParameterValue")
+ private fun pack(bytes: ByteArray, offset: Int, len: Int): Long {
+ var result: Long = 0
+ val maxOffset = if (len > 8) offset + 8 else offset + len
+ for (index in offset until maxOffset) {
+ result = result shl 8 or (bytes[index].toLong() and 0xffL)
+ }
+ return result shr 32 or (result and UINT32_MASK)
}
}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt
index 5013309b3..d9bd874b7 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/Tested.kt
@@ -5,4 +5,4 @@ package net.mamoe.mirai.utils
*
* @author Him188moe
*/
-internal annotation class TestedSuccessfully
\ No newline at end of file
+internal annotation class Tested
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
index 776723608..788ec66a4 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/Utils.kt
@@ -1,12 +1,16 @@
+@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
+
package net.mamoe.mirai.utils
import net.mamoe.mirai.network.Protocol
+import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import java.io.File
import java.lang.reflect.Field
import java.util.*
import java.util.zip.CRC32
+import javax.imageio.ImageIO
/**
* @author Him188moe
@@ -24,14 +28,14 @@ fun ByteArray.toHexString(separator: String = " "): String = this.joinToString(s
return@joinToString ret
}
-@ExperimentalUnsignedTypes
+
fun ByteArray.toUHexString(separator: String = " "): String = this.toUByteArray().toUHexString(separator)
-@ExperimentalUnsignedTypes
+
@JvmSynthetic
fun ByteArray.toUHexString(): String = this.toUByteArray().toUHexString()
-@ExperimentalUnsignedTypes
+
@JvmSynthetic
fun UByteArray.toUHexString(separator: String = " "): String {
return this.joinToString(separator) {
@@ -43,36 +47,36 @@ fun UByteArray.toUHexString(separator: String = " "): String {
}
}
-@ExperimentalUnsignedTypes
+
@JvmSynthetic
fun UByteArray.toUHexString(): String = this.toUHexString(" ")
-@ExperimentalUnsignedTypes
+
fun Byte.toUHexString(): String = this.toUByte().toString(16)
-@ExperimentalUnsignedTypes
+
fun String.hexToBytes(): ByteArray = Protocol.hexToBytes(this)
-@ExperimentalUnsignedTypes
+
fun String.hexToUBytes(): UByteArray = Protocol.hexToUBytes(this)
-@ExperimentalUnsignedTypes
+
fun String.hexToInt(): Int = hexToBytes().toUInt().toInt()
-@ExperimentalUnsignedTypes
+
fun ByteArray.toUInt(): UInt =
this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0)
open class ByteArrayDataOutputStream : DataOutputStream(ByteArrayOutputStream()) {
open fun toByteArray(): ByteArray = (out as ByteArrayOutputStream).toByteArray()
- @ExperimentalUnsignedTypes
+
open fun toUByteArray(): UByteArray = (out as ByteArrayOutputStream).toByteArray().toUByteArray()
}
@JvmSynthetic
-fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().let { t(it); return it.toByteArray() }
+fun lazyEncode(t: (ByteArrayDataOutputStream) -> Unit): ByteArray = ByteArrayDataOutputStream().also(t).toByteArray()
+
-@ExperimentalUnsignedTypes
fun getRandomByteArray(length: Int): ByteArray {
val bytes = LinkedList()
repeat(length) { bytes.add((Math.random() * 255).toByte()) }
@@ -134,4 +138,10 @@ fun ByteArray.removeZeroTail(): ByteArray {
--i
}
return this.copyOfRange(0, i + 1)
+}
+
+fun BufferedImage.toByteArray(formatName: String = "PNG"): ByteArray {
+ return lazyEncode {
+ ImageIO.write(this, formatName, it)
+ }
}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/Varint.kt b/mirai-core/src/main/java/net/mamoe/mirai/utils/Varint.kt
new file mode 100644
index 000000000..289749a18
--- /dev/null
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/Varint.kt
@@ -0,0 +1,133 @@
+@file:JvmName("Varint")
+@file:Suppress("EXPERIMENTAL_API_USAGE")
+
+package net.mamoe.mirai.utils
+
+import java.io.DataInputStream
+import java.io.DataOutputStream
+import java.io.IOException
+import java.io.InputStream
+import kotlin.experimental.or
+
+/**
+ * Tool class for VarInt or VarLong operations.
+ *
+ * Some code from http://wiki.vg/Protocol.
+ *
+ * @author MagicDroidX of Nukkit Project
+ * @author lmlstarqaq of Nukkit Project
+ */
+
+fun encodeZigZag32(signedInt: Int): Long {
+ return (signedInt shl 1 xor (signedInt shr 31)).toLong()
+}
+
+
+@JvmSynthetic
+fun decodeZigZag32(uint: UInt): Int {
+ return decodeZigZag32(uint.toLong())
+}
+
+fun decodeZigZag32(uint: Long): Int {
+ return (uint shr 1).toInt() xor -(uint and 1).toInt()
+}
+
+fun encodeZigZag64(signedLong: Long): Long {
+ return signedLong shl 1 xor (signedLong shr 63)
+}
+
+fun decodeZigZag64(signedLong: Long): Long {
+ return signedLong.ushr(1) xor -(signedLong and 1)
+}
+
+
+@Throws(IOException::class)
+fun DataInputStream.readVarInt(): Int {
+ return decodeZigZag32(this.readUnsignedVarInt())
+}
+
+
+@Throws(IOException::class)
+fun DataInputStream.readUnsignedVarInt(): UInt {
+ return read(this, 5).toUInt()
+}
+
+
+@Throws(IOException::class)
+fun DataInputStream.readVarLong(): Long {
+ return decodeZigZag64(readUnsignedVarLong().toLong())
+}
+
+
+@Throws(IOException::class)
+fun DataInputStream.readUnsignedVarLong(): ULong {
+ return read(this, 10).toULong()
+}
+
+@Throws(IOException::class)
+fun DataOutputStream.writeVarInt(signedInt: Int) {
+ this.writeUVarInt(encodeZigZag32(signedInt))
+}
+
+
+@Throws(IOException::class)
+fun DataOutputStream.writeUVarInt(uint: UInt) {
+ return writeUVarInt(uint.toLong())
+}
+
+@Throws(IOException::class)
+fun DataOutputStream.writeUVarInt(uint: Long) {
+ this.write0(uint)
+}
+
+@Throws(IOException::class)
+fun DataOutputStream.writeVarLong(signedLong: Long) {
+ this.writeUVarLong(encodeZigZag64(signedLong))
+}
+
+@Throws(IOException::class)
+fun DataOutputStream.writeUVarLong(ulong: Long) {
+ this.write0(ulong)
+}
+
+
+@Throws(IOException::class)
+private fun DataOutputStream.write0(long: Long) {
+ var value = long
+ do {
+ var temp = (value and 127).toByte()
+ value = value ushr 7
+ if (value != 0L) {
+ temp = temp or 128.toByte()
+ }
+ this.writeByte(temp.toInt())
+ } while (value != 0L)
+}
+
+@Throws(IOException::class)
+private fun read(stream: DataInputStream, maxSize: Int): Long {
+ var value: Long = 0
+ var size = 0
+ var b = stream.readByte().toInt()
+ while (b and 0x80 == 0x80) {
+ value = value or ((b and 0x7F).toLong() shl size++ * 7)
+ require(size < maxSize) { "VarLong too big" }
+ b = stream.readByte().toInt()
+ }
+
+ return value or ((b and 0x7F).toLong() shl size * 7)
+}
+
+@Throws(IOException::class)
+private fun read(stream: InputStream, maxSize: Int): Long {
+ var value: Long = 0
+ var size = 0
+ var b = stream.read()
+ while (b and 0x80 == 0x80) {
+ value = value or ((b and 0x7F).toLong() shl size++ * 7)
+ require(size < maxSize) { "VarLong too big" }
+ b = stream.read()
+ }
+
+ return value or ((b and 0x7F).toLong() shl size * 7)
+}
\ No newline at end of file
diff --git a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java
index 9c218d70b..a0a6cc6f2 100644
--- a/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java
+++ b/mirai-core/src/main/java/net/mamoe/mirai/utils/config/MiraiConfigSection.java
@@ -1,5 +1,6 @@
package net.mamoe.mirai.utils.config;
+import net.mamoe.mirai.utils.MiraiSynchronizedLinkedHashMap;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
@@ -10,7 +11,7 @@ import java.util.function.Supplier;
/**
* @author NaturalHG
*/
-public class MiraiConfigSection extends MiraiSynchronizedLinkedListMap {
+public class MiraiConfigSection extends MiraiSynchronizedLinkedHashMap {
public MiraiConfigSection(){
super();
diff --git a/mirai-core/src/test/java/PacketTest.kt b/mirai-core/src/test/java/PacketTest.kt
deleted file mode 100644
index 4d7b5ffb9..000000000
--- a/mirai-core/src/test/java/PacketTest.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-import net.mamoe.mirai.network.packet.login.ClientPasswordSubmissionPacket
-import net.mamoe.mirai.utils.toUHexString
-
-@ExperimentalUnsignedTypes
-fun main(){
- /*
- val data = "00 37 13 08 25 31 01 EB 10 08 30 69 50 1C 84 A9 C2 16 D7 52 B9 1C 79 CA 5A CF FD BC EB 10 08 30 69 50 1C 84 A9 C2 16 D7 52 B9 1C 79 CA 5A CF FD BC AE D8 A6 BB DC 21 6E 79 26 E1 A2 23 11 AA B0 9A AE D8 A6 BB DC 21 6E 79 26 E1 A2 23 11 AA B0 9A 76 E4 B8 DD 03 00 00 00 01 2E 01 00 00 68 52 00 00 00 00 A4 F1 91 88 C9 82 14 99 0C 9E 56 55 91 23 C8 3D C3 47 F0 25 A1 8E 74 EF 1E 0B 32 5B 20 8A FA 3B 0B 52 8F 86 E6 04 F1 D6 F8 63 75 60 8C 0C 7D 06 D1 E0 22 F8 49 EF AF 61 EE 7E 69 72 EB 10 08 30 69 50 1C 84 A9 C2 16 D7 52 B9 1C 79 CA 5A CF FD BC AE D8 A6 BB DC 21 6E 79 26 E1 A2 23 11 AA B0 9A 49 39 72 ED 61 12 B6 88 4D A2 56 23 E9 92 11 92 27 4A 70 00 C9 01 7B 03";
- val s = DataInputStream(data.hexToBytes().inputStream())
- val packet = ServerTouchResponsePacket(ServerTouchResponsePacket.Type.TYPE_08_25_31_01, s)
- packet.decode()
- System.out.println(packet.token.toUByteArray().toUHexString(" "))
- System.out.println(packet.loginTime.toUHexString(" "))
- System.out.println(packet.loginIP)
- */
-
- // val packet = ClientPasswordSubmissionPacket(1994701021,"xiaoqqq",)
-
- /*
- val data = "00 01 09 00 70 00 01 5C 71 80 A6 BA 20 62 2E C1 BE BF F2 47 37 40 A1 00 38 91 25 85 58 18 D3 67 77 2C 4D 02 D8 66 A6 F7 3E 57 D8 CE 01 47 7F D0 8F 13 C8 3A E5 19 A2 60 BC 4C 9A 35 4E 92 9F 21 48 6C 67 68 36 6B 94 C1 6F 11 8D 55 6B 04 9A 22 C3 00 20 29 7E D4 A7 16 02 07 14 41 90 3A 65 06 AC CB 28 AB 90 DB 46 33 C9 C0 1D 06 44 7A 92 17 C3 A5 F3 00 00 01 03 00 14 00 01 00 10 60 C9 5D A7 45 70 04 7F 21 7D 84 50 5C 66 A5 C6 01 07 01 D3 00 01 00 16 00 00 00 01 00 00 00 64 00 00 0D B6 00 09 3A 80 00 00 00 3C 00 02 2E 29 4E 47 5F 68 78 2C 47 25 5F 59 50 65 60 5D 00 88 00 04 5D 59 45 17 B7 5F F8 D4 00 00 00 00 00 78 38 E0 3B 23 4A C5 0E 93 CB C1 66 96 37 8B 46 B2 86 23 3F 2D 09 45 E0 16 1C E9 9C 11 7A FA 2D A8 50 47 42 74 01 06 84 76 0E 5F C6 04 29 1A 4A 65 AA 93 49 DF BD 00 ED 80 B8 26 CA 80 E8 20 6D 15 43 DD D8 E6 48 C2 8A 5A F8 70 6B 51 3A E2 2D 21 95 4B 6A 75 A8 90 CA B1 C0 E5 73 99 D7 59 D8 DD 3D C9 5C E4 49 61 22 11 60 85 48 C4 7D E0 84 62 AD B3 13 84 61 C1 9E 19 35 41 44 44 37 3F 21 33 64 4B 37 5D 77 6D 61 3F 00 78 00 01 5D 59 45 17 00 70 A4 D9 44 9E 95 51 B4 B0 91 CC 1E DB 34 F9 F7 13 8B 30 08 C0 AE 33 22 9C FF 87 CF 9B A2 B0 E5 E1 D0 E0 AD DD 8F E9 F6 1E 01 1F AA 74 46 66 B4 81 54 B9 29 E5 FC 0B 7F C9 13 AE 32 BA D6 55 2E B0 A1 30 24 B6 F2 E7 62 F9 2E 00 E4 51 61 50 7C D1 36 E8 61 96 36 FF B7 32 74 3C 2A F7 74 63 DA 7D 57 84 18 ED 84 E9 D8 87 6D 66 1D D5 84 D4 23 99 00 83 01 63 2A 69 2E 25 79 28 3B 29 33 29 40 28 54 7E 21 00 70 00 01 5D 59 45 17 00 68 6F 1F FB 31 7B D7 B7 D8 91 32 D7 20 8B 8A F6 02 C8 22 E5 24 8C 25 F2 6A C5 B0 ED 35 01 BF AF 42 72 33 4E FB 3F D3 02 BA F4 46 2B 68 20 0B E3 39 81 B1 D3 8A E0 1B 0F 69 D1 70 AE 49 A5 24 4F BB 58 4F F8 31 A0 37 4C CD F1 12 35 80 99 7D 25 CA F9 E9 45 B6 B0 57 56 66 61 C5 7B 90 57 BF E2 2C 94 91 80 1A B0 D7 21 A8 44 2C 33 4A 29 77 5F 71 40 41 38 3D 7A 41 65 33 01 08 00 29 00 01 00 25 00 1D 02 5B 14 28 E0 B9 91 E2 80 A2 CC 80 CF 89 E2 80 A2 CC 81 E0 B9 91 29 02 13 80 02 00 05 00 04 00 00 00 01 01 15 00 10 F9 86 85 81 30 F6 1B E0 E7 97 98 F6 46 C3 4F B2"
- val s = DataInputStream(data.hexToBytes().inputStream())
- val packet = ServerLoginResponseSuccessPacket(s,(data.length+1)/3)
- packet.decode()
- System.out.println("0828key: \n" + packet._0828_rec_decr_key.toUByteArray().toUHexString(" "))
- System.out.println("token88: \n" + packet.token88.toUByteArray().toUHexString(" "))
- System.out.println("token38: \n" + packet.token38.toUByteArray().toUHexString(" "))
- System.out.println("enckey: \n" + packet.encryptionKey.toUByteArray().toUHexString(" "))
- System.out.println("nick: " + packet.nick)
- System.out.println("age: " + packet.age)
- System.out.println("gender: " + packet.gender)
- */
-
- /*
- val data = "FB 01 04 03 33 00 01 00 BA 02 03 2C 13 00 05 01 00 00 01 23 00 38 F5 C3 CF F4 B4 27 C5 8F 9B D3 ED 18 73 7D E9 CB 43 1F 57 43 BE D3 1B 9A F5 26 2B F4 D9 43 14 9A ED 3B C3 6C E5 7F 4E B0 0C BA 55 57 18 06 78 E1 13 A7 B2 A8 7F 47 E1 1C 02 BC 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 82 00 00 00 35 08 03 00 00 00 BA 12 C3 02 00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05 00 00 00 01 73 52 47 42 00 AE CE 1C E9 00 00 00 6C 50 4C 54 45 F6 F8 F4 E3 EE FD E5 FD FF EE FD F0 F6 F9 E7 F7 F9 EC F4 FE FF F7 F7 F9 F5 F4 FF EC F7 FF E6 F4 E0 F5 FC F8 49 5B 8F CB DB FA BB C9 F5 2B 40 7C D6 E6 FF FD FB FF ED F1 F0 2A 43 B3 D6 F6 FF FC F1 FC 1C 38 91 A7 BD F6 63 74 BD 70 8B CC 34 4F A5 DE F3 F1 75 88 E7 A4 B7 D3 73 82 A1 8C A3 DC F3 FB D8 4D 68 B9 94 A8 FB 8D 9C B6 CB 42 B0 8E 00 00 0A 6F 49 44 41 54 58 C3 B4 57 89 76 EB 2A 12 14 20 B1 09 10 42 68 DF 2C FB FF FF 71 0A 79 49 E2 E4 CE CC 79 37 8F 13 59 B6 72 EC 2E AA BB AB 9A 2C FB 61 89 8F 95 E5 79 A9 70 CB B2 22 C7 2A 65 DB 5E 72 21 F1 A9 90 B2 28 C4 F9 AF 5F 5F 05 30 9C 28 D2 4B C6 58 F9 8C 92 17 39 13 6D 9B FF EB 10 8A 3B 0F 2F 10 52 64 45 51 64 42 A6 37 79 21 DA FC F2 6F 40 08 9F AE B7 9C A4 C8 27 31 F2 C4 50 A4 D4 9C F9 01 BC A2 90 0F 08 E1 2F AE 1F E3 7E 7D 24 52 F4 67 2C 91 02 E3 CA 13 2F 78 97 00 86 DF 4E C4 23 56 F1 F8 7B 10 90 42 3D D2 93 EE 27 04 F1 80 F0 4B 99 10 4F 62 5E D9 2D EE 57 8A F1 A0 E1 64 20 2B 55 F9 C8 C3 67 08 F8 81 7F F2 F7 BE FD 67 1D 9E 9B 4F EB 89 25 21 28 19 63 09 5D A9 94 2A 59 AA 87 B3 32 4E 84 FF 3C 13 8F AF 06 F1 29 FD E2 C1 02 54 E0 8C 5E 5C 2E A5 72 43 65 F4 E5 72 61 5C 4A 45 81 41 97 79 0A 9E FF 62 22 C2 EB 7E 42 48 45 80 2B 0F 58 B9 66 D6 1C 63 75 D3 97 BC F4 08 0F 08 5C 6B 8D CC 14 1F 10 C4 5F B4 C3 37 66 EE E5 78 CF 42 12 05 CD F5 4D 29 EB 36 AD 19 82 CF 76 35 C6 86 B2 94 A7 36 FC 62 39 BE A8 B8 D7 02 78 08 8F 66 E4 44 45 DF BB AB A3 34 92 14 DF 54 C6 32 1D 00 21 7F 41 08 7F C9 7F F8 28 88 4F B5 90 89 3A 21 90 9E DF E8 DE 34 4D 8C D1 EF C6 AC 6B 7F 74 8E B1 FA 84 90 7F B0 F0 CF 5A E2 27 4C AF 8E 28 EF 24 48 B2 AF B1 9A 96 3E F6 43 75 38 6F 29 5D 1A C7 59 78 87 F0 1B B2 F0 95 05 3C 93 B2 26 B6 9F BA 18 AB AE 59 EC 88 17 17 A9 8F 4B B3 CE 60 A1 CD F2 CB 03 42 10 7F D9 06 1F 76 80 8E 4F 6D 9F 56 99 05 25 A5 DD AA EB 62 7A 6F 87 E6 B8 0E 5D 33 C6 99 C6 C4 02 24 EB 74 8C 87 98 64 F8 5E A9 9E 1E F7 72 FB FF B3 21 8A A4 00 01 00 28 F9 59 C5 E6 34 43 53 95 C8 17 2E 62 78 BF E8 27 BF 20 BA 11 5A 74 D1 7C D0 95 6C F6 A3 41 D2 84 BD 7D F6 64 BC 27 40 50 01 15 00 10 44 98 EB B8 30 3B DE 7D 2B CC 4C 41 B3 1C 92 86"
- val s = DataInputStream(data.hexToBytes().inputStream())
- val packet = ServerLoginResponseVerificationCodePacket(s,(data.length+1)/3)
- packet.decode()
- println(packet.token00BA.toUByteArray().toUHexString(" "))
- println(packet.verifyCode.toUByteArray().toUHexString(" "))
- println(packet.verifyCodeLength)
-
- File(System.getProperty("user.dir") + "/5.png").createNewFile()
- packet.verifyCode.inputStream().transferTo(FileOutputStream(System.getProperty("user.dir") + "/5.png"))
- */
-
- val packet = ClientPasswordSubmissionPacket(1994701021, "xiaoqqq", 131513, "123.123.123.123", "tgtgtKey".toByteArray(), "".toByteArray())
- packet.encodeToByteArray().toUByteArray().toUHexString(" ")
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ed7631a11..1e2441ae9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,10 +17,12 @@
+
huawei
https://mirrors.huaweicloud.com/repository/maven/
@@ -52,17 +54,24 @@
1.18.8
compile