Rename responses

This commit is contained in:
Him188 2019-11-16 20:47:10 +08:00
parent e3add09391
commit 1c9994e87d
5 changed files with 263 additions and 201 deletions

View File

@ -20,8 +20,8 @@ import net.mamoe.mirai.network.protocol.tim.handler.TemporaryPacketHandler
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.protocol.tim.packet.SessionKey
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.protocol.tim.packet.action.ImageLink
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.getGTK
import net.mamoe.mirai.utils.internal.PositiveNumbers
@ -142,7 +142,7 @@ abstract class BotSessionBase(
suspend inline fun GroupId.group(): Group = bot.getGroup(this)
suspend inline fun GroupInternalId.group(): Group = bot.getGroup(this)
suspend fun Image.getLink(): ImageLink = FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect()
suspend fun Image.getLink(): FriendImageLink = FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect()
suspend inline fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray()
suspend inline fun Image.download(): ByteReadPacket = getLink().download()
}

View File

@ -2,9 +2,6 @@
package net.mamoe.mirai.network.protocol.tim.packet
import net.mamoe.mirai.event.Subscribable
import kotlin.reflect.KClass
/**
* ID. 除特殊外, [PacketFactory] 都需要这个注解来指定包 ID.
@ -18,18 +15,6 @@ annotation class AnnotatedId( // 注解无法在 JS 平台使用, 但现在暂
inline val AnnotatedId.value: UShort get() = id.value
/**
* 标记这个包对应的事件.
* 这个注解应该被标记在 [Packet]
*/
@MustBeDocumented
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class CorrespondingEvent(
val eventClass: KClass<out Subscribable>
)
/**
* 包的最后一次修改时间, 和分析时使用的 TIM 版本
*/

View File

@ -74,7 +74,7 @@ enum class KnownPacketId(override inline val value: UShort, override inline val
inline CAN_ADD_FRIEND(0x00A7u, CanAddFriendPacket),
inline ADD_FRIEND(0x00A8u, AddFriendPacket),
inline REQUEST_FRIEND_ADDITION_KEY(0x00AEu, RequestFriendAdditionKeyPacket),
inline GROUP_IMAGE_ID(0x0388u, GroupImageIdRequestPacket),
inline GROUP_IMAGE_ID(0x0388u, GroupImagePacket),
inline FRIEND_IMAGE_ID(0x0352u, FriendImagePacket),
inline REQUEST_PROFILE_AVATAR(0x0031u, RequestProfileAvatarPacket),

View File

@ -22,6 +22,7 @@ import net.mamoe.mirai.network.qqAccount
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.configureBody
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.withSession
@ -42,11 +43,11 @@ class OverFileSizeMaxException : IllegalStateException()
*/
suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
val userContext = coroutineContext
val response = GroupImageIdRequestPacket(bot.qqAccount, internalId, image, sessionKey).sendAndExpect<GroupImageIdRequestPacket.Response>()
val response = GroupImagePacket.RequestImageId(bot.qqAccount, internalId, image, sessionKey).sendAndExpect<GroupImageResponse>()
withContext(userContext) {
when (response) {
is GroupImageIdRequestPacket.Response.RequireUpload -> Http.postImage(
is GroupImageUKey -> Http.postImage(
htcmd = "0x6ff0071",
uin = bot.qqAccount,
groupId = GroupId(id),
@ -55,10 +56,11 @@ suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
uKeyHex = response.uKey.toUHexString("")
)
is GroupImageIdRequestPacket.Response.AlreadyExists -> {
is GroupImageAlreadyExists -> {
}
is GroupImageIdRequestPacket.Response.OverFileSizeMax -> throw OverFileSizeMaxException()
is GroupImageOverFileSizeMax -> throw OverFileSizeMaxException()
else -> assertUnreachable()
}
}
@ -75,9 +77,9 @@ suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
*/
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession {
FriendImagePacket.RequestImageId(qqAccount, sessionKey, id, image)
.sendAndExpectAsync<ImageResponse, ImageId> {
.sendAndExpectAsync<FriendImageResponse, ImageId> {
return@sendAndExpectAsync when (it) {
is ImageUKey -> {
is FriendImageUKey -> {
Http.postImage(
htcmd = "0x6ff0070",
uin = bot.qqAccount,
@ -88,8 +90,8 @@ suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession {
)
it.imageId
}
is ImageAlreadyExists -> it.imageId
is ImageOverFileSizeMax -> throw OverFileSizeMaxException()
is FriendImageAlreadyExists -> it.imageId
is FriendImageOverFileSizeMax -> throw OverFileSizeMaxException()
else -> error("This shouldn't happen")
}
}.await()
@ -183,41 +185,83 @@ object SubmitImageFilenamePacket : PacketFactory {
}
}*/
interface ImageResponse : EventPacket
// region FriendImageResponse
interface FriendImageResponse : EventPacket
/**
* 图片数据地址.
*/
// TODO: 2019/11/15 应该为 inline class, 但 kotlin 有 bug
data class ImageLink(inline val value: String) : ImageResponse {
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 = "ImageLink($value)"
override fun toString(): String = "FriendImageLink($value)"
}
/**
* 访问 HTTP API 时使用的 uKey
*/
class ImageUKey(inline val imageId: ImageId, inline val uKey: ByteArray) : ImageResponse {
override fun toString(): String = "ImageUKey(imageId=${imageId.value}, uKey=${uKey.toUHexString()})"
class FriendImageUKey(inline val imageId: ImageId, inline val uKey: ByteArray) : FriendImageResponse {
override fun toString(): String = "FriendImageUKey(imageId=${imageId.value}, uKey=${uKey.toUHexString()})"
}
/**
* 图片 ID 已存在
* 发送消息时使用的 id
*/
inline class ImageAlreadyExists(inline val imageId: ImageId) : ImageResponse {
inline class FriendImageAlreadyExists(inline val imageId: ImageId) : FriendImageResponse {
override fun toString(): String = "FriendImageAlreadyExists(imageId=${imageId.value})"
}
/**
* 超过文件大小上限
*/
object ImageOverFileSizeMax : ImageResponse {
object FriendImageOverFileSizeMax : FriendImageResponse {
override fun toString(): String = "FriendImageOverFileSizeMax"
}
// endregion
// regiion GroupImageResponse
interface GroupImageResponse : EventPacket
/**
* 图片数据地址.
*/
// TODO: 2019/11/15 应该为 inline class, 但 kotlin 有 bug
data class GroupImageLink(inline val value: String) : GroupImageResponse {
suspend fun downloadAsByteArray(): ByteArray = download().readBytes()
suspend fun download(): ByteReadPacket = Http.get(value)
override fun toString(): String = "GroupImageLink($value)"
}
/**
* 访问 HTTP API 时使用的 uKey
*/
inline class GroupImageUKey(inline val uKey: ByteArray) : GroupImageResponse {
override fun toString(): String = "GroupImageUKey(uKey=${uKey.toUHexString()})"
}
/**
* 图片 ID 已存在
* 发送消息时使用的 id
*/
object GroupImageAlreadyExists : GroupImageResponse {
override fun toString(): String = "GroupImageAlreadyExists"
}
/**
* 超过文件大小上限
*/
object GroupImageOverFileSizeMax : GroupImageResponse {
override fun toString(): String = "GroupImageOverFileSizeMax"
}
// endregion
/**
* 请求上传图片. 将发送图片的 md5, size, width, height.
* 服务器返回以下之一:
@ -226,7 +270,7 @@ object ImageOverFileSizeMax : ImageResponse {
*/
@AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
@PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)")
object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
object FriendImagePacket : SessionPacketFactory<FriendImageResponse>() {
@Suppress("FunctionName")
fun RequestImageId(
bot: UInt,
@ -333,103 +377,102 @@ object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
}
}
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): ImageResponse =
with(this) {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): FriendImageResponse {
// 上传图片, 成功获取ID
//00 00 00 08 00 00
// [01 0D]
// 12 [06] 98 01 01 A0 01 00
// 08 01 //packet type 01=上传图片; 02=下载图片
// 12 [86 02]
// 08 00
// 10 [9B A4 D4 9A 0A]
// 18 00
// 28 00
// 38 F1 C0 A1 BF 05
// 38 BB C8 E4 E2 0F
// 38 FB AE FA 9D 0A
// 38 E5 C6 8B CD 06
// 40 BB 03 // ports
// 40 90 3F
// 40 50
// 40 BB 03
// 4A [80 01] 76 B2 58 23 B8 F6 B1 E6 AE D4 76 EC 3C 08 79 B1 DF 05 D5 C2 4A E0 CC F1 2F 26 4F D4 DC 44 5A 9A 16 A9 E4 22 EB 92 96 05 C3 C9 8F C5 5F 84 00 A3 4E 63 BE 76 F7 B9 7B 09 43 A6 14 EE C8 6D 6A 48 02 E3 9D 62 CD 42 3E 15 93 64 8F FC F5 88 50 74 6A 6A 03 C9 FE F0 96 EA 76 02 DC 4F 09 D0 F5 60 73 B2 62 8F 8B 11 06 BF 06 1B 18 00 FE B4 5E F3 12 72 F2 66 9C F5 01 97 1C 0A 5B 68 5B 85 ED 9C
// 52 [25] 2F 37 38 62 36 34 64 63 32 2D 31 66 32 31 2D 34 33 62 38 2D 39 32 62 31 2D 61 30 35 30 35 30 34 30 35 66 65 32
// 5A [25] 2F 37 38 62 36 34 64 63 32 2D 31 66 32 31 2D 34 33 62 38 2D 39 32 62 31 2D 61 30 35 30 35 30 34 30 35 66 65 32
// 60 00 68 80 80 08
// 20 01
// 上传图片, 成功获取ID
//00 00 00 08 00 00
// [01 0D]
// 12 [06] 98 01 01 A0 01 00
// 08 01 //packet type 01=上传图片; 02=下载图片
// 12 [86 02]
// 08 00
// 10 [9B A4 D4 9A 0A]
// 18 00
// 28 00
// 38 F1 C0 A1 BF 05
// 38 BB C8 E4 E2 0F
// 38 FB AE FA 9D 0A
// 38 E5 C6 8B CD 06
// 40 BB 03 // ports
// 40 90 3F
// 40 50
// 40 BB 03
// 4A [80 01] 76 B2 58 23 B8 F6 B1 E6 AE D4 76 EC 3C 08 79 B1 DF 05 D5 C2 4A E0 CC F1 2F 26 4F D4 DC 44 5A 9A 16 A9 E4 22 EB 92 96 05 C3 C9 8F C5 5F 84 00 A3 4E 63 BE 76 F7 B9 7B 09 43 A6 14 EE C8 6D 6A 48 02 E3 9D 62 CD 42 3E 15 93 64 8F FC F5 88 50 74 6A 6A 03 C9 FE F0 96 EA 76 02 DC 4F 09 D0 F5 60 73 B2 62 8F 8B 11 06 BF 06 1B 18 00 FE B4 5E F3 12 72 F2 66 9C F5 01 97 1C 0A 5B 68 5B 85 ED 9C
// 52 [25] 2F 37 38 62 36 34 64 63 32 2D 31 66 32 31 2D 34 33 62 38 2D 39 32 62 31 2D 61 30 35 30 35 30 34 30 35 66 65 32
// 5A [25] 2F 37 38 62 36 34 64 63 32 2D 31 66 32 31 2D 34 33 62 38 2D 39 32 62 31 2D 61 30 35 30 35 30 34 30 35 66 65 32
// 60 00 68 80 80 08
// 20 01
// 上传图片, 图片过大
//00 00 00 09 00 00 00 1D 12 07 98 01 01 A0 01 C7 01 08 01 12 19 08 00 18 C7 01 22 12 66 69 6C 65 20 73 69 7A 65 20 6F 76 65 72 20 6D 61 78
discardExact(3) // 00 00 00
if (readUByte().toUInt() == 0x09u) {
return ImageOverFileSizeMax
}
discardExact(2) //00 00
// 上传图片, 图片过大
//00 00 00 09 00 00 00 1D 12 07 98 01 01 A0 01 C7 01 08 01 12 19 08 00 18 C7 01 22 12 66 69 6C 65 20 73 69 7A 65 20 6F 76 65 72 20 6D 61 78
discardExact(3) // 00 00 00
if (readUByte().toUInt() == 0x09u) {
return FriendImageOverFileSizeMax
}
discardExact(2) //00 00
discardExact(2) //全长 (有 offset)
discardExact(2) //全长 (有 offset)
discardExact(1); discardExact(readUVarInt().toInt()) // 12 06 98 01 01 A0 01 00
discardExact(1); discardExact(readUVarInt().toInt()) // 12 06 98 01 01 A0 01 00
check(readUByte().toUInt() == 0x08u)
when (val flag = readUByte().toUInt()) {
0x01u -> {
try {
while (readUByte().toUInt() != 0x4Au) readUVarLong()
val uKey = readBytes(readUVarInt().toInt())//128
while (readUByte().toUInt() != 0x52u) readUVarLong()
val imageId = ImageId(readString(readUVarInt().toInt()))//37
return ImageUKey(imageId, uKey)
} catch (e: EOFException) {
val toDiscard = readUByte().toInt() - 37
check(readUByte().toUInt() == 0x08u)
return when (val flag = readUByte().toUInt()) {
0x01u -> {
try {
while (readUByte().toUInt() != 0x4Au) readUVarLong()
val uKey = readBytes(readUVarInt().toInt())//128
while (readUByte().toUInt() != 0x52u) readUVarLong()
val imageId = ImageId(readString(readUVarInt().toInt()))//37
return FriendImageUKey(imageId, uKey)
} catch (e: EOFException) {
val toDiscard = readUByte().toInt() - 37
return if (toDiscard < 0) {
ImageOverFileSizeMax
} else {
discardExact(toDiscard)
val imageId = ImageId(readString(37))
ImageAlreadyExists(imageId)
}
return if (toDiscard < 0) {
FriendImageOverFileSizeMax
} else {
discardExact(toDiscard)
val imageId = ImageId(readString(37))
FriendImageAlreadyExists(imageId)
}
}
0x02u -> {
//00 00 00 08 00 00
// [02 2B]
// 12 [06] 98 01 02 A0 01 00
// 08 02
// 1A [A6 04]
// 0A [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
// 18 00
// 32 [7B] 68 74 74 70 3A 2F 2F 36 31 2E 31 35 31 2E 32 33 34 2E 35 34 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 30 31 2E 32 32 37 2E 31 33 31 2E 36 37 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7D 68 74 74 70 3A 2F 2F 31 35 37 2E 32 35 35 2E 31 39 32 2E 31 30 35 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 32 30 2E 32 34 31 2E 31 39 30 2E 34 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33
// 3A 00 80 01 00
//00 00 00 08 00 00
// [02 29]
// 12 [06] 98 01 02 A0 01 00
// 08 02
// 1A [A4 04]
// 0A [25] 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64
// 18 00
// 32 [7A] 68 74 74 70 3A 2F 2F 31 30 31 2E 38 39 2E 33 39 2E 32 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33
// 32 7B 68 74 74 70 3A 2F 2F 36 31 2E 31 35 31 2E 31 38 33 2E 32 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7D 68 74 74 70 3A 2F 2F 31 35 37 2E 32 35 35 2E 31 39 32 2E 31 30 35 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 32 30 2E 32 34 31 2E 31 39 30 2E 34 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 3A 00 80 01 00
discardExact(1)
discardExact(2)// [A4 04] 后文长度
check(readUByte().toUInt() == 0x0Au) { "Illegal identity. Required 0x0Au" }
/* val imageId = */ImageId(readString(readUByte().toInt()))
check(readUByte().toUInt() == 0x18u) { "Illegal identity. Required 0x18u" }
check(readUShort().toUInt() == 0x0032u) { "Illegal identity. Required 0x0032u" }
val link = readUVarIntLVString()
discard()
ImageLink(link)
}
else -> error("Unknown FriendImageIdRequestPacket flag $flag")
}
0x02u -> {
//00 00 00 08 00 00
// [02 2B]
// 12 [06] 98 01 02 A0 01 00
// 08 02
// 1A [A6 04]
// 0A [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
// 18 00
// 32 [7B] 68 74 74 70 3A 2F 2F 36 31 2E 31 35 31 2E 32 33 34 2E 35 34 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 30 31 2E 32 32 37 2E 31 33 31 2E 36 37 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7D 68 74 74 70 3A 2F 2F 31 35 37 2E 32 35 35 2E 31 39 32 2E 31 30 35 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 32 30 2E 32 34 31 2E 31 39 30 2E 34 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 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 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33
// 3A 00 80 01 00
//00 00 00 08 00 00
// [02 29]
// 12 [06] 98 01 02 A0 01 00
// 08 02
// 1A [A4 04]
// 0A [25] 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64
// 18 00
// 32 [7A] 68 74 74 70 3A 2F 2F 31 30 31 2E 38 39 2E 33 39 2E 32 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33
// 32 7B 68 74 74 70 3A 2F 2F 36 31 2E 31 35 31 2E 31 38 33 2E 32 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7D 68 74 74 70 3A 2F 2F 31 35 37 2E 32 35 35 2E 31 39 32 2E 31 30 35 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 32 7C 68 74 74 70 3A 2F 2F 31 32 30 2E 32 34 31 2E 31 39 30 2E 34 31 3A 38 30 2F 6F 66 66 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 2F 62 61 65 30 63 64 66 66 2D 65 33 34 30 2D 34 38 39 34 2D 39 37 36 65 2D 30 66 62 35 38 61 61 31 36 35 66 64 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 3A 00 80 01 00
discardExact(1)
discardExact(2)// [A4 04] 后文长度
check(readUByte().toUInt() == 0x0Au) { "Illegal identity. Required 0x0Au" }
/* val imageId = */ImageId(readString(readUByte().toInt()))
check(readUByte().toUInt() == 0x18u) { "Illegal identity. Required 0x18u" }
check(readUShort().toUInt() == 0x0032u) { "Illegal identity. Required 0x0032u" }
val link = readUVarIntLVString()
discard()
FriendImageLink(link)
}
else -> error("Unknown FriendImageIdRequestPacket flag $flag")
}
}
}
@ -438,104 +481,138 @@ object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
*/
@AnnotatedId(KnownPacketId.GROUP_IMAGE_ID)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2 (21173)")
object GroupImageIdRequestPacket : SessionPacketFactory<GroupImageIdRequestPacket.Response>() {
operator fun invoke(
object GroupImagePacket : SessionPacketFactory<GroupImageResponse>() {
@Suppress("FunctionName")
fun RequestImageId(
bot: UInt,
groupInternalId: GroupInternalId,
image: ExternalImage,
sessionKey: SessionKey
): OutgoingPacket = buildOutgoingPacket {
writeQQ(bot)
writeHex("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00")
): OutgoingPacket = buildSessionPacket(bot, sessionKey, version = TIMProtocol.version0x04) {
writeHex("00 00 00 07 00 00")
encryptAndWrite(sessionKey) {
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)
}
}
}
@Suppress("FunctionName")
fun RequestImageLink(
bot: UInt,
sessionKey: SessionKey,
imageId: ImageId
): 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")
writeShortLVPacket(lengthOffset = { it - 7 }) {
writeByte(0x08)
writeHex("01 12 03 98 01 01 10 01 1A")
writeUShort(0x004Bu)
writeUVarIntLVPacket(lengthOffset = { it }) {
writeTUVarint(0x08u, groupInternalId.value)
writeTUVarint(0x10u, bot)
writeTV(0x1800u)
writeUByte(0x08u)
writeTV(0x01_12u)
writeTV(0x03_98u)
writeTV(0x01_02u)
writeTV(0x08_02u)
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)
}
}
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")
}
}
private val value0x6A: UByteArray = ubyteArrayOf(0x05u, 0x32u, 0x36u, 0x36u, 0x35u, 0x36u)
sealed class Response : EventPacket {
data class RequireUpload(
/**
* 访问 HTTP API 时需要使用的一个 key. 128
*/
val uKey: ByteArray
) : Response() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is RequireUpload) return false
if (!uKey.contentEquals(other.uKey)) return false
return true
}
override fun hashCode(): Int = uKey.contentHashCode()
}
object AlreadyExists : Response() {
override fun toString(): String = this::class.simpleName!!
}
/**
* 超过文件大小上限
*/
object OverFileSizeMax : Response() {
override fun toString(): String = this::class.simpleName!!
}
}
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): Response {
override suspend fun ByteReadPacket.decode(id: PacketId, sequenceId: UShort, handler: BotNetworkHandler<*>): GroupImageResponse {
discardExact(6)//00 00 00 05 00 00
val length = remaining - 128 - 14
if (length < 0) {
return if (readUShort().toUInt() == 0x0025u) Response.OverFileSizeMax else Response.AlreadyExists
return if (readUShort().toUInt() == 0x0025u) GroupImageOverFileSizeMax else GroupImageAlreadyExists
}
discardExact(length)
return Response.RequireUpload(readBytes(128))
return GroupImageUKey(readBytes(128))
}
// 下载图片
// 00 00 00 05 00 00
// [02 46]
// 12 03 98 01 02 08 9B A4 D4 9A 0A 10 02 22 BB 04 08 92 A8 B2 D3 0A 12 10 EB 1A 34 01 8F 1E B4 73 39 34 F0 65 68 80 A7 52 18 00 48 BD EE 92 CD 01 48 BD EE 92 E5 01 48 B4 C3 A9 E8 06 48 BA F6 D7 5C 48 EF BC A4 DC 07 50 50 50 50 50 50 50 50 50 50 5A 0D 67 63 68 61 74 2E 71 70 69 63 2E 63 6E 62 77 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 38 31 34 37 37 37 32 33 30 2F 38 31 34 37 37 37 32 33 30 2D 32 38 35 39 32 34 32 35 31 34 2D 45 42 31 41 33 34 30 31 38 46 31 45 42 34 37 33 33 39 33 34 46 30 36 35 36 38 38 30 41 37 35 32 2F 31 39 38 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 6A 75 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 38 31 34 37 37 37 32 33 30 2F 38 31 34 37 37 37 32 33 30 2D 32 38 35 39 32 34 32 35 31 34 2D 45 42 31 41 33 34 30 31 38 46 31 45 42 34 37 33 33 39 33 34 46 30 36 35 36 38 38 30 41 37 35 32 2F 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 72 77 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 38 31 34 37 37 37 32 33 30 2F 38 31 34 37 37 37 32 33 30 2D 32 38 35 39 32 34 32 35 31 34 2D 45 42 31 41 33 34 30 31 38 46 31 45 42 34 37 33 33 39 33 34 46 30 36 35 36 38 38 30 41 37 35 32 2F 37 32 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 78 00 80 01 03 9A 01 77 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 38 31 34 37 37 37 32 33 30 2F 38 31 34 37 37 37 32 33 30 2D 32 38 35 39 32 34 32 35 31 34 2D 45 42 31 41 33 34 30 31 38 46 31 45 42 34 37 33 33 39 33 34 46 30 36 35 36 38 38 30 41 37 35 32 2F 34 30 30 3F 76 75 69 6E 3D 31 30 34 30 34 30 30 32 39 30 26 74 65 72 6D 3D 32 35 35 26 73 72 76 76 65 72 3D 32 36 39 33 33 A0 01 00
}

View File

@ -15,8 +15,8 @@ import net.mamoe.mirai.event.events.BotEvent
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.protocol.tim.packet.action.ImageLink
import net.mamoe.mirai.network.sessionKey
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.printTLVMap
@ -88,7 +88,7 @@ abstract class MessagePacketBase<TSubject : Contact> : EventPacket, BotEvent() {
// region Image download
suspend fun Image.getLink(): ImageLink = bot.withSession { FriendImagePacket.RequestImageLink(bot.qqAccount, bot.sessionKey, id).sendAndExpect() }
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