From 14722eb8389370f07319c95750f791367cf7b379 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 20 Oct 2019 20:27:35 +0800 Subject: [PATCH] Image uploading --- .../protocol/tim/packet/ClientPacket.kt | 2 +- .../protocol/tim/packet/UploadGroupImage.kt | 75 ++++++++++++------- .../tim/packet/event/ServerEventPackets.kt | 3 +- .../net.mamoe.mirai/utils/PlatformImage.kt | 27 +------ .../net.mamoe.mirai/utils/io/DebugUtil.kt | 19 ++++- .../net.mamoe.mirai/utils/io/OutputUtils.kt | 5 ++ .../utils/io/TypeConvertion.kt | 4 +- .../net/mamoe/mirai/utils/HexComparator.kt | 4 +- .../net/mamoe/mirai/utils/PlatformImageJvm.kt | 2 +- .../src/jvmTest/kotlin/TestGroupImage.kt | 23 ++++++ mirai-debug/src/main/java/HexComparator.kt | 6 +- .../mirai-demo-1/src/main/java/demo1/Main.kt | 4 +- 12 files changed, 107 insertions(+), 67 deletions(-) create mode 100644 mirai-core/src/jvmTest/kotlin/TestGroupImage.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt index 6821e342d..0c0ab0bbe 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/ClientPacket.kt @@ -30,7 +30,7 @@ abstract class ClientPacket : Packet(), Closeable { /** * 务必 [ByteReadPacket.close] 或 [close] 或使用 [Closeable.use] */ - internal var packet: ByteReadPacket = UninitializedByteReadPacket + var packet: ByteReadPacket = UninitializedByteReadPacket get() { if (field === UninitializedByteReadPacket) build() return field diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt index 071f0302e..f6c9bd1e1 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/UploadGroupImage.kt @@ -4,11 +4,11 @@ package net.mamoe.mirai.network.protocol.tim.packet import kotlinx.io.core.* import net.mamoe.mirai.contact.Group -import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.session import net.mamoe.mirai.qqAccount import net.mamoe.mirai.utils.* + suspend fun Group.uploadImage( image: PlatformImage ) = with(bot.network.session) { @@ -110,54 +110,79 @@ class GroupImageIdRequestPacket( // 78 03 // 80 01 00 + + /* writeQQ(bot) writeHex(TIMProtocol.version0x04) encryptAndWrite(sessionKey) { writeHex("00 00 00 07 00 00 00") - writeUVarintLVPacket(lengthOffset = { it - 6 }) { + writeUVarintLVPacket { + writeByte(0x08) writeHex("01 12 03 98 01 01 10 01 1A") - writeUVarintLVPacket(lengthOffset = { it + 1 }) { - writeUVarInt(groupId) - writeUVarInt(bot) - + writeUVarintLVPacket(lengthOffset = { it + 7 }) { + writeTUVarint(0x08u, groupId) + writeTUVarint(0x10u, bot) writeTV(0x1800u) writeTLV(0x22u, image.md5) writeTUVarint(0x28u, image.fileSize.toUInt()) writeUVarintLVPacket(tag = 0x32u) { - writeTV(0x31_00u) - writeTV(0x35_00u) - writeTV(0x4C_00u) + writeTV(0x5B_00u) + writeTV(0x40_00u) + writeTV(0x33_00u) + writeTV(0x48_00u) + writeTV(0x5F_00u) + writeTV(0x58_00u) + writeTV(0x46_00u) + writeTV(0x51_00u) + writeTV(0x45_00u) + writeTV(0x51_00u) + writeTV(0x40_00u) writeTV(0x24_00u) - writeTV(0x40_00u) - writeTV(0x5B_00u) - writeTV(0x4D_00u) - writeTV(0x5B_00u) - writeTV(0x39_00u) - writeTV(0x39_00u) - writeTV(0x40_00u) - writeTV(0x57_00u) - writeTV(0x5D_00u) + writeTV(0x4F_00u) } writeTV(0x38_01u) writeTV(0x48_01u) writeTUVarint(0x50u, image.imageWidth.toUInt()) writeTUVarint(0x58u, image.imageHeight.toUInt()) writeTV(0x60_02u) - writeTLV(0x6Au, value0x6A) - writeTV(0x70_00u) - writeTV(0x78_03u) - writeTV(0x80_01u) - writeUByte(0u) + writeTByteArray(0x6Au, value0x6A) } } + writeTV(0x70_00u) + writeTV(0x78_03u) + writeTV(0x80_01u) + writeUByte(0u) + + // this.debugColorizedPrintThis(compareTo = "00 00 00 07 00 00 00 5D 08 01 12 03 98 01 01 10 01 1A 59 08 FB D2 D8 94 02 10 A2 FF 8C F0 03 18 00 22 10 1D D2 2B 9B BC F2 10 83 DC 99 D2 2E 20 39 CC 0E 28 8A 03 32 1A 5B 00 40 00 33 00 48 00 5F 00 58 00 46 00 51 00 45 00 51 00 40 00 24 00 4F 00 38 01 48 01 50 EF 01 58 C2 01 60 02 6A 05 32 36 39 33 33 70 00 78 03 80 01 00") + }*/ + + writeQQ(bot) + writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00") + + encryptAndWrite(sessionKey) { + writeHex("00 00 00 07 00 00 00 5E 08 01 12 03 98 01 01 10 01 1A") + writeHex("5A 08") + writeUVarInt(groupId) + writeUByte(0x10u) + writeUVarInt(bot) + writeHex("18 00 22 10") + writeFully(image.md5) + writeUByte(0x28u) + writeUVarInt(image.fileSize.toUInt()) + writeHex("32 1A 37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00") + writeHex("38 01 48 01 50") + writeUVarInt(image.width.toUInt()) + writeUByte(0x58u) + writeUVarInt(image.height.toUInt()) + writeHex("60 04 6A 05 32 36 36 35 36 70 00 78 03 80 01 00") } } companion object { - private val value0x6A: UByteArray = ubyteArrayOf(32u, 36u, 39u, 33u, 33u) + private val value0x6A: UByteArray = ubyteArrayOf(0x05u, 0x32u, 0x36u, 0x39u, 0x33u, 0x33u) } @PacketId(0x0388u) @@ -189,7 +214,7 @@ class GroupImageIdRequestPacket( } fun main() { - ("12 03 98 01 01").hexToBytes().read { + ("8E 4B").hexToBytes().read { println(readUnsignedVarInt()) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt index a8cb34417..ffad8db3b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/ServerEventPackets.kt @@ -119,8 +119,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event } /** - * 忽略的事件. - * 如 00 79: 总是与 01 12 一起发生, 但 00 79 却没多大意义 + * 忽略的事件 */ @Suppress("unused") class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boolean = false, input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) { 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 index f2e46d898..ca755d5c3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformImage.kt @@ -3,7 +3,7 @@ package net.mamoe.mirai.utils import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.message.ImageId -data class PlatformImage( +class PlatformImage( val width: Int, val height: Int, val md5: ByteArray, @@ -14,30 +14,7 @@ data class PlatformImage( val id: ImageId by lazy { ImageId("{${md5[0..4]}-${md5[0..2]}-${md5[0..2]}-${md5[0..2]}-${md5[0..6]}}.$format") } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is PlatformImage) return false - - if (width != other.width) return false - if (height != other.height) return false - if (!md5.contentEquals(other.md5)) return false - if (format != other.format) return false - if (fileData != other.fileData) return false - if (fileSize != other.fileSize) return false - - return true - } - - override fun hashCode(): Int { - var result = width - result = 31 * result + height - result = 31 * result + md5.contentHashCode() - result = 31 * result + format.hashCode() - result = 31 * result + fileData.hashCode() - result = 31 * result + fileSize.hashCode() - return result - } + override fun toString(): String = "[PlatformImage(${width}x${height} $format)]" } private operator fun ByteArray.get(range: IntRange): String = buildString { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt index 402f13202..d866f923a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt @@ -45,6 +45,13 @@ internal fun BytePacketBuilder.debugColorizedPrintThis(name: String = "") { this.writeFully(data) } +@Deprecated("Low efficiency, only for debug purpose", ReplaceWith(" ")) +internal fun BytePacketBuilder.debugColorizedPrintThis(name: String = "", compareTo: String? = null) { + val data = this.build().readBytes() + data.printColorizedHex(name, compareTo = compareTo) + this.writeFully(data) +} + @Deprecated("Low efficiency, only for debug purpose", ReplaceWith(" ")) internal fun BytePacketBuilder.debugPrintThis(name: String = "") { val data = this.build().readBytes() @@ -57,14 +64,18 @@ internal fun String.printStringFromHex() { } -internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstConst: Boolean = false) { +internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstConst: Boolean = false, compareTo: String? = null) { println("Hex比较 `$name`") - println(toUHexString().colorize(ignoreUntilFirstConst)) + if (compareTo != null) { + println(printCompareHex(toUHexString(), compareTo)) + } else { + println(toUHexString().printColorize(ignoreUntilFirstConst)) + } println() } -expect fun compareHex(hex1s: String, hex2s: String): String -expect fun String.colorize(ignoreUntilFirstConst: Boolean = false): String +expect fun printCompareHex(hex1s: String, hex2s: String): String +expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String fun main() { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt index c6649e480..0426dce89 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt @@ -92,6 +92,11 @@ fun BytePacketBuilder.writeTByteArray(tag: UByte, value: ByteArray) { this.writeFully(value) } +fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) { + this.writeUByte(tag) + this.writeFully(value) +} + fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(key.readBytes(), encoder) fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) = writeFully(TEA.encrypt(BytePacketBuilder().apply(encoder).use { it.build().readBytes() }, key)) fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt index 5a885e5a1..5862b7621 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/TypeConvertion.kt @@ -74,8 +74,8 @@ internal object HexCache { } internal fun hexToUBytes(hex: String): UByteArray = - hex.split(" ").dropLastWhile { it.isEmpty() }.toTypedArray() - .map { value -> value.trim { it <= ' ' } } + hex.split(" ") + .filterNot { it.isEmpty() } .map { s -> s.toUByte(16) } .toUByteArray() } \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/HexComparator.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/HexComparator.kt index 3b539db7c..06482e76b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/HexComparator.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/HexComparator.kt @@ -286,5 +286,5 @@ internal object HexComparator { } -actual fun String.colorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) } -actual fun compareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) } \ No newline at end of file +actual fun String.printColorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) } +actual fun printCompareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) } \ 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 index 5cc573888..3e62bee6d 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformImageJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/PlatformImageJvm.kt @@ -8,7 +8,7 @@ import java.io.OutputStream import java.security.MessageDigest import javax.imageio.ImageIO -fun BufferedImage.toPlatformImage(formatName: String = "PNG"): PlatformImage { +fun BufferedImage.toPlatformImage(formatName: String = "png"): PlatformImage { val digest = MessageDigest.getInstance("md5") digest.reset() diff --git a/mirai-core/src/jvmTest/kotlin/TestGroupImage.kt b/mirai-core/src/jvmTest/kotlin/TestGroupImage.kt new file mode 100644 index 000000000..1c877f64e --- /dev/null +++ b/mirai-core/src/jvmTest/kotlin/TestGroupImage.kt @@ -0,0 +1,23 @@ +@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + +import net.mamoe.mirai.network.protocol.tim.packet.GroupImageIdRequestPacket +import net.mamoe.mirai.utils.hexToBytes +import net.mamoe.mirai.utils.readRemainingBytes +import net.mamoe.mirai.utils.toPlatformImage +import net.mamoe.mirai.utils.toUHexString +import java.io.File +import javax.imageio.ImageIO + +val sessionKey: ByteArray = "F1 ED F2 BC 55 17 7B FE CC CC F3 08 D1 8D A7 0E".hexToBytes() + +fun main() = println({ + val image = ImageIO.read(File("C:\\Users\\Him18\\Desktop\\test2.png").readBytes().inputStream()).toPlatformImage("png") + + // File("C:\\Users\\Him18\\Desktop\\test2.jpg").writeBytes(image.fileData.readBytes()) + GroupImageIdRequestPacket( + 1994701021u, + 580266363u, + image, + sessionKey + ).packet.readRemainingBytes().toUHexString() +}()) diff --git a/mirai-debug/src/main/java/HexComparator.kt b/mirai-debug/src/main/java/HexComparator.kt index 16917cdf3..973eceea0 100644 --- a/mirai-debug/src/main/java/HexComparator.kt +++ b/mirai-debug/src/main/java/HexComparator.kt @@ -1,11 +1,11 @@ @file:Suppress("ObjectPropertyName", "unused", "NonAsciiCharacters", "MayBeConstant") -import net.mamoe.mirai.utils.compareHex +import net.mamoe.mirai.utils.printCompareHex import java.util.* fun main() { -// println(HexComparator.colorize("00 00 00 25 00 08 00 02 00 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 E7 86 74 F2 64 55 AD 9A EB 2F B9 DF F1 7F 8C 28 00 0B 78 14 5D A2 F5 CB 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A2 F5 CA 9D 26 CB 5E 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")) +// println(HexComparator.printColorize("00 00 00 25 00 08 00 02 00 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 E7 86 74 F2 64 55 AD 9A EB 2F B9 DF F1 7F 8C 28 00 0B 78 14 5D A2 F5 CB 01 1D 00 00 00 00 01 00 00 00 01 4D 53 47 00 00 00 00 00 5D A2 F5 CA 9D 26 CB 5E 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")) val scanner = Scanner(System.`in`) while (true) { println("Hex1: ") @@ -13,7 +13,7 @@ fun main() { println("Hex2: ") val hex2 = scanner.nextLine() println("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n") - println(compareHex(hex1, hex2)) + println(printCompareHex(hex1, hex2)) 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 0e9f00a8b..3d673ee19 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 @@ -81,14 +81,14 @@ suspend fun main() { "上传好友图片" in it.message -> withTimeoutOrNull(3000) { val id = QQ(bot, 1040400290u) - .uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()) }.toPlatformImage("PNG")) + .uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()) }.toPlatformImage("png")) it.reply(id.value) delay(1000) it.reply(Image(id)) } "上传群图片" in it.message -> withTimeoutOrNull(3000) { - val image = withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()) }.toPlatformImage("PNG") + val image = withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\lemon.png").readBytes().inputStream()) }.toPlatformImage("png") Group(bot, 580266363u).uploadImage(image) it.reply(image.id.value) delay(1000)