From 1294441716f759b7dd5b36378e277b5a1bfdac15 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 20 Nov 2019 19:40:50 +0800 Subject: [PATCH] GroupImageDownload supports --- .../protocol/tim/packet/action/FriendImage.kt | 18 +- .../protocol/tim/packet/action/GroupImage.kt | 285 +++++++++++------- .../protocol/tim/packet/action/Image.kt | 78 +++++ .../protocol/tim/packet/event/Message.kt | 12 +- .../net.mamoe.mirai/utils/ExternalImage.kt | 8 +- 5 files changed, 267 insertions(+), 134 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Image.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/FriendImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/FriendImage.kt index ba8945999..0dc48d367 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/FriendImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/FriendImage.kt @@ -2,11 +2,11 @@ package net.mamoe.mirai.network.protocol.tim.packet.action -import io.ktor.client.request.get import kotlinx.io.charsets.Charsets import kotlinx.io.core.* import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.message.ImageId +import net.mamoe.mirai.message.ImageId0x06 import net.mamoe.mirai.message.requireLength import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.protocol.tim.TIMProtocol @@ -59,11 +59,8 @@ interface FriendImageResponse : EventPacket * 图片数据地址. */ // TODO: 2019/11/15 应该为 inline class, 但 kotlin 有 bug -data class FriendImageLink(inline val value: String) : FriendImageResponse { - suspend fun downloadAsByteArray(): ByteArray = download().readBytes() - suspend fun download(): ByteReadPacket = Http.get(value) - - override fun toString(): String = "FriendImageLink($value)" +data class FriendImageLink(override inline val original: String) : FriendImageResponse, ImageLink { + override fun toString(): String = "FriendImageLink($original)" } /** @@ -199,7 +196,7 @@ object FriendImagePacket : SessionPacketFactory() { writeUByte(0x1Au) writeUByte(0x47u) writeTUVarint(0x08u, bot) - writeTUVarint(0x10u, bot) + writeTUVarint(0x10u, bot) // 这里实际上应该是这张图片来自哪个 QQ 号. 但传 bot 也没事. writeTLV(0x1Au, imageId.value.toByteArray(Charsets.ISO_8859_1)) writeHex("20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01") } @@ -276,7 +273,7 @@ object FriendImagePacket : SessionPacketFactory() { while (readUByte().toUInt() != 0x4Au) readUVarLong() val uKey = readBytes(readUVarInt().toInt())//128 while (readUByte().toUInt() != 0x52u) readUVarLong() - val imageId = ImageId(readString(readUVarInt().toInt()))//37 + val imageId = ImageId0x06(readString(readUVarInt().toInt()))//37 return FriendImageUKey(imageId, uKey) } catch (e: EOFException) { val toDiscard = readUByte().toInt() - 37 @@ -285,7 +282,7 @@ object FriendImagePacket : SessionPacketFactory() { FriendImageOverFileSizeMax } else { discardExact(toDiscard) - val imageId = ImageId(readString(37)) + val imageId = ImageId0x06(readString(37)) FriendImageAlreadyExists(imageId) } } @@ -302,6 +299,7 @@ object FriendImagePacket : SessionPacketFactory() { // 3A 00 80 01 00 + //00 00 00 08 00 00 // [02 29] // 12 [06] 98 01 02 A0 01 00 @@ -315,7 +313,7 @@ object FriendImagePacket : SessionPacketFactory() { discardExact(1) discardExact(2)// [A4 04] 后文长度 check(readUByte().toUInt() == 0x0Au) { "Illegal identity. Required 0x0Au" } - /* val imageId = */ImageId(readString(readUByte().toInt())) + /* val imageId = */ImageId0x06(readString(readUByte().toInt())) check(readUByte().toUInt() == 0x18u) { "Illegal identity. Required 0x18u" } check(readUShort().toUInt() == 0x0032u) { "Illegal identity. Required 0x0032u" } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt index 1e794428b..4552eb13b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/GroupImage.kt @@ -3,8 +3,9 @@ package net.mamoe.mirai.network.protocol.tim.packet.action import kotlinx.coroutines.withContext -import kotlinx.io.charsets.Charsets -import kotlinx.io.core.* +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.discardExact +import kotlinx.io.core.readBytes import kotlinx.serialization.SerialId import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoBuf @@ -13,16 +14,17 @@ import net.mamoe.mirai.contact.GroupId import net.mamoe.mirai.contact.GroupInternalId import net.mamoe.mirai.contact.withSession import net.mamoe.mirai.message.ImageId +import net.mamoe.mirai.message.ImageId0x03 import net.mamoe.mirai.message.requireLength import net.mamoe.mirai.network.BotNetworkHandler -import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.event.EventPacket import net.mamoe.mirai.qqAccount import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.Http import net.mamoe.mirai.utils.assertUnreachable -import net.mamoe.mirai.utils.io.* +import net.mamoe.mirai.utils.io.debugPrintln +import net.mamoe.mirai.utils.io.toUHexString import kotlin.coroutines.coroutineContext @@ -64,14 +66,32 @@ interface GroupImageResponse : EventPacket // endregion +@Suppress("unused") @Serializable -data class ImageDownloadInfo( - @SerialId(11) val host: String, +class ImageDownloadInfo( + @SerialId(3) val errorCode: Int = 0, // 0 for success + @SerialId(4) val errorMessage: String? = null, // 感动中国 - @SerialId(12) val thumbnail: String, - @SerialId(13) val original: String, - @SerialId(14) val compressed: String -) : GroupImageResponse + @SerialId(10) private val _port: List? = null, + @SerialId(11) private val _host: String? = null, + + @SerialId(12) private val _thumbnail: String? = null, + @SerialId(13) private val _original: String? = null, + @SerialId(14) private val _compressed: String? = null +) : GroupImageResponse, ImageLink { + private val port: List get() = _port!! + private val host: String get() = "http://" + _host!! + + val thumbnail: String get() = host + ":" + port.first() + _thumbnail!! + override val original: String get() = host + ":" + port.first() + _original!! + val compressed: String get() = host + ":" + port.first() + _compressed!! + override fun toString(): String = "ImageDownloadInfo(${_original?.let { original } ?: errorMessage ?: "unknown"}" +} + +fun ImageDownloadInfo.requireSuccess(): ImageDownloadInfo { + require(this.errorCode == 0) { this.errorMessage ?: "null" } + return this +} @Serializable class ImageUploadInfo( @@ -86,126 +106,162 @@ class ImageUploadInfo( @AnnotatedId(KnownPacketId.GROUP_IMAGE_ID) @PacketVersion(date = "2019.10.26", timVersion = "2.3.2 (21173)") object GroupImagePacket : SessionPacketFactory() { + + /* +请求上传图片 +ProtoMap( + varint 0=0x5D(UVarInt(data=93)) + varint 1=0x01(UVarInt(data=1)) + delimi 2=98 01 01 + varint 2=0x01(UVarInt(data=1)) + delimi 3=08 A0 89 F7 B6 03 10 A2 FF 8C F0 03 18 00 22 10 2C F2 65 98 12 EA 9C 88 60 BD 7A 29 8E 6F 9B 4D 28 F0 0B 32 1A 43 00 4D 00 45 00 35 00 44 00 53 00 5A 00 43 00 4C 00 52 00 51 00 29 00 28 00 38 01 48 01 50 8B 02 58 BE 03 60 02 6A 05 32 36 39 33 33 70 00 78 03 80 01 00 +) + */ + private val RequestImageIdUnknownByteArray = ubyteArrayOf(0x98u, 0x01u, 0x01u).toByteArray() + private val constValue3 = byteArrayOf( + 0x28, 0x00, 0x5A, 0x00, 0x53, 0x00, 0x41, 0x00, 0x58, 0x00, 0x40, 0x00, 0x57, + 0x00, 0x4B, 0x00, 0x52, 0x00, 0x4A, 0x00, 0x5A, 0x00, 0x31, 0x00, 0x7E, 0x00 + ) + + @Suppress("unused") + @Serializable + private class RequestIdProto( + @SerialId(2) val unknown4: Byte = 1, + @SerialId(3) var body: Body + ) { + @Serializable + class Body( + @SerialId(1) val group: Int, + @SerialId(2) val bot: Int, + @SerialId(3) val const1: Byte = 0, + @SerialId(4) val md5: ByteArray, + @SerialId(5) val const2: Short = 0x0E2D, + @SerialId(6) val const3: ByteArray = constValue3, + @SerialId(7) val const4: Byte = 1, + @SerialId(9) val const5: Byte = 1, + @SerialId(10) val width: Int, + @SerialId(11) val height: Int, + @SerialId(12) val const6: Byte = 4, + @SerialId(13) val const7: ByteArray = constValue7, + @SerialId(14) val const8: Byte = 0, + @SerialId(15) val const9: Byte = 3, + @SerialId(16) val const10: Byte = 0 + ) + } + + @Suppress("unused") + @Serializable + private class RequestLinkProto( + @SerialId(2) val unknown4: Byte = 2, + @SerialId(4) var body: Body + ) { + /* + ProtoMap( + varint 1=0x8E87C28403(UVarInt(data=814777230)) + varint 2=0xA2FF8CF003(UVarInt(data=1040400290)) + varint 3=0xFBECB9A9A(UVarInt(data=2771285627)) + delimi 4=66 3C 60 FB 31 67 85 84 1A 18 00 52 2C D6 C8 7E + varint 5=0x04(UVarInt(data=4)) + varint 6=0x02(UVarInt(data=2)) + varint 7=0x20(UVarInt(data=32)) + varint 8=0xFF01(UVarInt(data=255)) + varint 9=0x00(UVarInt(data=0)) + varint 10=0x01(UVarInt(data=1)) + delimi 11=32 36 39 33 33 + varint 12=0x00(UVarInt(data=0)) + varint 13=0x00(UVarInt(data=0)) + varint 14=0x01(UVarInt(data=1)) + varint 15=0x00(UVarInt(data=0)) + varint 16=0xA412(UVarInt(data=2340)) + varint 17=0xB808(UVarInt(data=1080)) + varint 18=0xE807(UVarInt(data=1000)) + varint 20=0x01(UVarInt(data=1)) + ) + */ + @Serializable + class Body( + @SerialId(1) val group: Int, + @SerialId(2) val bot: Int, + @SerialId(3) val uniqueId: Int, + @SerialId(4) val md5: ByteArray, + @SerialId(5) val const2: Byte = 4, + @SerialId(6) val const3: Byte = 2, + @SerialId(7) val const4: Byte = 32, + @SerialId(8) val const14: Int = 255, + @SerialId(9) val const5: Byte = 0, + @SerialId(10) val unknown5: Int = 1, + @SerialId(11) val const7: ByteArray = constValue7, + @SerialId(12) val unknown6: Byte = 0, + @SerialId(13) val const6: Byte = 0, + @SerialId(14) val const8: Byte = 0, + @SerialId(15) val const9: Byte = 0, + @SerialId(16) val height: Int, + @SerialId(17) val width: Int, + @SerialId(18) val const12: Int = 1003, //?? 有时候还是1000, 1004 + @SerialId(20) val const13: Byte = 1 + ) + } + + private val constValue7: ByteArray = byteArrayOf(0x32, 0x36, 0x39, 0x33, 0x33) + + private val requestImageIdHead = ubyteArrayOf(0x12u, 0x03u, 0x98u, 0x01u, 0x01u) + @Suppress("FunctionName") fun RequestImageId( bot: UInt, groupInternalId: GroupInternalId, image: ExternalImage, sessionKey: SessionKey - ): OutgoingPacket = buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x04) { - writeHex("00 00 00 07 00 00") - - writeShortLVPacket(lengthOffset = { it - 7 }) { - writeByte(0x08) - writeHex("01 12 03 98 01 01 10 01 1A") - // 02 10 02 22 - - writeUVarIntLVPacket(lengthOffset = { it }) { - writeTUVarint(0x08u, groupInternalId.value) - writeTUVarint(0x10u, bot) - writeTV(0x1800u) - - writeUByte(0x22u) - writeUByte(0x10u) - writeFully(image.md5) - - writeTUVarint(0x28u, image.inputSize.toUInt()) - writeUVarIntLVPacket(tag = 0x32u) { - 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(0x4F_00u) - } - writeTV(0x38_01u) - writeTV(0x48_01u) - writeTUVarint(0x50u, image.width.toUInt()) - writeTUVarint(0x58u, image.height.toUInt()) - writeTV(0x60_04u)//这个似乎会变 有时候是02, 有时候是03 - writeTByteArray(0x6Au, value0x6A) - - writeTV(0x70_00u) - writeTV(0x78_03u) - writeTV(0x80_01u) - writeUByte(0u) - } - } - - } + ): OutgoingPacket = buildSessionProtoPacket( + bot, sessionKey, name = "GroupImagePacket.RequestImageId", + head = requestImageIdHead, + serializer = RequestIdProto.serializer(), + protoObj = RequestIdProto( + body = RequestIdProto.Body( + bot = bot.toInt(), + group = groupInternalId.value.toInt(), + md5 = image.md5, + height = image.height, + width = image.width + ) + ) + ) + private val requestImageLinkHead = ubyteArrayOf(0x08u, 0x01u, 0x12u, 0x03u, 0x98u, 0x01u, 0x2u) @Suppress("FunctionName") fun RequestImageLink( bot: UInt, sessionKey: SessionKey, - imageId: ImageId + imageId: ImageId0x03 ): OutgoingPacket { imageId.requireLength() - require(imageId.value.length == 37) { "ImageId.value.length must == 37" } - - // 00 00 00 07 00 00 00 - // [4B] - // 08 - // 01 12 - // 03 98 - // 01 02 - // 08 02 - // - // 1A [47] - // 08 [A2 FF 8C F0 03] UVarInt - // 10 [DD F1 92 B7 07] UVarInt - // 1A [25] 2F 38 65 32 63 32 38 62 64 2D 35 38 61 31 2D 34 66 37 30 2D 38 39 61 31 2D 65 37 31 39 66 63 33 30 37 65 65 66 - // 20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01 - - - // 00 00 00 07 00 00 00 - // [4B] - // 08 01 - // 12 03 - // 98 01 02 - // 08 02 - // - // 1A - // [47] - // 08 [A2 FF 8C F0 03] - // 10 [A6 A7 F1 EA 02] - // 1A [25] 2F 39 61 31 66 37 31 36 32 2D 38 37 30 38 2D 34 39 30 38 2D 38 31 63 30 2D 66 34 63 64 66 33 35 63 38 64 37 65 - // 20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01 - - return buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x04) { - writeHex("00 00 00 07 00 00") - - writeUShort(0x004Bu) - - writeUByte(0x08u) - writeTV(0x01_12u) - writeTV(0x03_98u) - writeTV(0x01_02u) - writeTV(0x08_02u) - - writeUByte(0x1Au) - writeUByte(0x47u) - writeTUVarint(0x08u, bot) - writeTUVarint(0x10u, bot) - writeTLV(0x1Au, imageId.value.toByteArray(Charsets.ISO_8859_1)) - writeHex("20 02 30 04 38 20 40 FF 01 50 00 6A 05 32 36 39 33 33 78 01") - } + //require(imageId.value.length == 37) { "ImageId.value.length must == 37" } + //[00 00 00 07] [00 00 00 52] (08 01 12 03 98 01 02) 10 02 22 4E 08 A0 89 F7 B6 03 10 A2 FF 8C F0 03 18 BB 92 94 BF 08 22 10 64 CF BB 65 00 13 8D B5 58 E2 45 1E EA 65 88 E1 28 04 30 02 38 20 40 FF 01 48 00 50 01 5A 05 32 36 39 33 33 60 00 68 00 70 00 78 00 80 01 97 04 88 01 ED 03 90 01 04 A0 01 01 + // head 长度 proto 长度 head proto + return buildSessionProtoPacket( + bot, + sessionKey, + name = "GroupImagePacket.RequestImageLink", + head = requestImageLinkHead, + serializer = RequestLinkProto.serializer(), + protoObj = RequestLinkProto( + body = RequestLinkProto.Body( + bot = bot.toInt(), // same bin representation, so will be decoded correctly as a unsigned value in the server + group = bot.toInt(), // TODO 似乎是必须要填group ?? + uniqueId = imageId.uniqueId.toInt(), + md5 = imageId.md5, + height = imageId.height, + width = imageId.width + ) + ) + ) } - private val value0x6A: UByteArray = ubyteArrayOf(0x05u, 0x32u, 0x36u, 0x36u, 0x35u, 0x36u) - override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupImageResponse { - discardExact(6)//00 00 00 05 00 00 - - discardExact(2) // 是 protobuf 的长度, 但是是错的 - val bytes = readBytes() + val headLength = readInt() + val protoLength = readInt() + discardExact(headLength) + val bytes = readBytes(protoLength) // println(ByteReadPacket(bytes).readProtoMap()) @Serializable @@ -214,6 +270,7 @@ object GroupImagePacket : SessionPacketFactory() { @SerialId(4) val imageDownloadInfo: ImageDownloadInfo? = null ) + debugPrintln("收到返回=" + bytes.toUHexString()) val proto = ProtoBuf.load(GroupImageResponseProto.serializer(), bytes) return when { proto.imageUploadInfoPacket != null -> proto.imageUploadInfoPacket diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Image.kt new file mode 100644 index 000000000..e577b0157 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Image.kt @@ -0,0 +1,78 @@ +@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "unused", "NO_REFLECTION_IN_CLASS_PATH") + +package net.mamoe.mirai.network.protocol.tim.packet.action + +import io.ktor.client.request.get +import kotlinx.io.core.ByteReadPacket +import kotlinx.io.core.readBytes +import net.mamoe.mirai.utils.Http + +/** + * 图片文件过大 + */ +class OverFileSizeMaxException : IllegalStateException() + +interface ImageLink { + /** + * 原图 + */ + val original: String + + suspend fun downloadAsByteArray(): ByteArray = download().readBytes() + + suspend fun download(): ByteReadPacket = Http.get(original) +} + +/* +/** + * 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00 + */ +@Deprecated("Useless packet") +@AnnotatedId(KnownPacketId.SUBMIT_IMAGE_FILE_NAME) +@PacketVersion(date = "2019.10.26", timVersion = "2.3.2 (21173)") +object SubmitImageFilenamePacket : PacketFactory { + operator fun invoke( + bot: UInt, + target: UInt, + filename: String, + sessionKey: SessionKey + ): OutgoingPacket = buildOutgoingPacket { + writeQQ(bot) + writeFully(TIMProtocol.fixVer2)//? + //writeHex("04 00 00 00 01 2E 01 00 00 69 35") + + encryptAndWrite(sessionKey) { + writeByte(0x01) + writeQQ(bot) + writeQQ(target) + writeZero(2) + writeUByte(0x02u) + writeRandom(1) + writeHex("00 0A 00 01 00 01") + val name = "UserDataImage:$filename" + writeShort(name.length.toShort()) + writeStringUtf8(name) + writeHex("00 00") + writeRandom(2)//这个也与是哪个好友有关? + writeHex("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2E 01")//35 02? 最后这个值是与是哪个好友有关 + + //this.debugPrintThis("SubmitImageFilenamePacket") + } + + //解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1A 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 28 5A 53 41 58 40 57 4B 52 4A 5A 31 7E 33 59 4F 53 53 4C 4D 32 4B 49 2E 6A 70 67 00 00 06 E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02 + //解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1B 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 28 5A 53 41 58 40 57 4B 52 4A 5A 31 7E 33 59 4F 53 53 4C 4D 32 4B 49 2E 6A 70 67 00 00 06 E2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02 + //解密body=01 3E 03 3F A2 7C BC D3 C1 00 00 27 1C 00 0A 00 01 00 01 00 30 55 73 65 72 44 61 74 61 43 75 73 74 6F 6D 46 61 63 65 3A 31 5C 29 37 42 53 4B 48 32 44 35 54 51 28 5A 35 7D 35 24 56 5D 32 35 49 4E 2E 6A 70 67 00 00 03 73 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2F 02 + } + + @PacketVersion(date = "2019.10.19", timVersion = "2.3.2 (21173)") + class Response { + override fun decode() = with(input) { + require(readBytes().contentEquals(expecting)) + } + + companion object { + private val expecting = byteArrayOf(0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00) + } + } +}*/ +// regiion GroupImageResponse diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt index 96b79b7ce..0cee8e206 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt @@ -5,24 +5,25 @@ package net.mamoe.mirai.network.protocol.tim.packet.event import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact import kotlinx.io.core.readUInt -import net.mamoe.mirai.* +import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.events.BotEvent +import net.mamoe.mirai.getGroup +import net.mamoe.mirai.getQQ import net.mamoe.mirai.message.* import net.mamoe.mirai.message.internal.readMessageChain import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion -import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageLink -import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImagePacket -import net.mamoe.mirai.network.sessionKey +import net.mamoe.mirai.network.protocol.tim.packet.action.ImageLink import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.io.printTLVMap import net.mamoe.mirai.utils.io.read import net.mamoe.mirai.utils.io.readTLVMap import net.mamoe.mirai.utils.io.readUShortLVByteArray +import net.mamoe.mirai.withSession import kotlin.jvm.JvmName /** @@ -87,8 +88,8 @@ abstract class MessagePacketBase : EventPacket, BotEvent() { // endregion // region Image download + suspend inline fun Image.getLink(): ImageLink = bot.withSession { getLink() } - suspend fun Image.getLink(): FriendImageLink = bot.withSession { FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect() } suspend inline fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray() suspend inline fun Image.download(): ByteReadPacket = getLink().download() // endregion @@ -106,6 +107,7 @@ data class GroupMessage( override val sender: QQ, override val message: MessageChain = NullMessageChain ) : MessagePacket() { + override val subject: Group get() = group } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt index 444bb4a36..ff26b1ed8 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt @@ -7,10 +7,7 @@ import kotlinx.io.core.Input import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.QQ -import net.mamoe.mirai.message.Image -import net.mamoe.mirai.message.ImageId -import net.mamoe.mirai.message.image -import net.mamoe.mirai.message.sendTo +import net.mamoe.mirai.message.* import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage import net.mamoe.mirai.utils.io.toUHexString @@ -52,7 +49,8 @@ class ExternalImage( /** * 用于发送消息的 [ImageId] */ - val groupImageId: ImageId by lazy { ImageId("{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format") } + @Suppress("EXPERIMENTAL_UNSIGNED_LITERALS") + val groupImageId: ImageId by lazy { ImageId0x03("{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format", 0u, height, width) } override fun toString(): String = "[ExternalImage(${width}x$height $format)]" }