diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt index a07661d73..e28a05cc1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt @@ -24,15 +24,17 @@ internal fun IoBuffer.parsePlainText(): PlainText { internal fun IoBuffer.parseMessageImage0x06(): Image { discardExact(1) - this.debugPrint("好友的图片") - //MiraiLogger.logDebug(this.toUHexString()) - val filenameLength = readShort() - val suffix = readString(filenameLength).substringAfter(".") - discardExact(this@parseMessageImage0x06.readRemaining - 37 - 1 - filenameLength - 2) - val imageId = readString(36) - MiraiLogger.logDebug(imageId) - discardExact(1)//0x41 - return Image("{$imageId}.$suffix") + with(this.debugPrint("好友的图片")) { + + //MiraiLogger.logDebug(this.toUHexString()) + val filenameLength = readShort() + val suffix = readString(filenameLength).substringAfter(".") + discardExact(this.readRemaining - 37 - 1 - filenameLength - 2 - 8 - 4) + val imageId = readString(36) + MiraiLogger.logDebug("imageId=$imageId")//todo ID似乎错了?? + discardExact(1)//0x41 + return Image("{$imageId}.$suffix") + } } internal fun IoBuffer.parseMessageImage0x03(): Image { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt index 3cc396be4..0662bf4cd 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotNetworkHandler.kt @@ -1,6 +1,8 @@ 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 net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler.BotSocketAdapter 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. */ +@Suppress("PropertyName") interface BotNetworkHandler : Closeable { /** * [BotNetworkHandler] 的协程作用域. @@ -38,11 +41,11 @@ interface BotNetworkHandler : Closeable { * - SKey 刷新 [ClientSKeyRefreshmentRequestPacket] * - 所有数据包处理和发送 * - * [BotNetworkHandler.close] 时将会 [取消][CoroutineScope.cancel] 所有此作用域下的协程 + * [BotNetworkHandler.close] 时将会 [取消][kotlin.coroutines.CoroutineContext.cancelChildren] 所有此作用域下的协程 */ val NetworkScope: CoroutineScope - var socket: Socket + val socket: Socket /** * 得到 [PacketHandler]. @@ -57,9 +60,10 @@ interface BotNetworkHandler : Closeable { suspend fun login(configuration: LoginConfiguration): LoginResult /** - * 添加一个临时包处理器 + * 添加一个临时包处理器, 并发送相应的包 * - * @see [TemporaryPacketHandler] + * @see [BotSession.sendAndExpect] 发送并期待一个包 + * @see [TemporaryPacketHandler] 临时包处理器 */ suspend fun addHandler(temporaryPacketHandler: TemporaryPacketHandler<*>) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt index eb24cc58a..18918cf44 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt @@ -1,3 +1,5 @@ +@file:Suppress("MemberVisibilityCanBePrivate", "unused") + package net.mamoe.mirai.network import kotlinx.coroutines.CompletableJob @@ -50,7 +52,7 @@ class BotSession( * * 实现方法: * ```kotlin - * session.expectPacket { + * session.sendAndExpect { * toSend { ClientPacketXXX(...) } * onExpect {//it: ServerPacketXXX * @@ -62,7 +64,7 @@ class BotSession( * @param handlerTemporary 处理器. */ //@JvmSynthetic - suspend inline fun expectPacket(handlerTemporary: TemporaryPacketHandler

.() -> Unit): CompletableJob { + suspend inline fun sendAndExpect(handlerTemporary: TemporaryPacketHandler

.() -> Unit): CompletableJob { val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job() this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also(handlerTemporary)) return job @@ -71,28 +73,28 @@ class BotSession( /** * 发送一个数据包, 并期待接受一个特定的 [ServerPacket]. * 发送成功后, 该方法会等待收到 [ServerPacket] 直到超时. - * 由于包名可能过长, 可使用 `DataPacketSocketAdapter.expectPacket(PacketProcessor)` 替代. + * 由于包名可能过长, 可使用 `DataPacketSocketAdapter.sendAndExpect(PacketProcessor)` 替代. * * 实现方法: * ```kotlin - * session.expectPacket(ClientPacketXXX(...)) {//it: ServerPacketXXX - * + * ClientPacketXXX(...).sendAndExpect { + * //it: ServerPacketXXX * } * ``` * * @param P 期待的包 - * @param toSend 将要发送的包 * @param handler 处理期待的包 */ - //@JvmSynthetic - suspend inline fun expectPacket(toSend: ClientPacket, noinline handler: suspend (P) -> Unit): CompletableJob { + suspend inline fun ClientPacket.sendAndExpect(noinline handler: suspend (P) -> Unit): CompletableJob { val job = coroutineContext[Job].takeIf { it != null }?.let { Job(it) } ?: Job() - this.bot.network.addHandler(TemporaryPacketHandler(P::class, job, this).also { - it.toSend(toSend) + bot.network.addHandler(TemporaryPacketHandler(P::class, job, this@BotSession).also { + it.toSend(this) it.onExpect(handler) }) return job } + + suspend inline fun ClientPacket.send() = socket.sendPacket(this) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt index f9d4046b4..da6ce9a47 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt @@ -30,6 +30,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : override val NetworkScope: CoroutineScope = CoroutineScope(Dispatchers.Default) override lateinit var socket: BotSocketAdapter + private set internal val temporaryPacketHandlers = mutableListOf>() private val handlersLock = Mutex() @@ -73,6 +74,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : add(EventPacketHandler(session).asNode(EventPacketHandler)) add(ActionPacketHandler(session).asNode(ActionPacketHandler)) + bot.logger.logPurple("Successfully logged in") } private lateinit var sessionKey: ByteArray @@ -110,9 +112,10 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : try { channel.read(buffer)//JVM: withContext(IO) } catch (e: ReadPacketInternalException) { - - } catch (e: Exception) { - e.log() + //read failed, continue and reread + continue + } catch (e: Throwable) { + e.log()//other unexpected exceptions caught. continue } @@ -123,11 +126,11 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : NetworkScope.launch { 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 { distributePacket(it.parseServerPacket(buffer.readRemaining)) } - } catch (e: Exception) { + } catch (e: Throwable) { e.log() } } @@ -168,6 +171,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : try { packet.decode() } catch (e: Exception) { + e.log() bot.printPacketDebugging(packet) packet.close() throw e @@ -238,9 +242,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}") bot.reinitializeNetworkHandler(configuration) return@withContext - } catch (e: Throwable) { - e.log() - return@withContext } finally { buffer.release(IoBuffer.Pool) } @@ -338,7 +339,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : is ServerLoginResponseCaptchaInitPacket -> { //[token00BA]来源之一: 验证码 this.token00BA = packet.token00BA - this.captchaCache = packet.verifyCodePart1 + this.captchaCache = packet.captchaPart1 if (packet.unknownBoolean == true) { this.captchaSectionId = 1 @@ -390,12 +391,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : privateKey = privateKey, token0825 = token0825, token00BA = packet.tokenUnknown ?: token00BA, - randomDeviceName = socket.configuration.randomDeviceName + randomDeviceName = socket.configuration.randomDeviceName, + tlv0006 = packet.tlv0006 )) } is ServerSessionKeyResponsePacket -> { sessionKey = packet.sessionKey + bot.logger.logPurple("sessionKey = ${sessionKey.toUHexString()}") heartbeatJob = NetworkScope.launch { while (socket.isOpen) { @@ -412,14 +415,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) : is ServerLoginSuccessPacket -> { BotLoginSucceedEvent(bot).broadcast() - //登录成功后会收到大量上次的消息, 忽略掉 todo 优化 - NetworkScope.launch { - delay(3000) - this@TIMBotNetworkHandler[EventPacketHandler].ignoreMessage = false - } - onLoggedIn(sessionKey) - this.close()//The LoginHandler is useless since then } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt index d9cb0c4bc..7ad7224c0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/ActionPacketHandler.kt @@ -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.ClientSKeyRequestPacket 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 { while (session.isOpen) { 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() + } } } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/DataPacketSocketAdapter.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/DataPacketSocketAdapter.kt index da33232f2..c8e6b6374 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/DataPacketSocketAdapter.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/DataPacketSocketAdapter.kt @@ -41,7 +41,7 @@ interface DataPacketSocketAdapter : Closeable { * * 可通过 hook 事件 [ServerPacketReceivedEvent] 来获取服务器返回. * - * @see [BotSession.expectPacket] kotlin DSL + * @see [BotSession.sendAndExpect] kotlin DSL */ suspend fun sendPacket(packet: ClientPacket) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt index 7bad8a865..911708110 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/EventPacketHandler.kt @@ -26,9 +26,6 @@ import net.mamoe.mirai.utils.MiraiLogger class EventPacketHandler(session: BotSession) : PacketHandler(session) { companion object Key : PacketHandler.Key - - internal var ignoreMessage: Boolean = true - override suspend fun onPacketReceived(packet: ServerPacket): Unit = with(session) { when (packet) { is ServerGroupUploadFileEventPacket -> { @@ -36,14 +33,12 @@ class EventPacketHandler(session: BotSession) : PacketHandler(session) { } is ServerFriendMessageEventPacket -> { - if (ignoreMessage) return - - FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast() + if (!packet.isPrevious) { + FriendMessageEvent(bot, bot.getQQ(packet.qq), packet.message).broadcast() + } } is ServerGroupMessageEventPacket -> { - if (ignoreMessage) return - if (packet.qq.toLong() == bot.account.account) return GroupMessageEvent(bot, bot.getGroupByNumber(packet.groupNumber), bot.getQQ(packet.qq), packet.message).broadcast() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt index 246fcc70a..9f34aac87 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt @@ -17,11 +17,11 @@ import kotlin.reflect.KClass * } * ``` * - * @see BotSession.expectPacket + * @see BotSession.sendAndExpect */ class TemporaryPacketHandler

( private val expectationClass: KClass

, - private val deferred: CompletableJob, + private val job: CompletableJob, private val fromSession: BotSession ) { private lateinit var toSend: ClientPacket @@ -54,7 +54,7 @@ class TemporaryPacketHandler

( kotlin.runCatching { @Suppress("UNCHECKED_CAST") expect(packet as P) - }.onFailure { deferred.completeExceptionally(it) }.onSuccess { deferred.complete() } + }.onFailure { job.completeExceptionally(it) }.onSuccess { job.complete() } return true } return false diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt index 72e3489b5..4127159e0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Heartbeat.kt @@ -5,7 +5,9 @@ package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.ByteReadPacket 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) class ClientHeartbeatPacket( @@ -13,9 +15,9 @@ class ClientHeartbeatPacket( private val sessionKey: ByteArray ) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(bot) - this.writeHex(TIMProtocol.fixVer) - this.encryptAndWrite(sessionKey) { + writeQQ(bot) + writeHex(TIMProtocol.fixVer) + encryptAndWrite(sessionKey) { writeHex("00 01 00 01") } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt index 238bc20b6..824753666 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/Packet.kt @@ -2,17 +2,16 @@ package net.mamoe.mirai.network.protocol.tim.packet -import kotlinx.io.core.Closeable import net.mamoe.mirai.utils.toUHexString /** * 数据包. */ -abstract class Packet : Closeable { +abstract class Packet { /** * 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. 唯一 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt index 3b3162725..44d9e566c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerEvent.kt @@ -26,13 +26,20 @@ fun BytePacketBuilder.writeEventPacketIdentity(identity: EventPacketIdentity) = writeFully(uniqueId) } +fun S.applyId(id: UShort): S { + this.id = id + return this +} + /** * Packet id: `00 CE` or `00 17` * * @author Him188moe */ 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) { val eventIdentity = EventPacketIdentity( from = readUInt(), @@ -65,7 +72,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event println(readUByte().toUInt()) //todo 错了. 可能是 00 79 才是. - return@with ServerFriendTypingCanceledPacket(input, eventIdentity) + ServerFriendTypingCanceledPacket(input, eventIdentity) /* if (readUByte().toUInt() == 0x37u) ServerFriendTypingStartedPacket(input, eventIdentity) else /*0x22*/ ServerFriendTypingCanceledPacket(input, eventIdentity)*/ @@ -81,8 +88,8 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event }.applyId(id).applySequence(sequenceId) } - class Encrypted(input: ByteReadPacket) : ServerPacket(input) { - fun decrypt(sessionKey: ByteArray): Raw = Raw(this.decryptBy(sessionKey)).applyId(id).applySequence(sequenceId) + class Encrypted(input: ByteReadPacket, override var id: UShort, override var sequenceId: UShort) : ServerPacket(input) { + 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 { val tlv = readTLVMap(true) 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 { tlv.containsKey(0x01) -> String(tlv.getValue(0x01)) 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 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() { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt index 95d6c179b..291d0a040 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ServerPacket.kt @@ -16,7 +16,8 @@ import kotlin.properties.Delegates * @see parseServerPacket */ 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() open fun decode() { @@ -28,11 +29,6 @@ abstract class ServerPacket(val input: ByteReadPacket) : Packet(), Closeable { override fun toString(): String = this.packetToString() } -fun S.applyId(id: UShort): S { - this.id = id - return this -} - fun S.applySequence(sequenceId: UShort): S { this.sequenceId = sequenceId return this diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt index da722af78..b56a5306c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadImage.kt @@ -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 import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.readBytes -import net.mamoe.mirai.utils.gotoWhere -import net.mamoe.mirai.utils.toReadPacket - -expect class PlatformImage +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.network.BotSession +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( botNumber: Long, sessionKey: ByteArray, @@ -16,19 +20,18 @@ expect class ClientTryGetImageIDPacket( image: PlatformImage ) : ClientPacket - -abstract class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : ServerPacket(input) { - +@PacketId(0x03_88u) +sealed class ServerTryGetImageIDResponsePacket(input: ByteReadPacket) : ServerPacket(input) { + @PacketId(0x03_88u) class Encrypted(input: ByteReadPacket) : ServerPacket(input) { fun decrypt(sessionKey: ByteArray): ServerTryGetImageIDResponsePacket { val data = this.decryptAsByteArray(sessionKey) - println(data.size) - println(data.size) + println("ServerTryGetImageIDResponsePacket.size=" + data.size) if (data.size == 209) { 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 用于客户端上传 */ +@PacketId(0x03_88u) class ServerTryGetImageIDSuccessPacket(input: ByteReadPacket) : ServerTryGetImageIDResponsePacket(input) { lateinit var uKey: ByteArray override fun decode() { - this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u)) + this.input.gotoWhere(ubyteArrayOf(0x42u, 0x80u, 0x01u))//todo 优化 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) { - 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 { + 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() } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt index baa3ff463..62b6f52ca 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ClientChangeOnlineStatusPacket.kt @@ -7,19 +7,22 @@ import kotlinx.io.core.writeUByte 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.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) class ClientChangeOnlineStatusPacket( - private val qq: Long, + private val bot: Long, private val sessionKey: ByteArray, private val loginStatus: OnlineStatus ) : ClientPacket() { override fun encode(builder: BytePacketBuilder) = with(builder) { - this.writeQQ(qq) + this.writeQQ(bot) this.writeHex(TIMProtocol.fixVer2) this.encryptAndWrite(sessionKey) { writeHex("01 00") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt index 595cfb793..3505b4578 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/SKey.kt @@ -45,8 +45,10 @@ class ServerSKeyResponsePacket(input: ByteReadPacket) : ServerPacket(input) { override fun decode() = with(input) { discardExact(4) - sKey = this.readString(10)//todo test - MiraiLogger.logDebug("SKey=$sKey") + //debugDiscardExact(2) + sKey = this.readString(10) + DebugLogger.logPurple("SKey=$sKey") + DebugLogger.logPurple("Skey包后面${this.readRemainingBytes().toUHexString()}") } @PacketId(0x00_1Du) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt index bc4403efa..0c00c9e9c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/ServerLoginResponse.kt @@ -16,8 +16,6 @@ class ServerLoginResponseFailedPacket(val loginResult: LoginResult, input: ByteR /** * 服务器进行加密后返回 privateKey - * - * @author NaturalHG */ @PacketId(0x08_36u) class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { @@ -29,7 +27,7 @@ class ServerLoginResponseKeyExchangePacket(input: ByteReadPacket) : ServerLoginR @Tested override fun decode() { 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 tlv0006 = this.input.readIoBuffer(0x78) @@ -127,7 +125,7 @@ class ServerLoginResponseSuccessPacket(input: ByteReadPacket) : ServerLoginRespo @PacketId(0x08_36u) class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginResponsePacket(input) { - lateinit var verifyCodePart1: IoBuffer + lateinit var captchaPart1: IoBuffer lateinit var token00BA: ByteArray var unknownBoolean: Boolean? = null @@ -136,8 +134,8 @@ class ServerLoginResponseCaptchaInitPacket(input: ByteReadPacket) : ServerLoginR override fun decode() { this.input.discardExact(78) //println(this.input.readRemainingBytes().toUHexString()) - val verifyCodeLength = this.input.readShort()//2bytes - this.verifyCodePart1 = this.input.readIoBuffer(verifyCodeLength) + val captchaLength = this.input.readShort()//2bytes + this.captchaPart1 = this.input.readIoBuffer(captchaLength) this.input.discardExact(1) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt index 56e3dfe70..107b0e2e9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/login/Session.kt @@ -9,14 +9,14 @@ import net.mamoe.mirai.utils.* @PacketId(0x08_28u) class ClientSessionRequestPacket( - private val qq: Long, + private val bot: Long, private val serverIp: String, private val token38: IoBuffer, private val token88: IoBuffer, private val encryptionKey: IoBuffer ) : ClientPacket() { 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("00 38") 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(TIMProtocol.constantData1) writeHex(TIMProtocol.constantData2) - writeQQ(qq) + writeQQ(bot) 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 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt index 4304f4f0b..d60ddc52e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ByteReadPacketUtil.kt @@ -69,12 +69,12 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { } 0x08_28u -> ServerSessionKeyResponsePacket.Encrypted(this) - 0x00_EC_u -> ServerSKeyResponsePacket(this) + 0x00_EC_u -> ServerLoginSuccessPacket(this) 0x00_1D_u -> ServerSKeyResponsePacket.Encrypted(this) 0x00_5C_u -> ServerAccountInfoResponsePacket.Encrypted(this) 0x00_58_u -> ServerHeartbeatResponsePacket(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_CD_u -> ServerSendFriendMessageResponsePacket(this) 0x00_02_u -> ServerSendGroupMessageResponsePacket(this) @@ -82,7 +82,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket { 0x03_88_u -> ServerTryGetImageIDResponsePacket.Encrypted(this) else -> UnknownServerPacket.Encrypted(this, id, sequenceId) - }.applyId(id).applySequence(sequenceId) + }.applySequence(sequenceId) } fun Input.readIP(): String = buildString(4 + 3) { @@ -145,6 +145,7 @@ fun Input.readLVNumber(): Number { //@JvmSynthetic @Deprecated("Low efficiency", ReplaceWith("")) fun I.gotoWhere(matcher: UByteArray): I { + @Suppress("DEPRECATION") return this.gotoWhere(matcher.toByteArray()) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt index f062582a2..25cce1f7d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/DebugUtil.kt @@ -15,19 +15,19 @@ internal fun ByteArray.debugPrint(name: String): ByteArray { return this } -@Deprecated("Low Efficiency", ReplaceWith("")) +@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("")) internal fun IoBuffer.debugPrint(name: String): IoBuffer { val readBytes = this.readBytes() DebugLogger.logPurple(name + "=" + readBytes.toUHexString()) 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 = "") { 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 { val bytes = this.readBytes() DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfigure.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfiguration.kt similarity index 100% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfigure.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginConfiguration.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformImage.kt new file mode 100644 index 000000000..ba4bd3707 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformImage.kt @@ -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 \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SocketBridge.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformSocket.kt similarity index 100% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SocketBridge.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformSocket.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt similarity index 63% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt index c472048e3..f8a558c4d 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt @@ -31,4 +31,15 @@ expect fun solveIpAddress(hostname: String): String /** * Localhost 解析 */ -expect fun localIpAddress(): String \ No newline at end of file +expect fun localIpAddress(): String + +/** + * 上传群图片 + */ +expect suspend fun httpPostGroupImage( + uKeyHex: String, + fileSize: Int, + botNumber: Long, + groupCode: Long, + imageData: ByteArray +): Boolean \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/VarDelegates.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/VarDelegates.kt new file mode 100644 index 000000000..1107dc4d4 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/VarDelegates.kt @@ -0,0 +1,22 @@ +package net.mamoe.mirai.utils + +import kotlin.properties.Delegates +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + + +fun Delegates.notNullBy(initializer: () -> T): ReadWriteProperty = NotNullVarWithDefault(lazy(initializer = initializer)) + +class NotNullVarWithDefault( + private val initializer: Lazy +) : ReadWriteProperty { + 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 + } +} diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt index 2c8a09c69..6361f740d 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJvm.kt @@ -35,7 +35,6 @@ actual sealed class Contact actual constructor(bot: Bot, number: Long) : Platfor */ fun blockingSendMessage(message: Message) = runBlocking { sendMessage(message) } - /** * 阻塞发送一个消息. 仅应在 Java 使用 */ @@ -44,23 +43,17 @@ actual sealed class Contact actual constructor(bot: Bot, number: Long) : Platfor /** * 异步发送一个消息. 仅应在 Java 使用 */ - fun asyncSendMessage(chain: MessageChain) { - bot.network.NetworkScope.launch { sendMessage(chain) } - } + fun asyncSendMessage(chain: MessageChain) = bot.network.NetworkScope.launch { sendMessage(chain) } /** * 异步发送一个消息. 仅应在 Java 使用 */ - fun asyncSendMessage(message: Message) { - bot.network.NetworkScope.launch { sendMessage(message) } - } + fun asyncSendMessage(message: Message) = bot.network.NetworkScope.launch { sendMessage(message) } /** * 异步发送一个消息. 仅应在 Java 使用 */ - fun asyncSendMessage(plain: String) { - bot.network.NetworkScope.launch { sendMessage(plain) } - } + fun asyncSendMessage(plain: String) = bot.network.NetworkScope.launch { sendMessage(plain) } } /** diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt deleted file mode 100644 index 15f5fef3c..000000000 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/UnsolvedImage.kt +++ /dev/null @@ -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 { - 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") - } - } -} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt index 01b5fe094..81c7f8c62 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/PacketInternalJvm.kt @@ -4,7 +4,6 @@ package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.IoBuffer -import kotlinx.io.core.internal.DangerousInternalIoApi import net.mamoe.mirai.utils.toUHexString import java.lang.reflect.Field @@ -37,7 +36,8 @@ private object IgnoreIdList : List by listOf( "EMPTY_ID_HEX", "input", "output", - "UninitializedByteReadPacket" + "UninitializedByteReadPacket", + "sessionKey" ) internal actual fun Packet.packetToString(): String = PacketNameFormatter.adjustName(this::class.simpleName + "(${this.idHexString})") + this::class.java.allDeclaredFields diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt index ba209e557..b7a4b3642 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/UploadImageJvm.kt @@ -5,9 +5,6 @@ package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.writeFully import net.mamoe.mirai.utils.* -import java.awt.image.BufferedImage - -actual typealias PlatformImage = BufferedImage actual typealias ClientTryGetImageIDPacket = ClientTryGetImageIDPacketJvm diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt deleted file mode 100644 index 9aafa5c71..000000000 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ImageNetworkUtils.kt +++ /dev/null @@ -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() } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt deleted file mode 100644 index 3846ebc7f..000000000 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformBridgeJvm.kt +++ /dev/null @@ -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 \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformImageJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformImageJvm.kt new file mode 100644 index 000000000..a007ed8d5 --- /dev/null +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformImageJvm.kt @@ -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() } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformSocketJvm.kt similarity index 94% rename from mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt rename to mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformSocketJvm.kt index 82b2874c1..d32760d8e 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/SocketBridgeJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformSocketJvm.kt @@ -14,6 +14,7 @@ actual class PlatformDatagramChannel actual constructor(serverHost: String, serv private val serverAddress: InetSocketAddress = InetSocketAddress(serverHost, serverPort.toInt()) private val channel: DatagramChannel = DatagramChannel.open().connect(serverAddress) + @Throws(ReadPacketInternalException::class) actual suspend fun read(buffer: IoBuffer) = withContext(Dispatchers.IO) { try { (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) { buffer.readDirect { try { diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt new file mode 100644 index 000000000..eaecd582f --- /dev/null +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformUtilsJvm.kt @@ -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 \ No newline at end of file diff --git a/mirai-debug/src/main/java/PacketDebuger.kt b/mirai-debug/src/main/java/PacketDebuger.kt index ad063f000..94bab2e6b 100644 --- a/mirai-debug/src/main/java/PacketDebuger.kt +++ b/mirai-debug/src/main/java/PacketDebuger.kt @@ -76,7 +76,6 @@ object Main { } } - /** * 可从 TIM 内存中读取 * @@ -89,7 +88,7 @@ object Main { * 6. 运行到 `mov eax,dword ptr ss:[ebp+10]` * 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) { println("--------------") diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt index 7d2a5bc5e..615f1928a 100644 --- a/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt +++ b/mirai-demos/mirai-demo-1/src/main/java/demo1/Main.kt @@ -1,7 +1,6 @@ package demo1 import kotlinx.coroutines.delay -import kotlinx.coroutines.withContext import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Group 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.Console import net.mamoe.mirai.utils.MiraiLogger -import kotlin.coroutines.coroutineContext -import kotlin.system.exitProcess -import kotlin.system.measureTimeMillis suspend fun main() { val bot = Bot(BotAccount(//填写你的账号 - account = 2903772581, - password = "zxc123456" + account = 1994701021, + password = "asdhim188666" ), Console()) bot.login { @@ -79,7 +75,7 @@ suspend fun main() { subscribeAll { always { //获取第一个纯文本消息 - val firstText = it.message.first

() + val firstText = it.message.firstOrNull<PlainText>() } }