mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-25 21:23:55 +08:00
Add extensions for images
This commit is contained in:
parent
47ece90d60
commit
0a471e9b31
mirai-core/src
commonMain/kotlin/net.mamoe.mirai
jvmMain/kotlin/net/mamoe/mirai
mirai-demos/mirai-demo-1/src/main/java/demo1
@ -1,5 +1,10 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
/**
|
||||
* Mirai 的一些信息.
|
||||
*
|
||||
* @see MiraiEnvironment 环境信息
|
||||
*/
|
||||
object Mirai {
|
||||
const val VERSION: String = "1.0.0"
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package net.mamoe.mirai
|
||||
|
||||
expect object MiraiEnvironment
|
@ -26,12 +26,12 @@ sealed class Contact(val bot: Bot, val id: UInt) {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
suspend fun Contact.sendMessage(plain: String) = sendMessage(PlainText(plain))
|
||||
suspend fun Contact.sendMessage(message: Message) = sendMessage(message.toChain())
|
||||
|
||||
|
||||
/**
|
||||
* 一般的用户可见的 ID.
|
||||
|
@ -2,6 +2,7 @@ package net.mamoe.mirai.event.events
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.MessageChain
|
||||
|
||||
|
@ -3,6 +3,7 @@ package net.mamoe.mirai.event.events
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.MessageChain
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.event.SenderPermission
|
||||
|
@ -4,6 +4,7 @@ package net.mamoe.mirai.message
|
||||
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
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.utils.ExternalImage
|
||||
|
||||
@ -78,6 +79,11 @@ interface Message {
|
||||
infix operator fun plus(another: Number): MessageChain = this.concat(another.toString().toMessage())
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 [this] 发送给指定联系人
|
||||
*/
|
||||
suspend fun Message.sendTo(contact: Contact) = contact.sendMessage(this)
|
||||
|
||||
// ==================================== PlainText ====================================
|
||||
|
||||
inline class PlainText(override val stringValue: String) : Message {
|
||||
@ -91,8 +97,6 @@ inline class PlainText(override val stringValue: String) : Message {
|
||||
* 由接收消息时构建, 可直接发送
|
||||
*
|
||||
* @param id 这个图片的 [ImageId]
|
||||
*
|
||||
* @see
|
||||
*/
|
||||
inline class Image(val id: ImageId) : Message {
|
||||
override val stringValue: String get() = "[${id.value}]"
|
||||
@ -108,6 +112,10 @@ inline class Image(val id: ImageId) : Message {
|
||||
*/
|
||||
inline class ImageId(val value: String)
|
||||
|
||||
fun ImageId.image(): Image = Image(this)
|
||||
|
||||
suspend fun ImageId.sendTo(contact: Contact) = contact.sendMessage(this.image())
|
||||
|
||||
// ==================================== At ====================================
|
||||
|
||||
/**
|
||||
|
@ -10,31 +10,44 @@ 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.PacketVersion
|
||||
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.session
|
||||
import net.mamoe.mirai.qqAccount
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.httpPostFriendImage
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
import net.mamoe.mirai.utils.readUnsignedVarInt
|
||||
import net.mamoe.mirai.utils.writeUVarInt
|
||||
import net.mamoe.mirai.withSession
|
||||
|
||||
/**
|
||||
* 上传图片
|
||||
* 挂起直到上传完成或失败
|
||||
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
|
||||
*/
|
||||
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = with(bot.network.session) {
|
||||
//SubmitImageFilenamePacket(account, account, "sdiovaoidsa.png", sessionKey).sendAndExpect<ServerSubmitImageFilenameResponsePacket>().join()
|
||||
DebugLogger.logPurple("正在上传好友图片, md5=${image.md5.toUHexString()}")
|
||||
return FriendImageIdRequestPacket(this.qqAccount, sessionKey, id, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
|
||||
if (it.uKey != null)
|
||||
require(
|
||||
httpPostFriendImage(
|
||||
botAccount = bot.qqAccount,
|
||||
uKeyHex = it.uKey!!.toUHexString(""),
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize
|
||||
suspend fun QQ.uploadImage(image: ExternalImage): ImageId = bot.withSession {
|
||||
FriendImageIdRequestPacket(qqAccount, sessionKey, id, image).sendAndExpect<FriendImageIdRequestPacket.Response, ImageId> {
|
||||
when (it.state) {
|
||||
REQUIRE_UPLOAD -> {
|
||||
require(
|
||||
httpPostFriendImage(
|
||||
botAccount = bot.qqAccount,
|
||||
uKeyHex = it.uKey!!.toUHexString(""),
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ALREADY_EXISTS -> {
|
||||
|
||||
}
|
||||
|
||||
OVER_FILE_SIZE_MAX -> {
|
||||
throw OverFileSizeMaxException()
|
||||
}
|
||||
}
|
||||
|
||||
it.imageId!!
|
||||
}.await()
|
||||
}
|
||||
@ -110,7 +123,7 @@ class SubmitImageFilenamePacket(
|
||||
@PacketId(0x03_52u)
|
||||
@PacketVersion(date = "2019.10.26", timVersion = "2.3.2.21173")
|
||||
class FriendImageIdRequestPacket(
|
||||
private val botNumber: UInt,
|
||||
private val bot: UInt,
|
||||
private val sessionKey: ByteArray,
|
||||
private val target: UInt,
|
||||
private val image: ExternalImage
|
||||
@ -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
|
||||
|
||||
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
|
||||
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(
|
||||
|
||||
writeUVarintLVPacket(tag = 0x12u, lengthOffset = { it + 1 }) {
|
||||
writeUByte(0x08u)
|
||||
writeUVarInt(botNumber)
|
||||
writeUVarInt(bot)
|
||||
|
||||
writeUByte(0x10u)
|
||||
writeUVarInt(target)
|
||||
@ -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
|
||||
val toDiscard = readUByte().toInt() - 37
|
||||
if (toDiscard < 0) {
|
||||
state = State.OVER_FILE_SIZE_MAX
|
||||
state = OVER_FILE_SIZE_MAX
|
||||
} else {
|
||||
discardExact(toDiscard)
|
||||
imageId = ImageId(readString(37))
|
||||
state = State.ALREADY_EXISTS
|
||||
state = ALREADY_EXISTS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import net.mamoe.mirai.contact.Group
|
||||
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.network.protocol.tim.packet.OutgoingPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketId
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
|
||||
@ -25,11 +26,9 @@ class OverFileSizeMaxException : IllegalStateException()
|
||||
/**
|
||||
* 上传群图片
|
||||
* 挂起直到上传完成或失败
|
||||
* 失败后抛出 [OverFileSizeMaxException]
|
||||
* @throws OverFileSizeMaxException 如果文件过大, 服务器拒绝接收时
|
||||
*/
|
||||
suspend fun Group.uploadImage(
|
||||
image: ExternalImage
|
||||
) = withSession {
|
||||
suspend fun Group.uploadImage(image: ExternalImage): ImageId = withSession {
|
||||
GroupImageIdRequestPacket(bot.qqAccount, internalId, image, sessionKey)
|
||||
.sendAndExpect<GroupImageIdRequestPacket.Response, Unit> {
|
||||
when (it.state) {
|
||||
@ -50,6 +49,7 @@ suspend fun Group.uploadImage(
|
||||
GroupImageIdRequestPacket.Response.State.OVER_FILE_SIZE_MAX -> throw OverFileSizeMaxException()
|
||||
}
|
||||
}.join()
|
||||
image.groupImageId
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,18 @@
|
||||
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
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
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.sendTo
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.uploadImage
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun ExternalImage(
|
||||
width: Int,
|
||||
height: Int,
|
||||
@ -14,6 +20,10 @@ fun ExternalImage(
|
||||
data: ByteReadPacket
|
||||
) = ExternalImage(width, height, md5, format, data, data.remaining)
|
||||
|
||||
/**
|
||||
* 外部图片. 图片数据还没有读取到内存.
|
||||
* @see ExternalImage.sendTo
|
||||
*/
|
||||
class ExternalImage(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
@ -40,6 +50,19 @@ class ExternalImage(
|
||||
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 {
|
||||
range.forEach {
|
||||
append(this@get[it].toUHexString())
|
||||
|
@ -54,6 +54,7 @@ fun String.hexToUBytes(): UByteArray = HexCache.hexToUBytes(this)
|
||||
|
||||
fun String.hexToInt(): Int = hexToBytes().toUInt().toInt()
|
||||
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, 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)
|
||||
|
@ -0,0 +1,14 @@
|
||||
@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() }
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
@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
|
@ -1,45 +0,0 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.streams.asInput
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.security.MessageDigest
|
||||
import javax.imageio.ImageIO
|
||||
import java.awt.image.BufferedImage as JavaBufferedImage
|
||||
|
||||
fun JavaBufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage {
|
||||
val digest = MessageDigest.getInstance("md5")
|
||||
digest.reset()
|
||||
|
||||
val buffer = buildPacket {
|
||||
ImageIO.write(this@toExternalImage, formatName, object : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
b.toByte().let {
|
||||
this@buildPacket.writeByte(it)
|
||||
digest.update(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ExternalImage(width, height, digest.digest(), formatName, buffer)
|
||||
}
|
||||
|
||||
fun File.toExternalImage(): ExternalImage {
|
||||
val input = ImageIO.createImageInputStream(this)
|
||||
val image = ImageIO.getImageReaders(input).asSequence().firstOrNull() ?: error("Unable to read file(${this.path}), no ImageReader found")
|
||||
image.input = input
|
||||
|
||||
return ExternalImage(
|
||||
width = image.getWidth(0),
|
||||
height = image.getHeight(0),
|
||||
md5 = this.md5(),
|
||||
imageFormat = image.formatName,
|
||||
input = this.inputStream().asInput(IoBuffer.Pool),
|
||||
inputSize = this.length()
|
||||
)
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.utils
|
||||
|
||||
import io.ktor.util.asStream
|
||||
import kotlinx.io.core.Input
|
||||
import kotlinx.io.core.IoBuffer
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.errors.IOException
|
||||
import kotlinx.io.streams.asInput
|
||||
import java.awt.image.BufferedImage
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.net.URL
|
||||
import java.security.MessageDigest
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
/*
|
||||
* 将各类型图片容器转为 [ExternalImage]
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 读取 [BufferedImage] 的属性, 然后构造 [ExternalImage]
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun BufferedImage.toExternalImage(formatName: String = "gif"): ExternalImage {
|
||||
val digest = MessageDigest.getInstance("md5")
|
||||
digest.reset()
|
||||
|
||||
val buffer = buildPacket {
|
||||
ImageIO.write(this@toExternalImage, formatName, object : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
b.toByte().let {
|
||||
this@buildPacket.writeByte(it)
|
||||
digest.update(it)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ExternalImage(width, height, digest.digest(), formatName, buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件头识别图片属性, 然后构造 [ExternalImage]
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun File.toExternalImage(): ExternalImage {
|
||||
val input = ImageIO.createImageInputStream(this)
|
||||
val image = ImageIO.getImageReaders(input).asSequence().firstOrNull() ?: error("Unable to read file(${this.path}), no ImageReader found")
|
||||
image.input = input
|
||||
|
||||
return ExternalImage(
|
||||
width = image.getWidth(0),
|
||||
height = image.getHeight(0),
|
||||
md5 = input.md5(),
|
||||
imageFormat = image.formatName,
|
||||
input = this.inputStream().asInput(IoBuffer.Pool),
|
||||
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()
|
||||
}
|
@ -10,7 +10,9 @@ import io.ktor.http.content.OutgoingContent
|
||||
import kotlinx.coroutines.io.ByteWriteChannel
|
||||
import kotlinx.io.core.Input
|
||||
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.net.InetAddress
|
||||
import java.security.MessageDigest
|
||||
@ -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)
|
||||
|
||||
fun File.md5(): ByteArray {
|
||||
fun InputStream.md5(): ByteArray {
|
||||
val digest = MessageDigest.getInstance("md5")
|
||||
digest.reset()
|
||||
this.inputStream().transferTo(object : OutputStream() {
|
||||
this.transferTo(object : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
b.toByte().let {
|
||||
digest.update(it)
|
||||
@ -39,6 +41,21 @@ fun File.md5(): ByteArray {
|
||||
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 localIpAddress(): String = InetAddress.getLocalHost().hostAddress
|
||||
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.event.events.FriendMessageEvent
|
||||
import net.mamoe.mirai.event.events.GroupMessageEvent
|
||||
import net.mamoe.mirai.event.subscribeAll
|
||||
|
Loading…
Reference in New Issue
Block a user