Commit 0494b374 authored by Him188's avatar Him188

Image sending is now available

parent bc57001e
......@@ -90,7 +90,7 @@ data class PlainText(override val stringValue: String) : Message() {
* 图片消息. 在发送时将会区分群图片和好友图片发送.
* 由接收消息时构建, 可直接发送
*
* @param id 类似 `/01ee6426-5ff1-4cf0-8278-e8634d2909ef` 或 `{F61593B5-5B98-1798-3F47-2A91D32ED2FC}.jpg`
* @param id 好友的为 `/01ee6426-5ff1-4cf0-8278-e8634d2909ef`, 群的为 `{F61593B5-5B98-1798-3F47-2A91D32ED2FC}.jpg`
* @param filename 文件名. 这将决定图片的显示
*/
data class Image(val id: ImageId, val filename: String = "") : Message() {
......
......@@ -203,11 +203,10 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
* 2F 37 33 38 33 35 38 36 37 2D 38 64 65 31 2D 34 65 30 66 2D 61 33 36 35 2D 34 39 62 30 33 39 63 34 61 39 31 66 41
*/
writeShortLVPacket {
//todo
writeByte(0x02)
//"46 52 25 46 60 30 59 4F 4A 5A 51 48 31 46 4A 53 4C 51 4C 4A 33 46 31 2E 6A 70 67".hexToBytes().stringOfWitch()
// writeShortLVString(filename)//图片文件名 FR%F`0YOJZQH1FJSLQLJ3F1.jpg
writeShortLVString(id.value.substring(1..24) + ".gif")//图片文件名 FR%F`0YOJZQH1FJSLQLJ3F1.jpg
writeShortLVString(id.value.substring(1..24) + ".gif")//图片文件名. 后缀不影响. 但无后缀会导致 PC QQ 无法显示这个图片
writeHex("03 00 04 00 00 02 A2 04")
writeShortLVString(id.value)
writeHex("14 00 04 03 00 00 00 0B 00 00 18")
......@@ -215,11 +214,14 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket {
writeHex("19 00 04 00 00 00 4E 1A 00 04 00 00 00 23 FF 00 63 16 20 20 39 39 31 30 20 38 38 31 43 42 20 20 20 20 20 20 20 ")
writeStringUtf8("674e")//补长度
writeStringUtf8(id.value.substring(1..id.value.lastIndex - 4).replace("-", "B"))//这一串文件名决定手机QQ保存的图片. 可以随意
writeStringUtf8(".gif")
writeStringUtf8("674e")//没有 "e" 服务器就不回复
writeStringUtf8(id.value.substring(1..id.value.lastIndex - 4))//这一串文件名决定手机 QQ 保存的图片. 可以随意
writeStringUtf8(".gif")//后缀要有
writeUByte(0x66u)
//有时候 PC QQ 看不到发这些消息, 但手机可以. 可能是 ID 过期了, 手机有缓存而电脑没有
/*
* writeHex("19 00 04 00 00 00 4E 1A 00 04 00 00 00 23 FF 00 63 16 20 20 39 39 31 30 20 38 38 31 43 42 20 20 20 20 20 20 20 ")
......
......@@ -187,7 +187,6 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
bot.logCyan("Packet received: $packet")
}
//coz removeIf is not inline
handlersLock.withLock {
temporaryPacketHandlers.removeIfInlined {
it.shouldRemove(this@TIMBotNetworkHandler[ActionPacketHandler].session, packet)
......
......@@ -14,7 +14,7 @@ import net.mamoe.mirai.utils.*
/**
* 上传图片
*/
suspend fun QQ.uploadImage(image: PlatformImage): ImageId = with(bot.network.session) {
suspend fun QQ.uploadImage(image: BufferedImage): ImageId = with(bot.network.session) {
//SubmitImageFilenamePacket(account, account, "sdiovaoidsa.png", sessionKey).sendAndExpect<ServerSubmitImageFilenameResponsePacket>().join()
DebugLogger.logPurple("正在上传好友图片, md5=${image.md5.toUHexString()}")
return FriendImageIdRequestPacket(account, sessionKey, account, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
......@@ -22,12 +22,12 @@ suspend fun QQ.uploadImage(image: PlatformImage): ImageId = with(bot.network.ses
require(httpPostFriendImage(
uKeyHex = it.uKey!!.toUHexString(""),
botNumber = bot.qqAccount,
imageData = image.fileData,
imageData = image.data,
fileSize = image.fileSize,
qq = account
))
it.imageId!!
} else image.id //todo 是这个么
} else TODO("分析服务器已有图片时的 imageId")
}.await()
}
......@@ -40,6 +40,9 @@ suspend fun QQ.uploadImage(image: PlatformImage): ImageId = with(bot.network.ses
//01 3E 03 3F A2 76 E4 B8 DD 00 00 50 7C 00 0A 00 01 00 01 00 2D 55 73 65 72 44 61 74 61 49 6D 61 67 65 3A 43 32 43 5C 40 53 51 25 4F 46 43 50 36 4C 48 30 47 34 43 47 57 53 49 52 46 37 32 2E 70 6E 67
// 00 01 61 A7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 2E 01
/**
* 似乎没有必要. 服务器的返回永远都是 01 00 00 00 02 00 00
*/
@PacketId(0X01_BDu)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
class SubmitImageFilenamePacket(
......@@ -95,16 +98,14 @@ class SubmitImageFilenamePacket(
* 服务器返回以下之一:
* - 服务器已经存有这个图片
* - 服务器未存有, 返回一个 key 用于客户端上传
*
* @author Him188moe
*/
@PacketId(0x03_52u)
@PacketVersion(date = "2019.10.19", timVersion = "2.3.2.21173")
@PacketVersion(date = "2019.10.20", timVersion = "2.3.2.21173")
class FriendImageIdRequestPacket(
private val botNumber: UInt,
private val sessionKey: ByteArray,
private val target: UInt,
private val image: PlatformImage
private val image: BufferedImage
) : ClientPacket() {
//00 00 00 07 00 00 00 4B 08 01 12 03 98 01 01 08 01 12 47 08 A2 FF 8C F0 03 10 89 FC A6 8C 0B 18 00 22 10 2B 23 D7 05 CA D1 F2 CF 37 10 FE 58 26 92 FC C4 28 FD 08 32 1A 7B 00 47 00 47 00 42 00 7E 00 49 00 31 00 5A 00 4D 00 43 00 28 00 25 00 49 00 38 01 48 00 70 42 78 42
......@@ -240,9 +241,9 @@ class FriendImageIdRequestPacket(
writeUShort(0x48_00u)
writeUByte(0x70u)
writeUVarInt(image.imageWidth.toUInt())
writeUVarInt(image.width.toUInt())
writeUByte(0x78u)
writeUVarInt(image.imageHeight.toUInt())
writeUVarInt(image.height.toUInt())
}
writeShort((packet.remaining - 7).toShort())//why?
writePacket(packet)
......@@ -283,9 +284,9 @@ class FriendImageIdRequestPacket(
} else {
//服务器已经有这个图片了
DebugLogger.logPurple("服务器已有好友图片 ")
}
println("获取图片 repsonse 后文=" + readRemainingBytes().toUHexString())
TODO("分析后文获取 imageId")
}
}
}
}
......
......@@ -10,13 +10,13 @@ import net.mamoe.mirai.utils.*
suspend fun Group.uploadImage(
image: PlatformImage
image: BufferedImage
) = with(bot.network.session) {
GroupImageIdRequestPacket(bot.qqAccount, groupId, image, sessionKey)
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
if (it.uKey != null) {
httpPostGroupImage(
imageData = image.fileData,
imageData = image.data,
fileSize = image.fileSize,
uKeyHex = it.uKey!!.toUHexString()
)
......@@ -32,7 +32,7 @@ suspend fun Group.uploadImage(
class GroupImageIdRequestPacket(
private val bot: UInt,
private val groupId: UInt,
private val image: PlatformImage,
private val image: BufferedImage,
private val sessionKey: ByteArray
) : ClientPacket() {
......
......@@ -3,18 +3,18 @@ package net.mamoe.mirai.utils
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.message.ImageId
class PlatformImage(
class BufferedImage(
val width: Int,
val height: Int,
val md5: ByteArray,
val format: String,
val fileData: ByteReadPacket
val data: ByteReadPacket
) {
val fileSize: Long = fileData.remaining
val fileSize: Long = data.remaining
val id: ImageId by lazy { ImageId("{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format") }
val groupImageId: ImageId by lazy { ImageId("{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format") }
override fun toString(): String = "[PlatformImage(${width}x${height} $format)]"
override fun toString(): String = "[BufferedImage(${width}x${height} $format)]"
}
private operator fun ByteArray.get(range: IntRange): String = buildString {
......@@ -22,7 +22,3 @@ private operator fun ByteArray.get(range: IntRange): String = buildString {
append(this@get[it].toUHexString())
}
}
\ No newline at end of file
expect val PlatformImage.imageWidth: Int
expect val PlatformImage.imageHeight: Int
\ No newline at end of file
......@@ -3,17 +3,19 @@
package net.mamoe.mirai.utils
import kotlinx.io.core.buildPacket
import java.awt.image.BufferedImage
import kotlinx.io.streams.writePacket
import java.io.InputStream
import java.io.OutputStream
import java.security.MessageDigest
import javax.imageio.ImageIO
import java.awt.image.BufferedImage as JavaBufferedImage
fun BufferedImage.toPlatformImage(formatName: String = "png"): PlatformImage {
fun JavaBufferedImage.toMiraiImage(formatName: String = "png"): BufferedImage {
val digest = MessageDigest.getInstance("md5")
digest.reset()
val buffer = buildPacket {
ImageIO.write(this@toPlatformImage, formatName, object : OutputStream() {
ImageIO.write(this@toMiraiImage, formatName, object : OutputStream() {
override fun write(b: Int) {
b.toByte().let {
this@buildPacket.writeByte(it)
......@@ -23,9 +25,18 @@ fun BufferedImage.toPlatformImage(formatName: String = "png"): PlatformImage {
})
}
return PlatformImage(this.width, this.height, digest.digest(), formatName, buffer)
return BufferedImage(width, height, digest.digest(), formatName, buffer)
}
actual val PlatformImage.imageWidth: Int get() = this.width
fun BufferedImage.toJavaImage(): JavaBufferedImage = ImageIO.read(object : InputStream() {
override fun read(): Int = with(this@toJavaImage.data) {
if (remaining != 0L)
readByte().toInt()
else -1
}
})
actual val PlatformImage.imageHeight: Int get() = this.height
\ No newline at end of file
/**
* 将缓存的图片写入流. 注意, 写入后缓存将会被清空.
*/
fun OutputStream.writeImage(image: BufferedImage) = this.writePacket(image.data)
\ No newline at end of file
......@@ -5,8 +5,8 @@ package net.mamoe.mirai.utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.streams.writePacket
import org.jsoup.Connection
import java.io.OutputStream
import java.net.HttpURLConnection
import java.net.InetAddress
import java.net.URL
......@@ -118,11 +118,3 @@ actual suspend fun httpPostGroupImage(uKeyHex: String, fileSize: Long, imageData
private suspend fun Connection.suspendExecute(): Connection.Response = withContext(Dispatchers.IO) {
execute()
}
\ No newline at end of file
private fun OutputStream.writePacket(packet: ByteReadPacket) {
val byteArray = ByteArray(1)
repeat(packet.remaining.toInt()) {
packet.readAvailable(byteArray)
this.write(byteArray)
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@
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.toMiraiImage
import net.mamoe.mirai.utils.toUHexString
import java.io.File
import javax.imageio.ImageIO
......@@ -11,7 +11,7 @@ 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")
val image = ImageIO.read(File("C:\\Users\\Him18\\Desktop\\test2.png").readBytes().inputStream()).toMiraiImage("png")
// File("C:\\Users\\Him18\\Desktop\\test2.jpg").writeBytes(image.fileData.readBytes())
GroupImageIdRequestPacket(
......
......@@ -81,18 +81,18 @@ suspend fun main() {
"上传好友图片" in it.message -> withTimeoutOrNull(3000) {
val id = QQ(bot, 1040400290u)
.uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\色图.jpg").readBytes().inputStream()) }.toPlatformImage("png"))
.uploadImage(withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\色图.jpg").readBytes().inputStream()) }.toMiraiImage("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\\色图.jpg").readBytes().inputStream()) }.toPlatformImage("png")
val image = withContext(Dispatchers.IO) { ImageIO.read(File("C:\\Users\\Him18\\Desktop\\色图.jpg").readBytes().inputStream()) }.toMiraiImage("png")
Group(bot, 580266363u).uploadImage(image)
it.reply(image.id.value)
it.reply(image.groupImageId.value)
delay(1000)
Group(bot, 580266363u).sendMessage(Image(image.id))
Group(bot, 580266363u).sendMessage(Image(image.groupImageId))
}
"发群图片" in it.message -> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment