mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-10 04:00:08 +08:00
Improve performance
This commit is contained in:
parent
24404f9af4
commit
fdb3be9453
@ -24,15 +24,17 @@ internal fun IoBuffer.parsePlainText(): PlainText {
|
|||||||
|
|
||||||
internal fun IoBuffer.parseMessageImage0x06(): Image {
|
internal fun IoBuffer.parseMessageImage0x06(): Image {
|
||||||
discardExact(1)
|
discardExact(1)
|
||||||
this.debugPrint("好友的图片")
|
with(this.debugPrint("好友的图片")) {
|
||||||
//MiraiLogger.logDebug(this.toUHexString())
|
|
||||||
val filenameLength = readShort()
|
//MiraiLogger.logDebug(this.toUHexString())
|
||||||
val suffix = readString(filenameLength).substringAfter(".")
|
val filenameLength = readShort()
|
||||||
discardExact(this@parseMessageImage0x06.readRemaining - 37 - 1 - filenameLength - 2)
|
val suffix = readString(filenameLength).substringAfter(".")
|
||||||
val imageId = readString(36)
|
discardExact(this.readRemaining - 37 - 1 - filenameLength - 2 - 8 - 4)
|
||||||
MiraiLogger.logDebug(imageId)
|
val imageId = readString(36)
|
||||||
discardExact(1)//0x41
|
MiraiLogger.logDebug("imageId=$imageId")//todo ID似乎错了??
|
||||||
return Image("{$imageId}.$suffix")
|
discardExact(1)//0x41
|
||||||
|
return Image("{$imageId}.$suffix")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun IoBuffer.parseMessageImage0x03(): Image {
|
internal fun IoBuffer.parseMessageImage0x03(): Image {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.mamoe.mirai.network
|
package net.mamoe.mirai.network
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.cancelChildren
|
||||||
import kotlinx.io.core.Closeable
|
import kotlinx.io.core.Closeable
|
||||||
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter
|
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter
|
||||||
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler
|
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.LoginHandler
|
||||||
@ -27,6 +29,7 @@ import kotlin.coroutines.ContinuationInterceptor
|
|||||||
*
|
*
|
||||||
* A BotNetworkHandler is used to connect with Tencent servers.
|
* A BotNetworkHandler is used to connect with Tencent servers.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("PropertyName")
|
||||||
interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : Closeable {
|
interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : Closeable {
|
||||||
/**
|
/**
|
||||||
* [BotNetworkHandler] 的协程作用域.
|
* [BotNetworkHandler] 的协程作用域.
|
||||||
@ -38,11 +41,11 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : Closeable {
|
|||||||
* - SKey 刷新 [ClientSKeyRefreshmentRequestPacket]
|
* - SKey 刷新 [ClientSKeyRefreshmentRequestPacket]
|
||||||
* - 所有数据包处理和发送
|
* - 所有数据包处理和发送
|
||||||
*
|
*
|
||||||
* [BotNetworkHandler.close] 时将会 [取消][CoroutineScope.cancel] 所有此作用域下的协程
|
* [BotNetworkHandler.close] 时将会 [取消][kotlin.coroutines.CoroutineContext.cancelChildren] 所有此作用域下的协程
|
||||||
*/
|
*/
|
||||||
val NetworkScope: CoroutineScope
|
val NetworkScope: CoroutineScope
|
||||||
|
|
||||||
var socket: Socket
|
val socket: Socket
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 得到 [PacketHandler].
|
* 得到 [PacketHandler].
|
||||||
@ -57,9 +60,10 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : Closeable {
|
|||||||
suspend fun login(configuration: LoginConfiguration): LoginResult
|
suspend fun login(configuration: LoginConfiguration): LoginResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加一个临时包处理器
|
* 添加一个临时包处理器, 并发送相应的包
|
||||||
*
|
*
|
||||||
* @see [TemporaryPacketHandler]
|
* @see [BotSession.sendAndExpect] 发送并期待一个包
|
||||||
|
* @see [TemporaryPacketHandler] 临时包处理器
|
||||||
*/
|
*/
|
||||||
suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>)
|
suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>)
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.network
|
package net.mamoe.mirai.network
|
||||||
|
|
||||||
import kotlinx.coroutines.CompletableJob
|
import kotlinx.coroutines.CompletableJob
|
||||||
@ -50,7 +52,7 @@ class BotSession(
|
|||||||
*
|
*
|
||||||
* 实现方法:
|
* 实现方法:
|
||||||
* ```kotlin
|
* ```kotlin
|
||||||
* session.expectPacket<ServerPacketXXX> {
|
* session.sendAndExpect<ServerPacketXXX> {
|
||||||
* toSend { ClientPacketXXX(...) }
|
* toSend { ClientPacketXXX(...) }
|
||||||
* onExpect {//it: ServerPacketXXX
|
* onExpect {//it: ServerPacketXXX
|
||||||
*
|
*
|
||||||
@ -62,7 +64,7 @@ class BotSession(
|
|||||||
* @param handlerTemporary 处理器.
|
* @param handlerTemporary 处理器.
|
||||||
*/
|
*/
|
||||||
//@JvmSynthetic
|
//@JvmSynthetic
|
||||||
suspend inline fun <reified P : ServerPacket> expectPacket(handlerTemporary: TemporaryPacketHandler<P>.() -> Unit): CompletableJob {
|
suspend inline fun <reified P : ServerPacket> sendAndExpect(handlerTemporary: TemporaryPacketHandler<P>.() -> Unit): CompletableJob {
|
||||||
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
|
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
|
||||||
this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also(handlerTemporary))
|
this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also(handlerTemporary))
|
||||||
return job
|
return job
|
||||||
@ -71,28 +73,28 @@ class BotSession(
|
|||||||
/**
|
/**
|
||||||
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
|
* 发送一个数据包, 并期待接受一个特定的 [ServerPacket].
|
||||||
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
|
* 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时.
|
||||||
* 由于包名可能过长, 可使用 `DataPacketSocketAdapter.expectPacket(PacketProcessor)` 替代.
|
* 由于包名可能过长, 可使用 `DataPacketSocketAdapter.sendAndExpect(PacketProcessor)` 替代.
|
||||||
*
|
*
|
||||||
* 实现方法:
|
* 实现方法:
|
||||||
* ```kotlin
|
* ```kotlin
|
||||||
* session.expectPacket<ServerPacketXXX>(ClientPacketXXX(...)) {//it: ServerPacketXXX
|
* ClientPacketXXX(...).sendAndExpect<ServerPacketXXX> {
|
||||||
*
|
* //it: ServerPacketXXX
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param P 期待的包
|
* @param P 期待的包
|
||||||
* @param toSend 将要发送的包
|
|
||||||
* @param handler 处理期待的包
|
* @param handler 处理期待的包
|
||||||
*/
|
*/
|
||||||
//@JvmSynthetic
|
suspend inline fun <reified P : ServerPacket> ClientPacket.sendAndExpect(noinline handler: suspend (P) -> Unit): CompletableJob {
|
||||||
suspend inline fun <reified P : ServerPacket> expectPacket(toSend: ClientPacket, noinline handler: suspend (P) -> Unit): CompletableJob {
|
|
||||||
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
|
val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job()
|
||||||
this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also {
|
bot.network.addHandler(TemporaryPacketHandler(P::class, job, this@BotSession).also {
|
||||||
it.toSend(toSend)
|
it.toSend(this)
|
||||||
it.onExpect(handler)
|
it.onExpect(handler)
|
||||||
})
|
})
|
||||||
return job
|
return job
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend inline fun ClientPacket.send() = socket.sendPacket(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
override val NetworkScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
override val NetworkScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
override lateinit var socket: BotSocketAdapter
|
override lateinit var socket: BotSocketAdapter
|
||||||
|
private set
|
||||||
|
|
||||||
internal val temporaryPacketHandlers = mutableListOf<TemporaryPacketHandler<*>>()
|
internal val temporaryPacketHandlers = mutableListOf<TemporaryPacketHandler<*>>()
|
||||||
private val handlersLock = Mutex()
|
private val handlersLock = Mutex()
|
||||||
@ -73,6 +74,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
|
|
||||||
add(EventPacketHandler(session).asNode(EventPacketHandler))
|
add(EventPacketHandler(session).asNode(EventPacketHandler))
|
||||||
add(ActionPacketHandler(session).asNode(ActionPacketHandler))
|
add(ActionPacketHandler(session).asNode(ActionPacketHandler))
|
||||||
|
bot.logger.logPurple("Successfully logged in")
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var sessionKey: ByteArray
|
private lateinit var sessionKey: ByteArray
|
||||||
@ -110,9 +112,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
try {
|
try {
|
||||||
channel.read(buffer)//JVM: withContext(IO)
|
channel.read(buffer)//JVM: withContext(IO)
|
||||||
} catch (e: ReadPacketInternalException) {
|
} catch (e: ReadPacketInternalException) {
|
||||||
|
//read failed, continue and reread
|
||||||
} catch (e: Exception) {
|
continue
|
||||||
e.log()
|
} catch (e: Throwable) {
|
||||||
|
e.log()//other unexpected exceptions caught.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,11 +126,11 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
|
|
||||||
NetworkScope.launch {
|
NetworkScope.launch {
|
||||||
try {
|
try {
|
||||||
//Ensure the packet is consumed totally so that all buffers are released
|
//`.use`: Ensure that the packet is consumed totally so that all the buffers are released
|
||||||
ByteReadPacket(buffer, IoBuffer.Pool).use {
|
ByteReadPacket(buffer, IoBuffer.Pool).use {
|
||||||
distributePacket(it.parseServerPacket(buffer.readRemaining))
|
distributePacket(it.parseServerPacket(buffer.readRemaining))
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Throwable) {
|
||||||
e.log()
|
e.log()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +171,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
try {
|
try {
|
||||||
packet.decode()
|
packet.decode()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
e.log()
|
||||||
bot.printPacketDebugging(packet)
|
bot.printPacketDebugging(packet)
|
||||||
packet.close()
|
packet.close()
|
||||||
throw e
|
throw e
|
||||||
@ -238,9 +242,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
|
bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
|
||||||
bot.reinitializeNetworkHandler(configuration)
|
bot.reinitializeNetworkHandler(configuration)
|
||||||
return@withContext
|
return@withContext
|
||||||
} catch (e: Throwable) {
|
|
||||||
e.log()
|
|
||||||
return@withContext
|
|
||||||
} finally {
|
} finally {
|
||||||
buffer.release(IoBuffer.Pool)
|
buffer.release(IoBuffer.Pool)
|
||||||
}
|
}
|
||||||
@ -338,7 +339,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
is ServerLoginResponseCaptchaInitPacket -> {
|
is ServerLoginResponseCaptchaInitPacket -> {
|
||||||
//[token00BA]来源之一: 验证码
|
//[token00BA]来源之一: 验证码
|
||||||
this.token00BA = packet.token00BA
|
this.token00BA = packet.token00BA
|
||||||
this.captchaCache = packet.verifyCodePart1
|
this.captchaCache = packet.captchaPart1
|
||||||
|
|
||||||
if (packet.unknownBoolean == true) {
|
if (packet.unknownBoolean == true) {
|
||||||
this.captchaSectionId = 1
|
this.captchaSectionId = 1
|
||||||
@ -390,12 +391,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
privateKey = privateKey,
|
privateKey = privateKey,
|
||||||
token0825 = token0825,
|
token0825 = token0825,
|
||||||
token00BA = packet.tokenUnknown ?: token00BA,
|
token00BA = packet.tokenUnknown ?: token00BA,
|
||||||
randomDeviceName = socket.configuration.randomDeviceName
|
randomDeviceName = socket.configuration.randomDeviceName,
|
||||||
|
tlv0006 = packet.tlv0006
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServerSessionKeyResponsePacket -> {
|
is ServerSessionKeyResponsePacket -> {
|
||||||
sessionKey = packet.sessionKey
|
sessionKey = packet.sessionKey
|
||||||
|
bot.logger.logPurple("sessionKey = ${sessionKey.toUHexString()}")
|
||||||
|
|
||||||
heartbeatJob = NetworkScope.launch {
|
heartbeatJob = NetworkScope.launch {
|
||||||
while (socket.isOpen) {
|
while (socket.isOpen) {
|
||||||
@ -412,14 +415,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
|
|||||||
is ServerLoginSuccessPacket -> {
|
is ServerLoginSuccessPacket -> {
|
||||||
BotLoginSucceedEvent(bot).broadcast()
|
BotLoginSucceedEvent(bot).broadcast()
|
||||||
|
|
||||||
//登录成功后会收到大量上次的消息, 忽略掉 todo 优化
|
|
||||||
NetworkScope.launch {
|
|
||||||
delay(3000)
|
|
||||||
this@TIMBotNetworkHandler[EventPacketHandler].ignoreMessage = false
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoggedIn(sessionKey)
|
onLoggedIn(sessionKey)
|
||||||
|
|
||||||
this.close()//The LoginHandler is useless since then
|
this.close()//The LoginHandler is useless since then
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.action.ServerCanAddFriendResp
|
|||||||
import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRequestPacket
|
import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRefreshmentRequestPacket
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRequestPacket
|
import net.mamoe.mirai.network.protocol.tim.packet.login.ClientSKeyRequestPacket
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.login.ServerSKeyResponsePacket
|
import net.mamoe.mirai.network.protocol.tim.packet.login.ServerSKeyResponsePacket
|
||||||
|
import net.mamoe.mirai.utils.log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动作: 获取好友列表, 点赞, 踢人等.
|
* 动作: 获取好友列表, 点赞, 踢人等.
|
||||||
@ -64,7 +65,11 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
|
|||||||
sKeyRefresherJob = session.scope.launch {
|
sKeyRefresherJob = session.scope.launch {
|
||||||
while (session.isOpen) {
|
while (session.isOpen) {
|
||||||
delay(1800000)
|
delay(1800000)
|
||||||
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.account, session.sessionKey))
|
try {
|
||||||
|
session.socket.sendPacket(ClientSKeyRefreshmentRequestPacket(session.bot.account.account, session.sessionKey))
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.log()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ interface DataPacketSocketAdapter : Closeable {
|
|||||||
*
|
*
|
||||||
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
|
* 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回.
|
||||||
*
|
*
|
||||||
* @see [BotSession.expectPacket] kotlin DSL
|
* @see [BotSession.sendAndExpect] kotlin DSL
|
||||||
*/
|
*/
|
||||||
suspend fun sendPacket(packet: ClientPacket)
|
suspend fun sendPacket(packet: ClientPacket)
|
||||||
|
|
||||||
|
@ -26,9 +26,6 @@ import net.mamoe.mirai.utils.MiraiLogger
|
|||||||
class EventPacketHandler(session: BotSession) : PacketHandler(session) {
|
class EventPacketHandler(session: BotSession) : PacketHandler(session) {
|
||||||
companion object Key : PacketHandler.Key<EventPacketHandler>
|
companion object Key : PacketHandler.Key<EventPacketHandler>
|
||||||
|
|
||||||
|
|
||||||
internal var ignoreMessage: Boolean = true
|
|
||||||
|
|
||||||
override suspend fun onPacketReceived(packet: ServerPacket): Unit = with(session) {
|
override suspend fun onPacketReceived(packet: ServerPacket): Unit = with(session) {
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is ServerGroupUploadFileEventPacket -> {
|
is ServerGroupUploadFileEventPacket -> {
|
||||||
@ -36,14 +33,12 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is ServerFriendMessageEventPacket -> {
|
is ServerFriendMessageEventPacket -> {
|
||||||
if (ignoreMessage) return
|
if (!packet.isPrevious) {
|
||||||
|
FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast()
|
||||||
FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ServerGroupMessageEventPacket -> {
|
is ServerGroupMessageEventPacket -> {
|
||||||
if (ignoreMessage) return
|
|
||||||
|
|
||||||
if (packet.qq.toLong() == bot.account.account) return
|
if (packet.qq.toLong() == bot.account.account) return
|
||||||
|
|
||||||
GroupMessageEvent(bot, bot.getGroupByNumber(packet.groupNumber), bot.getQQ(packet.qq), packet.message).broadcast()
|
GroupMessageEvent(bot, bot.getGroupByNumber(packet.groupNumber), bot.getQQ(packet.qq), packet.message).broadcast()
|
||||||
|
@ -17,11 +17,11 @@ import kotlin.reflect.KClass
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @see BotSession.expectPacket
|
* @see BotSession.sendAndExpect
|
||||||
*/
|
*/
|
||||||
class TemporaryPacketHandler<P : ServerPacket>(
|
class TemporaryPacketHandler<P : ServerPacket>(
|
||||||
private val expectationClass: KClass<P>,
|
private val expectationClass: KClass<P>,
|
||||||
private val deferred: CompletableJob,
|
private val job: CompletableJob,
|
||||||
private val fromSession: BotSession
|
private val fromSession: BotSession
|
||||||
) {
|
) {
|
||||||
private lateinit var toSend: ClientPacket
|
private lateinit var toSend: ClientPacket
|
||||||
@ -54,7 +54,7 @@ class TemporaryPacketHandler<P : ServerPacket>(
|
|||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
expect(packet as P)
|
expect(packet as P)
|
||||||
}.onFailure { deferred.completeExceptionally(it) }.onSuccess { deferred.complete() }
|
}.onFailure { job.completeExceptionally(it) }.onSuccess { job.complete() }
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -5,7 +5,9 @@ package net.mamoe.mirai.network.protocol.tim.packet
|
|||||||
import kotlinx.io.core.BytePacketBuilder
|
import kotlinx.io.core.BytePacketBuilder
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.encryptAndWrite
|
||||||
|
import net.mamoe.mirai.utils.writeHex
|
||||||
|
import net.mamoe.mirai.utils.writeQQ
|
||||||
|
|
||||||
@PacketId(0x00_58u)
|
@PacketId(0x00_58u)
|
||||||
class ClientHeartbeatPacket(
|
class ClientHeartbeatPacket(
|
||||||
@ -13,9 +15,9 @@ class ClientHeartbeatPacket(
|
|||||||
private val sessionKey: ByteArray
|
private val sessionKey: ByteArray
|
||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
||||||
this.writeQQ(bot)
|
writeQQ(bot)
|
||||||
this.writeHex(TIMProtocol.fixVer)
|
writeHex(TIMProtocol.fixVer)
|
||||||
this.encryptAndWrite(sessionKey) {
|
encryptAndWrite(sessionKey) {
|
||||||
writeHex("00 01 00 01")
|
writeHex("00 01 00 01")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,16 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.network.protocol.tim.packet
|
package net.mamoe.mirai.network.protocol.tim.packet
|
||||||
|
|
||||||
import kotlinx.io.core.Closeable
|
|
||||||
import net.mamoe.mirai.utils.toUHexString
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据包.
|
* 数据包.
|
||||||
*/
|
*/
|
||||||
abstract class Packet : Closeable {
|
abstract class Packet {
|
||||||
/**
|
/**
|
||||||
* 2 Ubyte
|
* 2 Ubyte
|
||||||
*/
|
*/
|
||||||
open val id: UShort = (this::class.annotations.firstOrNull { it is PacketId } as? PacketId)?.value ?: error("Annotation PacketId not found")
|
open val id: UShort by lazy { (this::class.annotations.firstOrNull { it is PacketId } as? PacketId)?.value ?: error("Annotation PacketId not found") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包序列 id. 唯一
|
* 包序列 id. 唯一
|
||||||
|
@ -26,13 +26,20 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) =
|
|||||||
writeFully(uniqueId)
|
writeFully(uniqueId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <S : ServerEventPacket> S.applyId(id: UShort): S {
|
||||||
|
this.id = id
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Packet id: `00 CE` or `00 17`
|
* Packet id: `00 CE` or `00 17`
|
||||||
*
|
*
|
||||||
* @author Him188moe
|
* @author Him188moe
|
||||||
*/
|
*/
|
||||||
abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: EventPacketIdentity) : ServerPacket(input) {
|
abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: EventPacketIdentity) : ServerPacket(input) {
|
||||||
class Raw(input: ByteReadPacket) : ServerPacket(input) {
|
override var id: UShort = 0u
|
||||||
|
|
||||||
|
class Raw(input: ByteReadPacket, override val id: UShort) : ServerPacket(input) {
|
||||||
fun distribute(): ServerEventPacket = with(input) {
|
fun distribute(): ServerEventPacket = with(input) {
|
||||||
val eventIdentity = EventPacketIdentity(
|
val eventIdentity = EventPacketIdentity(
|
||||||
from = readUInt(),
|
from = readUInt(),
|
||||||
@ -65,7 +72,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
|
|||||||
println(readUByte().toUInt())
|
println(readUByte().toUInt())
|
||||||
|
|
||||||
//todo 错了. 可能是 00 79 才是.
|
//todo 错了. 可能是 00 79 才是.
|
||||||
return@with ServerFriendTypingCanceledPacket(input, eventIdentity)
|
ServerFriendTypingCanceledPacket(input, eventIdentity)
|
||||||
/*
|
/*
|
||||||
if (readUByte().toUInt() == 0x37u) ServerFriendTypingStartedPacket(input, eventIdentity)
|
if (readUByte().toUInt() == 0x37u) ServerFriendTypingStartedPacket(input, eventIdentity)
|
||||||
else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/
|
else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/
|
||||||
@ -81,8 +88,8 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
|
|||||||
}.applyId(id).applySequence(sequenceId)
|
}.applyId(id).applySequence(sequenceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
|
class Encrypted(input: ByteReadPacket, override var id: UShort, override var sequenceId: UShort) : ServerPacket(input) {
|
||||||
fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey)).applyId(id).applySequence(sequenceId)
|
fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey), id).applySequence(sequenceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +192,8 @@ class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventP
|
|||||||
senderName = map.getValue(18).read {
|
senderName = map.getValue(18).read {
|
||||||
val tlv = readTLVMap(true)
|
val tlv = readTLVMap(true)
|
||||||
tlv.printTLVMap("子map")
|
tlv.printTLVMap("子map")
|
||||||
//群主的18: 05 00 04 00 00 00 03 08 00 04 00 00 00 04 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
|
////群主的18: 05 00 04 00 00 00 03 08 00 04 00 00 00 04 01 00 09 48 69 6D 31 38 38 6D 6F 65 03 00 01 04 04 00 04 00 00 00 08
|
||||||
|
//群主的 子map= {5=00 00 00 03, 8=00 00 00 04, 1=48 69 6D 31 38 38 6D 6F 65, 3=04, 4=00 00 00 08}
|
||||||
when {
|
when {
|
||||||
tlv.containsKey(0x01) -> String(tlv.getValue(0x01))
|
tlv.containsKey(0x01) -> String(tlv.getValue(0x01))
|
||||||
tlv.containsKey(0x02) -> String(tlv.getValue(0x02))
|
tlv.containsKey(0x02) -> String(tlv.getValue(0x02))
|
||||||
@ -202,7 +209,7 @@ class ServerGroupMessageEventPacket(input: ByteReadPacket, eventIdentity: EventP
|
|||||||
//刚刚的消息: 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 01 38 03 3E 03 3F A2 76 E4 B8 DD 11 F4 B2 F2 1A E7 1F C4 F1 3F 23 FB 74 80 42 64 00 0B 78 1A 5D A3 26 C1 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A3 26 C1 AA 34 08 42 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E4 BD A0 E5 A5 BD 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
|
//刚刚的消息: 00 00 00 2D 00 05 00 02 00 01 00 06 00 04 00 01 2E 01 00 09 00 06 00 01 00 00 00 01 00 0A 00 04 01 00 00 00 00 01 00 04 00 00 00 00 00 03 00 01 01 38 03 3E 03 3F A2 76 E4 B8 DD 11 F4 B2 F2 1A E7 1F C4 F1 3F 23 FB 74 80 42 64 00 0B 78 1A 5D A3 26 C1 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A3 26 C1 AA 34 08 42 00 00 00 00 0C 00 86 22 00 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 00 00 01 00 09 01 00 06 E4 BD A0 E5 A5 BD 0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
println("08 02 1A 12 08 95 02 10 90 04 40 D6 DE 8C ED 05 48 CF B5 90 D6 02 08 DD F1 92 B7 07 10 DD F1 92 B7 07 1A 14 08 00 10 05 18 D6 DE 8C ED 05 20 02 28 FF FF FF FF 0F 32 00".hexToBytes().stringOf())
|
println("01 00 32 AA 02 2F 50 03 60 00 68 00 9A 01 26 08 09 20 BF 02 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00".hexToBytes().stringOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main2() {
|
fun main2() {
|
||||||
|
@ -16,7 +16,8 @@ import kotlin.properties.Delegates
|
|||||||
* @see parseServerPacket
|
* @see parseServerPacket
|
||||||
*/
|
*/
|
||||||
abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable {
|
abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable {
|
||||||
override var id: UShort = super.id
|
override val id: UShort by lazy { super.id }
|
||||||
|
|
||||||
override var sequenceId: UShort by Delegates.notNull()
|
override var sequenceId: UShort by Delegates.notNull()
|
||||||
|
|
||||||
open fun decode() {
|
open fun decode() {
|
||||||
@ -28,11 +29,6 @@ abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable {
|
|||||||
override fun toString(): String = this.packetToString()
|
override fun toString(): String = this.packetToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <S : ServerPacket> S.applyId(id: UShort): S {
|
|
||||||
this.id = id
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <S : ServerPacket> S.applySequence(sequenceId: UShort): S {
|
fun <S : ServerPacket> S.applySequence(sequenceId: UShort): S {
|
||||||
this.sequenceId = sequenceId
|
this.sequenceId = sequenceId
|
||||||
return this
|
return this
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.network.protocol.tim.packet
|
package net.mamoe.mirai.network.protocol.tim.packet
|
||||||
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.readBytes
|
import kotlinx.io.core.readBytes
|
||||||
import net.mamoe.mirai.utils.gotoWhere
|
import net.mamoe.mirai.contact.Group
|
||||||
import net.mamoe.mirai.utils.toReadPacket
|
import net.mamoe.mirai.contact.QQ
|
||||||
|
import net.mamoe.mirai.network.BotSession
|
||||||
expect class PlatformImage
|
import net.mamoe.mirai.network.account
|
||||||
|
import net.mamoe.mirai.network.protocol.tim.handler.ActionPacketHandler
|
||||||
|
import net.mamoe.mirai.qqAccount
|
||||||
|
import net.mamoe.mirai.utils.*
|
||||||
|
|
||||||
|
@PacketId(0x03_88u)
|
||||||
expect class ClientTryGetImageIDPacket(
|
expect class ClientTryGetImageIDPacket(
|
||||||
botNumber: Long,
|
botNumber: Long,
|
||||||
sessionKey: ByteArray,
|
sessionKey: ByteArray,
|
||||||
@ -16,19 +20,18 @@ expect class ClientTryGetImageIDPacket(
|
|||||||
image: PlatformImage
|
image: PlatformImage
|
||||||
) : ClientPacket
|
) : ClientPacket
|
||||||
|
|
||||||
|
@PacketId(0x03_88u)
|
||||||
abstract class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
|
sealed class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
|
||||||
|
@PacketId(0x03_88u)
|
||||||
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
|
class Encrypted(input: ByteReadPacket) : ServerPacket(input) {
|
||||||
fun decrypt(sessionKey: ByteArray): ServerTryGetImageIDResponsePacket {
|
fun decrypt(sessionKey: ByteArray): ServerTryGetImageIDResponsePacket {
|
||||||
val data = this.decryptAsByteArray(sessionKey)
|
val data = this.decryptAsByteArray(sessionKey)
|
||||||
println(data.size)
|
println("ServerTryGetImageIDResponsePacket.size=" + data.size)
|
||||||
println(data.size)
|
|
||||||
if (data.size == 209) {
|
if (data.size == 209) {
|
||||||
return ServerTryGetImageIDSuccessPacket(data.toReadPacket()).applySequence(sequenceId)
|
return ServerTryGetImageIDSuccessPacket(data.toReadPacket()).applySequence(sequenceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ServerTryGetImageIDFailedPacket(data.toReadPacket())
|
return ServerTryGetImageIDFailedPacket(data.toReadPacket()).applySequence(sequenceId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -36,12 +39,14 @@ abstract class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : Server
|
|||||||
/**
|
/**
|
||||||
* 服务器未存有图片, 返回一个 key 用于客户端上传
|
* 服务器未存有图片, 返回一个 key 用于客户端上传
|
||||||
*/
|
*/
|
||||||
|
@PacketId(0x03_88u)
|
||||||
class ServerTryGetImageIDSuccessPacket(input: ByteReadPacket) : ServerTryGetImageIDResponsePacket(input) {
|
class ServerTryGetImageIDSuccessPacket(input: ByteReadPacket) : ServerTryGetImageIDResponsePacket(input) {
|
||||||
lateinit var uKey: ByteArray
|
lateinit var uKey: ByteArray
|
||||||
|
|
||||||
override fun decode() {
|
override fun decode() {
|
||||||
this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u))
|
this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u))//todo 优化
|
||||||
uKey = this.input.readBytes(128)
|
uKey = this.input.readBytes(128)
|
||||||
|
DebugLogger.logPurple("获得 uKey(128)=${uKey.toUHexString()}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +54,41 @@ class ServerTryGetImageIDSuccessPacket(input: ByteReadPacket) : ServerTryGetImag
|
|||||||
* 服务器已经存有这个图片
|
* 服务器已经存有这个图片
|
||||||
*/
|
*/
|
||||||
class ServerTryGetImageIDFailedPacket(input: ByteReadPacket) : ServerTryGetImageIDResponsePacket(input) {
|
class ServerTryGetImageIDFailedPacket(input: ByteReadPacket) : ServerTryGetImageIDResponsePacket(input) {
|
||||||
override fun decode() {
|
override fun decode(): Unit = with(input) {
|
||||||
|
readRemainingBytes().debugPrint("ServerTryGetImageIDFailedPacket的body")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun Group.uploadImage(imageId: String, image: PlatformImage) {
|
||||||
|
this.bot.network[ActionPacketHandler].session.uploadGroupImage(number, imageId, image)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun QQ.uploadImage(imageId: String, image: PlatformImage) {
|
||||||
|
TODO()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun BotSession.uploadGroupImage(groupNumberOrAccount: Long, imageId: String, image: PlatformImage) {
|
||||||
|
ClientTryGetImageIDPacket(
|
||||||
|
account,
|
||||||
|
sessionKey,
|
||||||
|
groupNumberOrAccount,
|
||||||
|
image
|
||||||
|
).sendAndExpect<ServerTryGetImageIDResponsePacket> {
|
||||||
|
when (it) {
|
||||||
|
is ServerTryGetImageIDFailedPacket -> {
|
||||||
|
//服务器已存有图片
|
||||||
|
}
|
||||||
|
is ServerTryGetImageIDSuccessPacket -> {
|
||||||
|
val data = image.toByteArray()
|
||||||
|
httpPostGroupImage(
|
||||||
|
uKeyHex = it.uKey.toUHexString(""),
|
||||||
|
botNumber = bot.qqAccount,
|
||||||
|
fileSize = data.size,
|
||||||
|
imageData = data,
|
||||||
|
groupCode = groupNumberOrAccount
|
||||||
|
)
|
||||||
|
//todo HTTP upload image.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
}
|
}
|
@ -7,19 +7,22 @@ import kotlinx.io.core.writeUByte
|
|||||||
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
|
import net.mamoe.mirai.network.protocol.tim.packet.ClientPacket
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
|
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.OnlineStatus
|
||||||
|
import net.mamoe.mirai.utils.encryptAndWrite
|
||||||
|
import net.mamoe.mirai.utils.writeHex
|
||||||
|
import net.mamoe.mirai.utils.writeQQ
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 改变在线状态: "我在线上", "隐身" 等
|
* 改变在线状态: "我在线上", "隐身" 等
|
||||||
*/
|
*/
|
||||||
@PacketId(0x00_ECu)
|
@PacketId(0x00_ECu)
|
||||||
class ClientChangeOnlineStatusPacket(
|
class ClientChangeOnlineStatusPacket(
|
||||||
private val qq: Long,
|
private val bot: Long,
|
||||||
private val sessionKey: ByteArray,
|
private val sessionKey: ByteArray,
|
||||||
private val loginStatus: OnlineStatus
|
private val loginStatus: OnlineStatus
|
||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
||||||
this.writeQQ(qq)
|
this.writeQQ(bot)
|
||||||
this.writeHex(TIMProtocol.fixVer2)
|
this.writeHex(TIMProtocol.fixVer2)
|
||||||
this.encryptAndWrite(sessionKey) {
|
this.encryptAndWrite(sessionKey) {
|
||||||
writeHex("01 00")
|
writeHex("01 00")
|
||||||
|
@ -45,8 +45,10 @@ class ServerSKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) {
|
|||||||
|
|
||||||
override fun decode() = with(input) {
|
override fun decode() = with(input) {
|
||||||
discardExact(4)
|
discardExact(4)
|
||||||
sKey = this.readString(10)//todo test
|
//debugDiscardExact(2)
|
||||||
MiraiLogger.logDebug("SKey=$sKey")
|
sKey = this.readString(10)
|
||||||
|
DebugLogger.logPurple("SKey=$sKey")
|
||||||
|
DebugLogger.logPurple("Skey包后面${this.readRemainingBytes().toUHexString()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@PacketId(0x00_1Du)
|
@PacketId(0x00_1Du)
|
||||||
|
@ -16,8 +16,6 @@ class ServerLoginResponseFailedPacket(val loginResult: LoginResult, input: ByteR
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务器进行加密后返回 privateKey
|
* 服务器进行加密后返回 privateKey
|
||||||
*
|
|
||||||
* @author NaturalHG
|
|
||||||
*/
|
*/
|
||||||
@PacketId(0x08_36u)
|
@PacketId(0x08_36u)
|
||||||
class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
|
class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
|
||||||
@ -29,7 +27,7 @@ class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginR
|
|||||||
@Tested
|
@Tested
|
||||||
override fun decode() {
|
override fun decode() {
|
||||||
this.input.discardExact(5)//01 00 1E 00 10
|
this.input.discardExact(5)//01 00 1E 00 10
|
||||||
privateKeyUpdate = this.input.readBytes(0x10)//22
|
privateKeyUpdate = this.input.readBytes(0x10)
|
||||||
this.input.discardExact(4)//00 06 00 78
|
this.input.discardExact(4)//00 06 00 78
|
||||||
tlv0006 = this.input.readIoBuffer(0x78)
|
tlv0006 = this.input.readIoBuffer(0x78)
|
||||||
|
|
||||||
@ -127,7 +125,7 @@ class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginRespo
|
|||||||
@PacketId(0x08_36u)
|
@PacketId(0x08_36u)
|
||||||
class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
|
class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) {
|
||||||
|
|
||||||
lateinit var verifyCodePart1: IoBuffer
|
lateinit var captchaPart1: IoBuffer
|
||||||
lateinit var token00BA: ByteArray
|
lateinit var token00BA: ByteArray
|
||||||
var unknownBoolean: Boolean? = null
|
var unknownBoolean: Boolean? = null
|
||||||
|
|
||||||
@ -136,8 +134,8 @@ class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginR
|
|||||||
override fun decode() {
|
override fun decode() {
|
||||||
this.input.discardExact(78)
|
this.input.discardExact(78)
|
||||||
//println(this.input.readRemainingBytes().toUHexString())
|
//println(this.input.readRemainingBytes().toUHexString())
|
||||||
val verifyCodeLength = this.input.readShort()//2bytes
|
val captchaLength = this.input.readShort()//2bytes
|
||||||
this.verifyCodePart1 = this.input.readIoBuffer(verifyCodeLength)
|
this.captchaPart1 = this.input.readIoBuffer(captchaLength)
|
||||||
|
|
||||||
this.input.discardExact(1)
|
this.input.discardExact(1)
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@ import net.mamoe.mirai.utils.*
|
|||||||
|
|
||||||
@PacketId(0x08_28u)
|
@PacketId(0x08_28u)
|
||||||
class ClientSessionRequestPacket(
|
class ClientSessionRequestPacket(
|
||||||
private val qq: Long,
|
private val bot: Long,
|
||||||
private val serverIp: String,
|
private val serverIp: String,
|
||||||
private val token38: IoBuffer,
|
private val token38: IoBuffer,
|
||||||
private val token88: IoBuffer,
|
private val token88: IoBuffer,
|
||||||
private val encryptionKey: IoBuffer
|
private val encryptionKey: IoBuffer
|
||||||
) : ClientPacket() {
|
) : ClientPacket() {
|
||||||
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
override fun encode(builder: BytePacketBuilder) = with(builder) {
|
||||||
this.writeQQ(qq)
|
this.writeQQ(bot)
|
||||||
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
|
this.writeHex("02 00 00 00 01 2E 01 00 00 68 52 00 30 00 3A")
|
||||||
this.writeHex("00 38")
|
this.writeHex("00 38")
|
||||||
this.writeFully(token38)
|
this.writeFully(token38)
|
||||||
@ -31,7 +31,7 @@ class ClientSessionRequestPacket(
|
|||||||
writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00")
|
writeHex("00 36 00 12 00 02 00 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00")
|
||||||
writeHex(TIMProtocol.constantData1)
|
writeHex(TIMProtocol.constantData1)
|
||||||
writeHex(TIMProtocol.constantData2)
|
writeHex(TIMProtocol.constantData2)
|
||||||
writeQQ(qq)
|
writeQQ(bot)
|
||||||
writeHex("00 00 00 00 00 1F 00 22 00 01")
|
writeHex("00 00 00 00 00 1F 00 22 00 01")
|
||||||
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
|
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
|
||||||
|
|
||||||
|
@ -69,12 +69,12 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
|
|||||||
}
|
}
|
||||||
0x08_28u -> ServerSessionKeyResponsePacket.Encrypted(this)
|
0x08_28u -> ServerSessionKeyResponsePacket.Encrypted(this)
|
||||||
|
|
||||||
0x00_EC_u -> ServerSKeyResponsePacket(this)
|
0x00_EC_u -> ServerLoginSuccessPacket(this)
|
||||||
0x00_1D_u -> ServerSKeyResponsePacket.Encrypted(this)
|
0x00_1D_u -> ServerSKeyResponsePacket.Encrypted(this)
|
||||||
0x00_5C_u -> ServerAccountInfoResponsePacket.Encrypted(this)
|
0x00_5C_u -> ServerAccountInfoResponsePacket.Encrypted(this)
|
||||||
0x00_58_u -> ServerHeartbeatResponsePacket(this)
|
0x00_58_u -> ServerHeartbeatResponsePacket(this)
|
||||||
0x00_BA_u -> ServerCaptchaPacket.Encrypted(this)
|
0x00_BA_u -> ServerCaptchaPacket.Encrypted(this)
|
||||||
0x00_CE_u, 0x00_17_u -> ServerEventPacket.Raw.Encrypted(this)
|
0x00_CE_u, 0x00_17_u -> ServerEventPacket.Raw.Encrypted(this, id, sequenceId)
|
||||||
0x00_81_u -> ServerFieldOnlineStatusChangedPacket.Encrypted(this)
|
0x00_81_u -> ServerFieldOnlineStatusChangedPacket.Encrypted(this)
|
||||||
0x00_CD_u -> ServerSendFriendMessageResponsePacket(this)
|
0x00_CD_u -> ServerSendFriendMessageResponsePacket(this)
|
||||||
0x00_02_u -> ServerSendGroupMessageResponsePacket(this)
|
0x00_02_u -> ServerSendGroupMessageResponsePacket(this)
|
||||||
@ -82,7 +82,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
|
|||||||
0x03_88_u -> ServerTryGetImageIDResponsePacket.Encrypted(this)
|
0x03_88_u -> ServerTryGetImageIDResponsePacket.Encrypted(this)
|
||||||
|
|
||||||
else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
|
else -> UnknownServerPacket.Encrypted(this, id, sequenceId)
|
||||||
}.applyId(id).applySequence(sequenceId)
|
}.applySequence(sequenceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Input.readIP(): String = buildString(4 + 3) {
|
fun Input.readIP(): String = buildString(4 + 3) {
|
||||||
@ -145,6 +145,7 @@ fun Input.readLVNumber(): Number {
|
|||||||
//@JvmSynthetic
|
//@JvmSynthetic
|
||||||
@Deprecated("Low efficiency", ReplaceWith(""))
|
@Deprecated("Low efficiency", ReplaceWith(""))
|
||||||
fun <I : Input> I.gotoWhere(matcher: UByteArray): I {
|
fun <I : Input> I.gotoWhere(matcher: UByteArray): I {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
return this.gotoWhere(matcher.toByteArray())
|
return this.gotoWhere(matcher.toByteArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,19 +15,19 @@ internal fun ByteArray.debugPrint(name: String): ByteArray {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Low Efficiency", ReplaceWith(""))
|
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(""))
|
||||||
internal fun IoBuffer.debugPrint(name: String): IoBuffer {
|
internal fun IoBuffer.debugPrint(name: String): IoBuffer {
|
||||||
val readBytes = this.readBytes()
|
val readBytes = this.readBytes()
|
||||||
DebugLogger.logPurple(name + "=" + readBytes.toUHexString())
|
DebugLogger.logPurple(name + "=" + readBytes.toUHexString())
|
||||||
return readBytes.toIoBuffer()
|
return readBytes.toIoBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Low Efficiency", ReplaceWith("discardExact(n)"))
|
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("discardExact(n)"))
|
||||||
internal fun Input.debugDiscardExact(n: Number, name: String = "") {
|
internal fun Input.debugDiscardExact(n: Number, name: String = "") {
|
||||||
DebugLogger.logPurple("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
|
DebugLogger.logPurple("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Low Efficiency", ReplaceWith(""))
|
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(""))
|
||||||
internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
|
internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
|
||||||
val bytes = this.readBytes()
|
val bytes = this.readBytes()
|
||||||
DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString())
|
DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString())
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import kotlin.jvm.JvmOverloads
|
||||||
|
|
||||||
|
|
||||||
|
expect class PlatformImage
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
expect fun PlatformImage.toByteArray(formatName: String = "JPG"): ByteArray
|
@ -31,4 +31,15 @@ expect fun solveIpAddress(hostname: String): String
|
|||||||
/**
|
/**
|
||||||
* Localhost 解析
|
* Localhost 解析
|
||||||
*/
|
*/
|
||||||
expect fun localIpAddress(): String
|
expect fun localIpAddress(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传群图片
|
||||||
|
*/
|
||||||
|
expect suspend fun httpPostGroupImage(
|
||||||
|
uKeyHex: String,
|
||||||
|
fileSize: Int,
|
||||||
|
botNumber: Long,
|
||||||
|
groupCode: Long,
|
||||||
|
imageData: ByteArray
|
||||||
|
): Boolean
|
@ -0,0 +1,22 @@
|
|||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import kotlin.properties.Delegates
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
|
||||||
|
fun <T : Any> Delegates.notNullBy(initializer: () -> T): ReadWriteProperty<Any?, T> = NotNullVarWithDefault(lazy(initializer = initializer))
|
||||||
|
|
||||||
|
class NotNullVarWithDefault<T : Any>(
|
||||||
|
private val initializer: Lazy<T>
|
||||||
|
) : ReadWriteProperty<Any?, T> {
|
||||||
|
private var value: T? = null
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
return value ?: initializer.value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,6 @@ actual sealed class Contact actual constructor(bot: Bot, number: Long) : Platfor
|
|||||||
*/
|
*/
|
||||||
fun blockingSendMessage(message: Message) = runBlocking { sendMessage(message) }
|
fun blockingSendMessage(message: Message) = runBlocking { sendMessage(message) }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 阻塞发送一个消息. 仅应在 Java 使用
|
* 阻塞发送一个消息. 仅应在 Java 使用
|
||||||
*/
|
*/
|
||||||
@ -44,23 +43,17 @@ actual sealed class Contact actual constructor(bot: Bot, number: Long) : Platfor
|
|||||||
/**
|
/**
|
||||||
* 异步发送一个消息. 仅应在 Java 使用
|
* 异步发送一个消息. 仅应在 Java 使用
|
||||||
*/
|
*/
|
||||||
fun asyncSendMessage(chain: MessageChain) {
|
fun asyncSendMessage(chain: MessageChain) = bot.network.NetworkScope.launch { sendMessage(chain) }
|
||||||
bot.network.NetworkScope.launch { sendMessage(chain) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步发送一个消息. 仅应在 Java 使用
|
* 异步发送一个消息. 仅应在 Java 使用
|
||||||
*/
|
*/
|
||||||
fun asyncSendMessage(message: Message) {
|
fun asyncSendMessage(message: Message) = bot.network.NetworkScope.launch { sendMessage(message) }
|
||||||
bot.network.NetworkScope.launch { sendMessage(message) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 异步发送一个消息. 仅应在 Java 使用
|
* 异步发送一个消息. 仅应在 Java 使用
|
||||||
*/
|
*/
|
||||||
fun asyncSendMessage(plain: String) {
|
fun asyncSendMessage(plain: String) = bot.network.NetworkScope.launch { sendMessage(plain) }
|
||||||
bot.network.NetworkScope.launch { sendMessage(plain) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
package net.mamoe.mirai.message
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CompletableJob
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.network.BotSession
|
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ClientTryGetImageIDPacketJvm
|
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDFailedPacket
|
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDResponsePacket
|
|
||||||
import net.mamoe.mirai.network.protocol.tim.packet.ServerTryGetImageIDSuccessPacket
|
|
||||||
import net.mamoe.mirai.qqAccount
|
|
||||||
import net.mamoe.mirai.utils.ImageNetworkUtils
|
|
||||||
import net.mamoe.mirai.utils.md5
|
|
||||||
import net.mamoe.mirai.utils.toByteArray
|
|
||||||
import net.mamoe.mirai.utils.toUHexString
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.File
|
|
||||||
import java.net.URL
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 不确定是否存在于服务器的 [Image].
|
|
||||||
* 必须在发送之前 [UnsolvedImage.upload] 或 [Contact.uploadImage], 否则会发送失败.
|
|
||||||
*
|
|
||||||
* @suppress todo 重新设计
|
|
||||||
* @author Him188moe
|
|
||||||
*/
|
|
||||||
class UnsolvedImage(private val filename: String, val image: BufferedImage) {
|
|
||||||
constructor(imageFile: File) : this(imageFile.name, ImageIO.read(imageFile))
|
|
||||||
constructor(url: URL) : this(File(url.file))
|
|
||||||
|
|
||||||
suspend fun upload(session: BotSession, contact: Contact): CompletableJob {
|
|
||||||
return session.expectPacket<ServerTryGetImageIDResponsePacket> {
|
|
||||||
toSend { ClientTryGetImageIDPacketJvm(session.bot.qqAccount, session.sessionKey, contact.number, image) }
|
|
||||||
|
|
||||||
onExpect {
|
|
||||||
when (it) {
|
|
||||||
is ServerTryGetImageIDFailedPacket -> {
|
|
||||||
//已经存在于服务器了
|
|
||||||
}
|
|
||||||
|
|
||||||
is ServerTryGetImageIDSuccessPacket -> {
|
|
||||||
val data = image.toByteArray()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
if (!ImageNetworkUtils.postImage(it.uKey.toUHexString(), data.size, session.bot.qqAccount, contact.number, data)) {
|
|
||||||
throw RuntimeException("cannot upload image")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toImage(): Image {
|
|
||||||
return Image(getImageId(filename))
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun getImageId(filename: String): String {
|
|
||||||
val md5 = md5(filename)
|
|
||||||
|
|
||||||
return "{" + md5.copyOfRange(0, 4).toUHexString("") + "-"
|
|
||||||
.plus(md5.copyOfRange(4, 6).toUHexString("")) + "-"
|
|
||||||
.plus(md5.copyOfRange(6, 8).toUHexString("")) + "-"
|
|
||||||
.plus(md5.copyOfRange(8, 10).toUHexString("")) + "-"
|
|
||||||
.plus(md5.copyOfRange(10, 16).toUHexString("")) + "}." + if (filename.endsWith(".jpeg")) "jpg" else filename.substringAfter(".", "jpg")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ package net.mamoe.mirai.network.protocol.tim.packet
|
|||||||
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.io.core.IoBuffer
|
import kotlinx.io.core.IoBuffer
|
||||||
import kotlinx.io.core.internal.DangerousInternalIoApi
|
|
||||||
import net.mamoe.mirai.utils.toUHexString
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
@ -37,7 +36,8 @@ private object IgnoreIdList : List<String> by listOf(
|
|||||||
"EMPTY_ID_HEX",
|
"EMPTY_ID_HEX",
|
||||||
"input",
|
"input",
|
||||||
"output",
|
"output",
|
||||||
"UninitializedByteReadPacket"
|
"UninitializedByteReadPacket",
|
||||||
|
"sessionKey"
|
||||||
)
|
)
|
||||||
|
|
||||||
internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields
|
internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields
|
||||||
|
@ -5,9 +5,6 @@ package net.mamoe.mirai.network.protocol.tim.packet
|
|||||||
import kotlinx.io.core.BytePacketBuilder
|
import kotlinx.io.core.BytePacketBuilder
|
||||||
import kotlinx.io.core.writeFully
|
import kotlinx.io.core.writeFully
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
|
|
||||||
actual typealias PlatformImage = BufferedImage
|
|
||||||
|
|
||||||
actual typealias ClientTryGetImageIDPacket = ClientTryGetImageIDPacketJvm
|
actual typealias ClientTryGetImageIDPacket = ClientTryGetImageIDPacketJvm
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package net.mamoe.mirai.utils
|
|
||||||
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.IOException
|
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import java.net.URL
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author NaturalHG
|
|
||||||
*/
|
|
||||||
object ImageNetworkUtils {
|
|
||||||
@Throws(IOException::class)
|
|
||||||
fun postImage(uKeyHex: String, fileSize: Int, botNumber: Long, groupCode: Long, img: ByteArray): Boolean {
|
|
||||||
//http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=” + 删全部空 (ukey) + “&filesize=” + 到文本 (fileSize) + “&range=0&uin=” + g_uin + “&groupcode=” + Group
|
|
||||||
|
|
||||||
val builder = "http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
|
|
||||||
"&ukey=" + uKeyHex.replace(" ", "") +
|
|
||||||
"&filezise=" + fileSize +
|
|
||||||
"&range=" + "0" +
|
|
||||||
"&uin=" + botNumber +
|
|
||||||
"&groupcode=" + groupCode
|
|
||||||
val conn = URL(builder).openConnection() as HttpURLConnection
|
|
||||||
conn.setRequestProperty("User-Agent", "QQClient")
|
|
||||||
conn.setRequestProperty("Content-Length", "" + fileSize)
|
|
||||||
conn.requestMethod = "POST"
|
|
||||||
conn.doOutput = true
|
|
||||||
conn.outputStream.write(img)
|
|
||||||
|
|
||||||
conn.connect()
|
|
||||||
return conn.responseCode == 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun BufferedImage.toByteArray(): ByteArray = ByteArrayOutputStream().use { ImageIO.write(this, "JPG", it); it.toByteArray() }
|
|
@ -1,18 +0,0 @@
|
|||||||
package net.mamoe.mirai.utils
|
|
||||||
|
|
||||||
import java.net.InetAddress
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.util.zip.CRC32
|
|
||||||
|
|
||||||
actual val currentTime: Long = System.currentTimeMillis()
|
|
||||||
|
|
||||||
actual val deviceName: String = InetAddress.getLocalHost().hostName
|
|
||||||
|
|
||||||
|
|
||||||
actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
|
|
||||||
|
|
||||||
actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
|
|
||||||
|
|
||||||
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
|
|
||||||
|
|
||||||
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
|
|
@ -0,0 +1,10 @@
|
|||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import javax.imageio.ImageIO
|
||||||
|
|
||||||
|
actual typealias PlatformImage = BufferedImage
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
|
actual fun BufferedImage.toByteArray(formatName: String): ByteArray = ByteArrayOutputStream().use { ImageIO.write(this, "JPG", it); it.toByteArray() }
|
@ -14,6 +14,7 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv
|
|||||||
private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt())
|
private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt())
|
||||||
private val channel: DatagramChannel = DatagramChannel.open().connect(serverAddress)
|
private val channel: DatagramChannel = DatagramChannel.open().connect(serverAddress)
|
||||||
|
|
||||||
|
@Throws(ReadPacketInternalException::class)
|
||||||
actual suspend fun read(buffer: IoBuffer) = withContext(Dispatchers.IO) {
|
actual suspend fun read(buffer: IoBuffer) = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
(channel as ReadableByteChannel).read(buffer)
|
(channel as ReadableByteChannel).read(buffer)
|
||||||
@ -22,6 +23,7 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(SendPacketInternalException::class)
|
||||||
actual suspend fun send(buffer: IoBuffer) = withContext(Dispatchers.IO) {
|
actual suspend fun send(buffer: IoBuffer) = withContext(Dispatchers.IO) {
|
||||||
buffer.readDirect {
|
buffer.readDirect {
|
||||||
try {
|
try {
|
@ -0,0 +1,56 @@
|
|||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.jsoup.Connection
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.util.zip.CRC32
|
||||||
|
|
||||||
|
actual val currentTime: Long = System.currentTimeMillis()
|
||||||
|
|
||||||
|
actual val deviceName: String = InetAddress.getLocalHost().hostName
|
||||||
|
|
||||||
|
|
||||||
|
actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
|
||||||
|
|
||||||
|
actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
|
||||||
|
|
||||||
|
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
|
||||||
|
|
||||||
|
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
|
||||||
|
|
||||||
|
actual suspend fun httpPostGroupImage(
|
||||||
|
uKeyHex: String,
|
||||||
|
fileSize: Int,
|
||||||
|
botNumber: Long,
|
||||||
|
groupCode: Long,
|
||||||
|
imageData: ByteArray
|
||||||
|
): Boolean = Jsoup
|
||||||
|
.connect("http://htdata2.qq.com/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc" +
|
||||||
|
"&ukey=" + uKeyHex.replace(" ", "") +
|
||||||
|
"&filezise=" + fileSize +
|
||||||
|
"&range=" + "0" +
|
||||||
|
"&uin=" + botNumber +
|
||||||
|
"&groupcode=" + groupCode)
|
||||||
|
.userAgent("QQClient")
|
||||||
|
.header("Content-Length", fileSize.toString())
|
||||||
|
.requestBody(String(imageData))
|
||||||
|
.method(Connection.Method.POST)
|
||||||
|
.ignoreContentType(true)
|
||||||
|
.let {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
it.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
val conn = URL(builder).openConnection() as HttpURLConnection
|
||||||
|
conn.setRequestProperty("User-Agent", "QQClient")
|
||||||
|
conn.setRequestProperty("Content-Length", "" + fileSize)
|
||||||
|
conn.requestMethod = "POST"
|
||||||
|
conn.doOutput = true
|
||||||
|
conn.outputStream.write(img)
|
||||||
|
|
||||||
|
conn.connect()*/
|
||||||
|
.statusCode() == 200
|
@ -76,7 +76,6 @@ object Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可从 TIM 内存中读取
|
* 可从 TIM 内存中读取
|
||||||
*
|
*
|
||||||
@ -89,7 +88,7 @@ object Main {
|
|||||||
* 6. 运行到 `mov eax,dword ptr ss:[ebp+10]`
|
* 6. 运行到 `mov eax,dword ptr ss:[ebp+10]`
|
||||||
* 7. 查看内存, 从 `eax` 开始的 16 bytes 便是 `sessionKey`
|
* 7. 查看内存, 从 `eax` 开始的 16 bytes 便是 `sessionKey`
|
||||||
*/
|
*/
|
||||||
val sessionKey: ByteArray = "9E A6 16 46 FF 15 FB 73 2F 31 0D 7E CB C4 E6 49".hexToBytes()
|
val sessionKey: ByteArray = "F1 68 24 ED A8 6D 33 6E 5C B7 E0 F4 45 77 21 04".hexToBytes()
|
||||||
|
|
||||||
fun dataReceived(data: ByteArray) {
|
fun dataReceived(data: ByteArray) {
|
||||||
println("--------------")
|
println("--------------")
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package demo1
|
package demo1
|
||||||
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.contact.Group
|
import net.mamoe.mirai.contact.Group
|
||||||
import net.mamoe.mirai.event.events.FriendMessageEvent
|
import net.mamoe.mirai.event.events.FriendMessageEvent
|
||||||
@ -17,14 +16,11 @@ import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
|||||||
import net.mamoe.mirai.utils.BotAccount
|
import net.mamoe.mirai.utils.BotAccount
|
||||||
import net.mamoe.mirai.utils.Console
|
import net.mamoe.mirai.utils.Console
|
||||||
import net.mamoe.mirai.utils.MiraiLogger
|
import net.mamoe.mirai.utils.MiraiLogger
|
||||||
import kotlin.coroutines.coroutineContext
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
suspend fun main() {
|
suspend fun main() {
|
||||||
val bot = Bot(BotAccount(//填写你的账号
|
val bot = Bot(BotAccount(//填写你的账号
|
||||||
account = 2903772581,
|
account = 1994701021,
|
||||||
password = "zxc123456"
|
password = "asdhim188666"
|
||||||
), Console())
|
), Console())
|
||||||
|
|
||||||
bot.login {
|
bot.login {
|
||||||
@ -79,7 +75,7 @@ suspend fun main() {
|
|||||||
subscribeAll<FriendMessageEvent> {
|
subscribeAll<FriendMessageEvent> {
|
||||||
always {
|
always {
|
||||||
//获取第一个纯文本消息
|
//获取第一个纯文本消息
|
||||||
val firstText = it.message.first<PlainText>()
|
val firstText = it.message.firstOrNull<PlainText>()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user