Commit 0a471e9b authored by Him188's avatar Him188

Add extensions for images

parent 47ece90d
package net.mamoe.mirai package net.mamoe.mirai
/**
* Mirai 的一些信息.
*
* @see MiraiEnvironment 环境信息
*/
object Mirai { object Mirai {
const val VERSION: String = "1.0.0" const val VERSION: String = "1.0.0"
} }
\ No newline at end of file
package net.mamoe.mirai
expect object MiraiEnvironment
\ No newline at end of file
...@@ -26,12 +26,12 @@ sealed class Contact(val bot: Bot, val id: UInt) { ...@@ -26,12 +26,12 @@ sealed class Contact(val bot: Bot, val id: UInt) {
abstract suspend fun sendMessage(message: MessageChain) abstract suspend fun sendMessage(message: MessageChain)
suspend fun sendMessage(message: Message) = sendMessage(message.toChain())
suspend fun sendMessage(plain: String) = sendMessage(PlainText(plain))
abstract suspend fun sendXMLMessage(message: String) abstract suspend fun sendXMLMessage(message: String)
} }
suspend fun Contact.sendMessage(plain: String) = sendMessage(PlainText(plain))
suspend fun Contact.sendMessage(message: Message) = sendMessage(message.toChain())
/** /**
* 一般的用户可见的 ID. * 一般的用户可见的 ID.
......
...@@ -2,6 +2,7 @@ package net.mamoe.mirai.event.events ...@@ -2,6 +2,7 @@ package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
......
...@@ -3,6 +3,7 @@ package net.mamoe.mirai.event.events ...@@ -3,6 +3,7 @@ package net.mamoe.mirai.event.events
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
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.contact.sendMessage
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.network.protocol.tim.packet.event.SenderPermission import net.mamoe.mirai.network.protocol.tim.packet.event.SenderPermission
......
...@@ -4,6 +4,7 @@ package net.mamoe.mirai.message ...@@ -4,6 +4,7 @@ package net.mamoe.mirai.message
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
...@@ -78,6 +79,11 @@ interface Message { ...@@ -78,6 +79,11 @@ interface Message {
infix operator fun plus(another: Number): MessageChain = this.concat(another.toString().toMessage()) infix operator fun plus(another: Number): MessageChain = this.concat(another.toString().toMessage())
} }
/**
* 将 [this] 发送给指定联系人
*/
suspend fun Message.sendTo(contact: Contact) = contact.sendMessage(this)
// ==================================== PlainText ==================================== // ==================================== PlainText ====================================
inline class PlainText(override val stringValue: String) : Message { inline class PlainText(override val stringValue: String) : Message {
...@@ -91,8 +97,6 @@ inline class PlainText(override val stringValue: String) : Message { ...@@ -91,8 +97,6 @@ inline class PlainText(override val stringValue: String) : Message {
* 由接收消息时构建, 可直接发送 * 由接收消息时构建, 可直接发送
* *
* @param id 这个图片的 [ImageId] * @param id 这个图片的 [ImageId]
*
* @see
*/ */
inline class Image(val id: ImageId) : Message { inline class Image(val id: ImageId) : Message {
override val stringValue: String get() = "[${id.value}]" override val stringValue: String get() = "[${id.value}]"
...@@ -108,6 +112,10 @@ inline class Image(val id: ImageId) : Message { ...@@ -108,6 +112,10 @@ inline class Image(val id: ImageId) : Message {
*/ */
inline class ImageId(val value: String) inline class ImageId(val value: String)
fun ImageId.image(): Image = Image(this)
suspend fun ImageId.sendTo(contact: Contact) = contact.sendMessage(this.image())
// ==================================== At ==================================== // ==================================== At ====================================
/** /**
......
...@@ -10,23 +10,25 @@ import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket ...@@ -10,23 +10,25 @@ import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket import net.mamoe.mirai.network.protocol.tim.packet.ResponsePacket
import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImageIdRequestPacket.Response.State.*
import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.httpPostFriendImage import net.mamoe.mirai.utils.httpPostFriendImage
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.readUnsignedVarInt import net.mamoe.mirai.utils.readUnsignedVarInt
import net.mamoe.mirai.utils.writeUVarInt import net.mamoe.mirai.utils.writeUVarInt
import net.mamoe.mirai.withSession
/** /**
* 上传图片 * 上传图片
* 挂起直到上传完成或失败
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/ */
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.session) { suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession {
//SubmitImageFilenamePacket(account, account, "sdiovaoidsa.png", sessionKey).sendAndExpect<ServerSubmitImageFilenameResponsePacket>().join() FriendImageIdRequestPacket(qqAccount, sessionKey, id, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
DebugLogger.logPurple("正在上传好友图片, md5=${image.md5.toUHexString()}") when (it.state) {
return FriendImageIdRequestPacket(this.qqAccount, sessionKey, id, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> { REQUIRE_UPLOAD -> {
if (it.uKey != null)
require( require(
httpPostFriendImage( httpPostFriendImage(
botAccount = bot.qqAccount, botAccount = bot.qqAccount,
...@@ -35,6 +37,17 @@ suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.ses ...@@ -35,6 +37,17 @@ suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.ses
inputSize = image.inputSize inputSize = image.inputSize
) )
) )
}
ALREADY_EXISTS -> {
}
OVER_FILE_SIZE_MAX -> {
throw OverFileSizeMaxException()
}
}
it.imageId!! it.imageId!!
}.await() }.await()
} }
...@@ -110,7 +123,7 @@ class SubmitImageFilenamePacket( ...@@ -110,7 +123,7 @@ class SubmitImageFilenamePacket(
@PacketId(0x03_52u) @PacketId(0x03_52u)
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173") @PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
class FriendImageIdRequestPacket( class FriendImageIdRequestPacket(
private val botNumber: UInt, private val bot: UInt,
private val sessionKey: ByteArray, private val sessionKey: ByteArray,
private val target: UInt, private val target: UInt,
private val image: ExternalImage private val image: ExternalImage
...@@ -119,7 +132,7 @@ class FriendImageIdRequestPacket( ...@@ -119,7 +132,7 @@ class FriendImageIdRequestPacket(
//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 //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
override fun encode(builder: BytePacketBuilder) = with(builder) { override fun encode(builder: BytePacketBuilder) = with(builder) {
writeQQ(botNumber) writeQQ(bot)
//04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00 //04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00
writeHex("04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00") writeHex("04 00 00 00 01 2E 01 00 00 69 35 00 00 00 00 00 00 00 00")
...@@ -209,7 +222,7 @@ class FriendImageIdRequestPacket( ...@@ -209,7 +222,7 @@ class FriendImageIdRequestPacket(
writeUVarintLVPacket(tag = 0x12u, lengthOffset = { it + 1 }) { writeUVarintLVPacket(tag = 0x12u, lengthOffset = { it + 1 }) {
writeUByte(0x08u) writeUByte(0x08u)
writeUVarInt(botNumber) writeUVarInt(bot)
writeUByte(0x10u) writeUByte(0x10u)
writeUVarInt(target) writeUVarInt(target)
...@@ -321,11 +334,11 @@ class FriendImageIdRequestPacket( ...@@ -321,11 +334,11 @@ class FriendImageIdRequestPacket(
//83 12 06 98 01 01 A0 01 00 08 01 12 7D 08 00 10 9B A4 DC 92 06 18 00 28 01 32 1B 0A 10 8E C4 9D 72 26 AE 20 C0 5D A2 B6 78 4D 12 B7 3A 10 00 18 86 1F 20 30 28 30 52 25 2F 30 31 62 //83 12 06 98 01 01 A0 01 00 08 01 12 7D 08 00 10 9B A4 DC 92 06 18 00 28 01 32 1B 0A 10 8E C4 9D 72 26 AE 20 C0 5D A2 B6 78 4D 12 B7 3A 10 00 18 86 1F 20 30 28 30 52 25 2F 30 31 62
val toDiscard = readUByte().toInt() - 37 val toDiscard = readUByte().toInt() - 37
if (toDiscard < 0) { if (toDiscard < 0) {
state = State.OVER_FILE_SIZE_MAX state = OVER_FILE_SIZE_MAX
} else { } else {
discardExact(toDiscard) discardExact(toDiscard)
imageId = ImageId(readString(37)) imageId = ImageId(readString(37))
state = State.ALREADY_EXISTS state = ALREADY_EXISTS
} }
} }
} }
......
...@@ -7,6 +7,7 @@ import net.mamoe.mirai.contact.Group ...@@ -7,6 +7,7 @@ import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.GroupId import net.mamoe.mirai.contact.GroupId
import net.mamoe.mirai.contact.GroupInternalId import net.mamoe.mirai.contact.GroupInternalId
import net.mamoe.mirai.contact.withSession import net.mamoe.mirai.contact.withSession
import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
...@@ -25,11 +26,9 @@ class OverFileSizeMaxException : IllegalStateException() ...@@ -25,11 +26,9 @@ class OverFileSizeMaxException : IllegalStateException()
/** /**
* 上传群图片 * 上传群图片
* 挂起直到上传完成或失败 * 挂起直到上传完成或失败
* 失败后抛出 [OverFileSizeMaxException] * @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
*/ */
suspend fun Group.uploadImage( suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
image: ExternalImage
) = withSession {
GroupImageIdRequestPacket(bot.qqAccount, internalId, image, sessionKey) GroupImageIdRequestPacket(bot.qqAccount, internalId, image, sessionKey)
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> { .sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
when (it.state) { when (it.state) {
...@@ -50,6 +49,7 @@ suspend fun Group.uploadImage( ...@@ -50,6 +49,7 @@ suspend fun Group.uploadImage(
GroupImageIdRequestPacket.Response.State.OVER_FILE_SIZE_MAX -> throw OverFileSizeMaxException() GroupImageIdRequestPacket.Response.State.OVER_FILE_SIZE_MAX -> throw OverFileSizeMaxException()
} }
}.join() }.join()
image.groupImageId
} }
/** /**
......
package net.mamoe.mirai.utils
import kotlinx.io.pool.DefaultPool
import kotlinx.io.pool.ObjectPool
internal const val DEFAULT_BUFFER_SIZE = 4098
internal const val DEFAULT_BYTE_ARRAY_POOL_SIZE = 2048
/**
* The default ktor byte buffer pool
*/
val ByteArrayPool: ObjectPool<ByteArray> = ByteBufferPool()
class ByteBufferPool : DefaultPool<ByteArray>(DEFAULT_BYTE_ARRAY_POOL_SIZE) {
override fun produceInstance(): ByteArray = ByteArray(DEFAULT_BUFFER_SIZE)
override fun clearInstance(instance: ByteArray): ByteArray = instance.apply { map { 0 } }
}
...@@ -4,8 +4,14 @@ package net.mamoe.mirai.utils ...@@ -4,8 +4,14 @@ package net.mamoe.mirai.utils
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input 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.ImageId import net.mamoe.mirai.message.ImageId
import net.mamoe.mirai.message.sendTo
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
@Suppress("FunctionName")
fun ExternalImage( fun ExternalImage(
width: Int, width: Int,
height: Int, height: Int,
...@@ -14,6 +20,10 @@ fun ExternalImage( ...@@ -14,6 +20,10 @@ fun ExternalImage(
data: ByteReadPacket data: ByteReadPacket
) = ExternalImage(width, height, md5, format, data, data.remaining) ) = ExternalImage(width, height, md5, format, data, data.remaining)
/**
* 外部图片. 图片数据还没有读取到内存.
* @see ExternalImage.sendTo
*/
class ExternalImage( class ExternalImage(
val width: Int, val width: Int,
val height: Int, val height: Int,
...@@ -40,6 +50,19 @@ class ExternalImage( ...@@ -40,6 +50,19 @@ class ExternalImage(
override fun toString(): String = "[ExternalImage(${width}x$height $format)]" override fun toString(): String = "[ExternalImage(${width}x$height $format)]"
} }
/**
* 将图片发送给指定联系人
*/
suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
is Group -> contact.uploadImage(this).sendTo(contact)
is QQ -> contact.uploadImage(this).sendTo(contact)
}
/**
* 将图片发送给 [this]
*/
suspend fun Contact.sendMessage(image: ExternalImage) = image.sendTo(this)
private operator fun ByteArray.get(range: IntRange): String = buildString { private operator fun ByteArray.get(range: IntRange): String = buildString {
range.forEach { range.forEach {
append(this@get[it].toUHexString()) append(this@get[it].toUHexString())
......
...@@ -54,6 +54,7 @@ fun String.hexToUBytes(): UByteArray = HexCache.hexToUBytes(this) ...@@ -54,6 +54,7 @@ fun String.hexToUBytes(): UByteArray = HexCache.hexToUBytes(this)
fun String.hexToInt(): Int = hexToBytes().toUInt().toInt() fun String.hexToInt(): Int = hexToBytes().toUInt().toInt()
fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() } fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Random.nextInt(0..255).toByte() }
fun getRandomString(length: Int): String = getRandomString(length, 'a'..'z', 'A'..'Z', '0'..'9')
fun getRandomString(length: Int, charRange: CharRange): String = String(CharArray(length) { charRange.random() }) fun getRandomString(length: Int, charRange: CharRange): String = String(CharArray(length) { charRange.random() })
fun getRandomString(length: Int, vararg charRanges: CharRange): String = String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() }) fun getRandomString(length: Int, vararg charRanges: CharRange): String = String(CharArray(length) { charRanges[Random.Default.nextInt(0..charRanges.lastIndex)].random() })
fun ByteArray.toUInt(): UInt = this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0) fun ByteArray.toUInt(): UInt = this[0].toUInt().and(255u).shl(24) + this[1].toUInt().and(255u).shl(16) + this[2].toUInt().and(255u).shl(8) + this[3].toUInt().and(255u).shl(0)
......
@file:Suppress("MayBeConstant", "unused")
package net.mamoe.mirai
import java.io.File
actual typealias MiraiEnvironment = MiraiEnvironmentJvm
object MiraiEnvironmentJvm {
/**
* JVM only, 临时文件夹
*/
val TEMP_DIR: File = createTempDir().apply { deleteOnExit() }
}
\ No newline at end of file
@file:Suppress("unused")
package net.mamoe.mirai.message
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.io.core.Input
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.network.protocol.tim.packet.action.OverFileSizeMaxException
import net.mamoe.mirai.utils.sendTo
import net.mamoe.mirai.utils.toExternalImage
import java.awt.image.BufferedImage
import java.io.File
import java.io.InputStream
import java.net.URL
/*
* 发送图片的一些扩展函数.
*/
// region Type extensions
/**
* 将图片发送到指定联系人. 不会保存临时文件
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun BufferedImage.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/**
* 下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/**
* 读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/**
* 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun InputStream.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
/**
* 将文件作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun File.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact)
// endregion
// region Contact extensions
/**
* 将图片发送到指定联系人. 不会保存临时文件
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Contact.sendImage(bufferedImage: BufferedImage) = bufferedImage.sendAsImageTo(this)
/**
* 下载 [URL] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this)
/**
* 读取 [Input] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this)
/**
* 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this)
/**
* 将文件作为图片发送到指定联系人
* @throws OverFileSizeMaxException
*/
@Throws(OverFileSizeMaxException::class)
suspend fun Contact.sendImage(file: File) = file.sendAsImageTo(this)
// endregion
\ No newline at end of file
...@@ -2,16 +2,31 @@ ...@@ -2,16 +2,31 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import io.ktor.util.asStream
import kotlinx.io.core.Input
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.errors.IOException
import kotlinx.io.streams.asInput import kotlinx.io.streams.asInput
import java.awt.image.BufferedImage
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.net.URL
import java.security.MessageDigest import java.security.MessageDigest
import javax.imageio.ImageIO import javax.imageio.ImageIO
import java.awt.image.BufferedImage as JavaBufferedImage
fun JavaBufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage { /*
* 将各类型图片容器转为 [ExternalImage]
*/
/**
* 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage]
*/
@Throws(IOException::class)
fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage {
val digest = MessageDigest.getInstance("md5") val digest = MessageDigest.getInstance("md5")
digest.reset() digest.reset()
...@@ -29,6 +44,10 @@ fun JavaBufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage ...@@ -29,6 +44,10 @@ fun JavaBufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage
return ExternalImage(width, height, digest.digest(), formatName, buffer) return ExternalImage(width, height, digest.digest(), formatName, buffer)
} }
/**
* 读取文件头识别图片属性, 然后构造 [ExternalImage]
*/
@Throws(IOException::class)
fun File.toExternalImage(): ExternalImage { fun File.toExternalImage(): ExternalImage {
val input = ImageIO.createImageInputStream(this) val input = ImageIO.createImageInputStream(this)
val image = ImageIO.getImageReaders(input).asSequence().firstOrNull() ?: error("Unable to read file(${this.path}), no ImageReader found") val image = ImageIO.getImageReaders(input).asSequence().firstOrNull() ?: error("Unable to read file(${this.path}), no ImageReader found")
...@@ -37,9 +56,39 @@ fun File.toExternalImage(): ExternalImage { ...@@ -37,9 +56,39 @@ fun File.toExternalImage(): ExternalImage {
return ExternalImage( return ExternalImage(
width = image.getWidth(0), width = image.getWidth(0),
height = image.getHeight(0), height = image.getHeight(0),
md5 = this.md5(), md5 = input.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()
) )
} }
/**
* 下载文件到临时目录然后调用 [File.toExternalImage]
*/
@Throws(IOException::class)
fun URL.toExternalImage(): ExternalImage {
val file = createTempFile().apply { deleteOnExit() }
openStream().transferTo(FileOutputStream(file))
return file.toExternalImage()
}
/**
* 保存为临时文件然后调用 [File.toExternalImage]
*/
@Throws(IOException::class)
fun InputStream.toExternalImage(): ExternalImage {
val file = createTempFile().apply { deleteOnExit() }
this.transferTo(FileOutputStream(file))
return file.toExternalImage()
}
/**
* 保存为临时文件然后调用 [File.toExternalImage]
*/
@Throws(IOException::class)
fun Input.toExternalImage(): ExternalImage {
val file = createTempFile().apply { deleteOnExit() }
this.asStream().transferTo(FileOutputStream(file))
return file.toExternalImage()
}
\ No newline at end of file
...@@ -10,7 +10,9 @@ import io.ktor.http.content.OutgoingContent ...@@ -10,7 +10,9 @@ import io.ktor.http.content.OutgoingContent
import kotlinx.coroutines.io.ByteWriteChannel import kotlinx.coroutines.io.ByteWriteChannel
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.readFully import kotlinx.io.core.readFully
import java.io.File import java.io.DataInput
import java.io.EOFException
import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.net.InetAddress import java.net.InetAddress
import java.security.MessageDigest import java.security.MessageDigest
...@@ -26,10 +28,10 @@ actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.t ...@@ -26,10 +28,10 @@ actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.t
actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray) actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
fun File.md5(): ByteArray { fun InputStream.md5(): ByteArray {
val digest = MessageDigest.getInstance("md5") val digest = MessageDigest.getInstance("md5")
digest.reset() digest.reset()
this.inputStream().transferTo(object : OutputStream() { this.transferTo(object : OutputStream() {
override fun write(b: Int) { override fun write(b: Int) {
b.toByte().let { b.toByte().let {
digest.update(it) digest.update(it)
...@@ -39,6 +41,21 @@ fun File.md5(): ByteArray { ...@@ -39,6 +41,21 @@ fun File.md5(): ByteArray {
return digest.digest() return digest.digest()
} }
fun DataInput.md5(): ByteArray {
val digest = MessageDigest.getInstance("md5")
digest.reset()
val buffer = byteArrayOf(1)
while (true) {
try {
this.readFully(buffer)
} catch (e: EOFException) {
break
}
digest.update(buffer[0])
}
return digest.digest()
}
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress
......
...@@ -6,6 +6,7 @@ import kotlinx.coroutines.delay ...@@ -6,6 +6,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.event.events.FriendMessageEvent import net.mamoe.mirai.event.events.FriendMessageEvent
import net.mamoe.mirai.event.events.GroupMessageEvent import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.subscribeAll import net.mamoe.mirai.event.subscribeAll
......
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