Commit a776b25b authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

parents df24d101 b240370f
package net.mamoe.mirai.qqandroid package net.mamoe.mirai.qqandroid
import kotlinx.io.core.readBytes
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendNameRemark import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.PreviousNameList import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.NotOnlineImageFromFile
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.network.highway.Highway
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.withUse
import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.io.PlatformSocket
import net.mamoe.mirai.utils.io.toUHexString
import net.mamoe.mirai.utils.unsafeWeakRef import net.mamoe.mirai.utils.unsafeWeakRef
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
...@@ -116,7 +127,73 @@ internal class GroupImpl( ...@@ -116,7 +127,73 @@ internal class GroupImpl(
} }
override suspend fun uploadImage(image: ExternalImage): Image { override suspend fun uploadImage(image: ExternalImage): Image {
TODO("not implemented") bot.network.run {
val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp(
bot.client,
uin = bot.uin,
groupCode = id,
md5 = image.md5,
size = image.inputSize,
picWidth = image.width,
picHeight = image.height,
picType = image.imageType,
filename = image.filename
).sendAndExpect()
when (response) {
is ImgStore.GroupPicUp.Response.Failed -> error("upload group image failed with reason ${response.message}")
is ImgStore.GroupPicUp.Response.FileExists -> {
val resourceId = image.calculateImageResourceId()
return NotOnlineImageFromFile(
resourceId = resourceId,
md5 = response.fileInfo.fileMd5,
filepath = resourceId,
fileLength = response.fileInfo.fileSize.toInt(),
height = response.fileInfo.fileHeight,
width = response.fileInfo.fileWidth,
imageType = response.fileInfo.fileType
)
}
is ImgStore.GroupPicUp.Response.RequireUpload -> {
val socket = PlatformSocket()
socket.connect(response.uploadIpList.first().toIpV4AddressString().also { println("serverIp=$it") }, response.uploadPortList.first())
// socket.use {
socket.send(
Highway.RequestDataTrans(
uin = bot.uin,
command = "PicUp.DataUp",
buildVer = bot.client.buildVer,
uKey = response.uKey,
data = image.input,
dataSize = image.inputSize.toInt(),
md5 = image.md5,
sequenceId = bot.client.nextHighwayDataTransSequenceId()
)
)
// }
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00
socket.read().withUse {
readByte()
val headLength = readInt()
val bodyLength = readInt()
val proto = readProtoBuf(CSDataHighwayHead.RspDataHighwayHead.serializer(), length = headLength)
println(proto.contentToString())
println(readBytes(bodyLength).toUHexString())
}
val resourceId = image.calculateImageResourceId()
return NotOnlineImageFromFile(
resourceId = resourceId,
md5 = image.md5,
filepath = resourceId,
fileLength = image.inputSize.toInt(),
height = image.height,
width = image.width,
imageType = image.imageType
)
}
}
}
} }
} }
\ No newline at end of file
...@@ -95,7 +95,8 @@ internal open class QQAndroidClient( ...@@ -95,7 +95,8 @@ internal open class QQAndroidClient(
var openAppId: Long = 715019303L var openAppId: Long = 715019303L
val apkVersionName: ByteArray = "8.2.0".toByteArray() val apkVersionName: ByteArray get() = "8.2.0".toByteArray()
val buildVer: String get() = "8.2.0.1296"
private val messageSequenceId: AtomicInt = atomic(0) private val messageSequenceId: AtomicInt = atomic(0)
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2) internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
...@@ -103,6 +104,9 @@ internal open class QQAndroidClient( ...@@ -103,6 +104,9 @@ internal open class QQAndroidClient(
private val requestPacketRequestId: AtomicInt = atomic(1921334513) private val requestPacketRequestId: AtomicInt = atomic(1921334513)
internal fun nextRequestPacketRequestId(): Int = requestPacketRequestId.getAndAdd(2) internal fun nextRequestPacketRequestId(): Int = requestPacketRequestId.getAndAdd(2)
private val highwayDataTransSequenceId: AtomicInt = atomic(87017)
internal fun nextHighwayDataTransSequenceId(): Int = highwayDataTransSequenceId.getAndAdd(2)
val appClientVersion: Int = 0 val appClientVersion: Int = 0
var networkType: NetworkType = NetworkType.WIFI var networkType: NetworkType = NetworkType.WIFI
......
package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.URLProtocol
import io.ktor.http.content.OutgoingContent
import io.ktor.http.userAgent
import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.io.ByteArrayPool
@Suppress("SpellCheckingInspection")
internal suspend inline fun HttpClient.postImage(
htcmd: String,
uin: Long,
groupcode: Long?,
imageInput: Input,
inputSize: Long,
uKeyHex: String
): Boolean = try {
post<HttpStatusCode> {
url {
protocol = URLProtocol.HTTP
host = "htdata2.qq.com"
path("cgi-bin/httpconn")
parameters["htcmd"] = htcmd
parameters["uin"] = uin.toString()
if (groupcode != null) parameters["groupcode"] = groupcode.toString()
parameters["term"] = "pc"
parameters["ver"] = "5603"
parameters["filesize"] = inputSize.toString()
parameters["range"] = 0.toString()
parameters["ukey"] = uKeyHex
userAgent("QQClient")
}
body = object : OutgoingContent.WriteChannelContent() {
override val contentType: ContentType = ContentType.Image.Any
override val contentLength: Long = inputSize
override suspend fun writeTo(channel: ByteWriteChannel) {
ByteArrayPool.useInstance { buffer: ByteArray ->
var size: Int
while (imageInput.readAvailable(buffer).also { size = it } != 0) {
channel.writeFully(buffer, 0, size)
}
}
}
}
} == HttpStatusCode.OK
} finally {
imageInput.close()
}
object Highway {
fun RequestDataTrans(
uin: Long,
command: String,
sequenceId: Int,
buildVer: String,
appId: Int = 537062845,
dataFlag: Int = 4096,
commandId: Int = 2,
localId: Int = 2052,
uKey: ByteArray,
data: Input,
dataSize: Int,
md5: ByteArray
): ByteReadPacket {
val dataHighwayHead = CSDataHighwayHead.DataHighwayHead(
version = 1,
uin = uin.toString(),
command = command,
seq = sequenceId,
retryTimes = 0,
appid = appId,
dataflag = dataFlag,
commandId = commandId,
buildVer = buildVer,
localeId = localId
)
val segHead = CSDataHighwayHead.SegHead(
datalength = dataSize,
filesize = dataSize.toLong() and 0xFFffFFff,
serviceticket = uKey,
md5 = md5,
fileMd5 = md5
)
return Codec.buildC2SData(dataHighwayHead, segHead, EMPTY_BYTE_ARRAY, null, data, dataSize)
}
private object Codec {
fun buildC2SData(
dataHighwayHead: CSDataHighwayHead.DataHighwayHead,
segHead: CSDataHighwayHead.SegHead,
extendInfo: ByteArray,
loginSigHead: CSDataHighwayHead.LoginSigHead?,
body: Input,
bodySize: Int
): ByteReadPacket {
val head = CSDataHighwayHead.ReqDataHighwayHead(
msgBasehead = dataHighwayHead,
msgSeghead = segHead,
reqExtendinfo = extendInfo,
msgLoginSigHead = loginSigHead
).toByteArray(CSDataHighwayHead.ReqDataHighwayHead.serializer())
return buildPacket {
writeByte(40)
writeInt(head.size)
writeInt(bodySize)
writeFully(head)
body.copyTo(this)
writeByte(41)
}
}
}
}
\ No newline at end of file
...@@ -5,7 +5,9 @@ import kotlinx.io.pool.useInstance ...@@ -5,7 +5,9 @@ import kotlinx.io.pool.useInstance
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImageUpPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
...@@ -119,6 +121,9 @@ internal object KnownPacketFactories { ...@@ -119,6 +121,9 @@ internal object KnownPacketFactories {
FriendList.GetFriendGroupList, FriendList.GetFriendGroupList,
FriendList.GetTroopListSimplify, FriendList.GetTroopListSimplify,
FriendList.GetTroopMemberList, FriendList.GetTroopMemberList,
ImgStore.GroupPicUp,
ImageUpPacket,
LongConn.OffPicDown,
TroopManagement.EditNametag, TroopManagement.EditNametag,
TroopManagement.Mute, TroopManagement.Mute,
TroopManagement.MuteAll TroopManagement.MuteAll
......
...@@ -10,15 +10,13 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352Packet ...@@ -10,15 +10,13 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352Packet
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.UploadImgReq import net.mamoe.mirai.qqandroid.network.protocol.data.proto.UploadImgReq
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
internal object ImageUpPacket : OutgoingPacketFactory<ImageUpPacket.ImageUpPacketResponse>("LongConn.OffPicUp") { internal object ImageUpPacket : OutgoingPacketFactory<ImageUpPacket.ImageUpPacketResponse>("LongConn.OffPicUp") {
operator fun invoke(client: QQAndroidClient, req: UploadImgReq): OutgoingPacket { operator fun invoke(client: QQAndroidClient, req: UploadImgReq): OutgoingPacket {
// TODO: 2020/1/24 测试: bodyType, subAppId // TODO: 2020/1/24 测试: bodyType, subAppId
return buildLoginOutgoingPacket(client, key = client.wLoginSigInfo.d2Key, bodyType = 1) { return buildOutgoingUniPacket(client) {
writeSsoPacket(client, subAppId = 0, commandName = commandName, sequenceId = it) {
val data = ProtoBufWithNullableSupport.dump( val data = ProtoBufWithNullableSupport.dump(
Cmd0x352Packet.serializer(), Cmd0x352Packet.serializer(),
Cmd0x352Packet.createByImageRequest(req) Cmd0x352Packet.createByImageRequest(req)
...@@ -27,7 +25,6 @@ internal object ImageUpPacket : OutgoingPacketFactory<ImageUpPacket.ImageUpPacke ...@@ -27,7 +25,6 @@ internal object ImageUpPacket : OutgoingPacketFactory<ImageUpPacket.ImageUpPacke
writeFully(data) writeFully(data)
} }
} }
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): ImageUpPacketResponse { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): ImageUpPacketResponse {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
......
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image
import io.ktor.client.HttpClient
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.io.serialization.writeProtoBuf
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x388
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
internal class ImgStore {
object GroupPicUp : OutgoingPacketFactory<GroupPicUp.Response>("ImgStore.GroupPicUp") {
operator fun invoke(
client: QQAndroidClient,
uin: Long,
groupCode: Long,
md5: ByteArray,
size: Long,
picWidth: Int,
picHeight: Int,
picType: Int = 1000,
fileId: Long = 0,
filename: String,
srcTerm: Int = 5,
platformType: Int = 9,
buType: Int = 1,
appPicType: Int = 1006,
originalPic: Int = 0
): OutgoingPacket = buildOutgoingUniPacket(client) {
writeProtoBuf(
Cmd0x388.ReqBody.serializer(),
Cmd0x388.ReqBody(
netType = 3, // wifi
subcmd = 1,
msgTryupImgReq = listOf(
Cmd0x388.TryUpImgReq(
groupCode = groupCode,
srcUin = uin,
fileMd5 = md5,
fileSize = size,
fileId = fileId,
fileName = filename,
picWidth = picWidth,
picHeight = picHeight,
picType = picType,
appPicType = appPicType,
buildVer = client.buildVer,
srcTerm = srcTerm,
platformType = platformType,
originalPic = originalPic,
buType = buType
)
)
)
)
}
sealed class Response : Packet {
class FileExists(
val fileId: Long,
val fileInfo: Cmd0x388.ImgInfo
) : Response() {
override fun toString(): String {
return "FileExists(fileId=$fileId, fileInfo=$fileInfo)"
}
}
class RequireUpload(
val fileId: Long,
val uKey: ByteArray,
val uploadIpList: List<Int>,
val uploadPortList: List<Int>
) : Response() {
override fun toString(): String {
return "RequireUpload(fileId=$fileId, uKey=${uKey.contentToString()})"
}
}
data class Failed(
val resultCode: Int,
val message: String
) : Response()
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
val resp0 = readProtoBuf(Cmd0x388.RspBody.serializer())
resp0.msgTryupImgRsp ?: error("cannot find `msgTryupImgRsp` from `Cmd0x388.RspBody`")
val resp = resp0.msgTryupImgRsp.first()
return when {
resp.result != 0 -> Response.Failed(resultCode = resp.result, message = resp.failMsg)
resp.boolFileExit -> Response.FileExists(fileId = resp.fileid, fileInfo = resp.msgImgInfo!!)
else -> Response.RequireUpload(fileId = resp.fileid, uKey = resp.upUkey, uploadIpList = resp.uint32UpIp!!, uploadPortList = resp.uint32UpPort!!)
}
}
}
}
\ No newline at end of file
...@@ -13,8 +13,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory ...@@ -13,8 +13,9 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
internal object ImageDownPacket : OutgoingPacketFactory<ImageDownPacket.ImageDownPacketResponse>("LongConn.OffPicDown") { internal class LongConn {
object OffPicDown : OutgoingPacketFactory<OffPicDown.ImageDownPacketResponse>("LongConn.OffPicDown"){
operator fun invoke(client: QQAndroidClient, req: GetImgUrlReq): OutgoingPacket { operator fun invoke(client: QQAndroidClient, req: GetImgUrlReq): OutgoingPacket {
// TODO: 2020/1/24 测试: bodyType, subAppId // TODO: 2020/1/24 测试: bodyType, subAppId
return buildLoginOutgoingPacket(client, key = client.wLoginSigInfo.d2Key, bodyType = 1) { return buildLoginOutgoingPacket(client, key = client.wLoginSigInfo.d2Key, bodyType = 1) {
...@@ -37,6 +38,5 @@ internal object ImageDownPacket : OutgoingPacketFactory<ImageDownPacket.ImageDow ...@@ -37,6 +38,5 @@ internal object ImageDownPacket : OutgoingPacketFactory<ImageDownPacket.ImageDow
sealed class ImageDownPacketResponse : Packet { sealed class ImageDownPacketResponse : Packet {
object Success : ImageDownPacketResponse() object Success : ImageDownPacketResponse()
} }
}
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ import java.io.File ...@@ -6,7 +6,7 @@ import java.io.File
fun main() { fun main() {
println( println(
File("""/Users/jiahua.liu/Desktop/QQAndroid-F/app/src/main/java/tencent/im/group/group_label/""") File("""/Users/jiahua.liu/Desktop/QQAndroid-F/app/src/main/java/tencent/im/s2c/msgtype0x210/submsgtype0xc7/bussinfo/mutualmark""")
.generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n") .generateUnarrangedClasses().toMutableList().arrangeClasses().joinToString("\n\n")
) )
} }
......
...@@ -31,8 +31,6 @@ interface Group : Contact, CoroutineScope { ...@@ -31,8 +31,6 @@ interface Group : Contact, CoroutineScope {
/** /**
* 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新 * 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新
*
* **注意**: 获得的列表仅为这一时刻的成员列表的镜像. 它将不会被更新
*/ */
val members: ContactList<Member> val members: ContactList<Member>
......
...@@ -5,6 +5,7 @@ package net.mamoe.mirai.message.data ...@@ -5,6 +5,7 @@ package net.mamoe.mirai.message.data
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
sealed class Image : Message { sealed class Image : Message {
abstract val filepath: String
abstract val md5: ByteArray abstract val md5: ByteArray
abstract override fun toString(): String abstract override fun toString(): String
...@@ -15,7 +16,7 @@ sealed class Image : Message { ...@@ -15,7 +16,7 @@ sealed class Image : Message {
} }
abstract class CustomFace : Image() { abstract class CustomFace : Image() {
abstract val filepath: String abstract override val filepath: String
abstract val fileId: Int abstract val fileId: Int
abstract val serverIp: Int abstract val serverIp: Int
abstract val serverPort: Int abstract val serverPort: Int
...@@ -106,7 +107,7 @@ data class CustomFaceFromFile( ...@@ -106,7 +107,7 @@ data class CustomFaceFromFile(
abstract class NotOnlineImage : Image() { abstract class NotOnlineImage : Image() {
abstract val resourceId: String abstract val resourceId: String
abstract override val md5: ByteArray abstract override val md5: ByteArray
abstract val filepath: String abstract override val filepath: String
abstract val fileLength: Int abstract val fileLength: Int
abstract val height: Int abstract val height: Int
abstract val width: Int abstract val width: Int
......
...@@ -7,18 +7,10 @@ import kotlinx.io.core.Input ...@@ -7,18 +7,10 @@ import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.sendTo
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
@Suppress("FunctionName")
fun ExternalImage(
width: Int,
height: Int,
md5: ByteArray,
format: String,
data: ByteReadPacket
): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining)
/** /**
* 外部图片. 图片数据还没有读取到内存. * 外部图片. 图片数据还没有读取到内存.
* *
...@@ -33,19 +25,53 @@ class ExternalImage( ...@@ -33,19 +25,53 @@ class ExternalImage(
val md5: ByteArray, val md5: ByteArray,
imageFormat: String, imageFormat: String,
val input: Input, val input: Input,
val inputSize: Long val inputSize: Long,
val filename: String
) { ) {
private val format: String companion object {
operator fun invoke(
width: Int,
height: Int,
md5: ByteArray,
format: String,
data: ByteReadPacket,
filename: String
): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename)
}
init { private val format: String = when (val it =imageFormat.toLowerCase()) {
if (imageFormat == "JPEG" || imageFormat == "jpeg") {//必须转换 "jpeg" -> "jpg" //必须转换
this.format = "jpg" else -> it
} else {
this.format = imageFormat
} }
/**
*
* ImgType:
* JPG: 1000
* PNG: 1001
* WEBP: 1002
* BMP: 1005
* GIG: 2000
* APNG: 2001
* SHARPP: 1004
*/
val imageType: Int
get() = when (format){
"jpg" -> 1000
"png" -> 1001
"webp" -> 1002
"bmp" -> 1005
"gig" -> 2000
"apng" -> 2001
"sharpp" -> 1004
else -> 1000 // unsupported, just make it jpg
} }
override fun toString(): String = "[ExternalImage(${width}x$height $format)]" override fun toString(): String = "[ExternalImage(${width}x$height $format)]"
fun calculateImageResourceId(): String {
return "{${md5[0..3]}-${md5[4..5]}-${md5[6..7]}-${md5[8..9]}-${md5[10..15]}}.$format"
}
} }
/** /**
......
...@@ -12,6 +12,7 @@ import kotlinx.io.core.copyTo ...@@ -12,6 +12,7 @@ import kotlinx.io.core.copyTo
import kotlinx.io.errors.IOException import kotlinx.io.errors.IOException
import kotlinx.io.streams.asInput import kotlinx.io.streams.asInput
import kotlinx.io.streams.asOutput import kotlinx.io.streams.asOutput
import net.mamoe.mirai.utils.io.getRandomString
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
...@@ -44,7 +45,7 @@ fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage { ...@@ -44,7 +45,7 @@ fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage {
}) })
} }
return ExternalImage(width, height, digest.digest(), formatName, buffer) return ExternalImage(width, height, digest.digest(), formatName, buffer, getRandomString(10) + "." + formatName)
} }
suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() } suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withContext(IO) { toExternalImage() }
...@@ -66,7 +67,8 @@ fun File.toExternalImage(): ExternalImage { ...@@ -66,7 +67,8 @@ fun File.toExternalImage(): ExternalImage {
md5 = this.inputStream().use { it.md5() }, md5 = this.inputStream().use { it.md5() },
imageFormat = image.formatName, imageFormat = image.formatName,
input = this.inputStream().asInput(IoBuffer.Pool), input = this.inputStream().asInput(IoBuffer.Pool),
inputSize = this.length() inputSize = this.length(),
filename = this.name
) )
} }
......
...@@ -15,7 +15,10 @@ dependencies { ...@@ -15,7 +15,10 @@ dependencies {
implementation 'org.jsoup:jsoup:1.12.1' implementation 'org.jsoup:jsoup:1.12.1'
} }
mainClassName = "demo.gentleman.MainKt" run{
standardInput = System.in
mainClassName = "demo.gentleman.MainKt"
}
compileKotlin { compileKotlin {
kotlinOptions { kotlinOptions {
freeCompilerArgs = ["-XXLanguage:+InlineClasses"] freeCompilerArgs = ["-XXLanguage:+InlineClasses"]
......
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