mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-11 18:10:26 +08:00
Simplify platform structure: merge jvmMain into commonMain
This commit is contained in:
parent
c030155505
commit
66464b87fd
@ -22,7 +22,6 @@ import net.mamoe.mirai.message.recall
|
|||||||
import net.mamoe.mirai.utils.ExternalImage
|
import net.mamoe.mirai.utils.ExternalImage
|
||||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
||||||
import net.mamoe.mirai.utils.WeakRefProperty
|
import net.mamoe.mirai.utils.WeakRefProperty
|
||||||
import kotlin.jvm.JvmSynthetic
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
package net.mamoe.mirai.event
|
package net.mamoe.mirai.event
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.event.internal.registerEvent
|
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
@ -15,11 +15,12 @@
|
|||||||
@file:JvmMultifileClass
|
@file:JvmMultifileClass
|
||||||
@file:JvmName("SendImageUtilsJvmKt")
|
@file:JvmName("SendImageUtilsJvmKt")
|
||||||
|
|
||||||
package net.mamoe.mirai.message
|
package event
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
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.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.Image
|
import net.mamoe.mirai.message.data.Image
|
||||||
import net.mamoe.mirai.message.data.Voice
|
import net.mamoe.mirai.message.data.Voice
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.message
|
package net.mamoe.mirai.message
|
||||||
|
|
||||||
|
import event.*
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.contact.*
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.event.events.BotEvent
|
import net.mamoe.mirai.event.events.BotEvent
|
||||||
@ -26,9 +27,9 @@ import net.mamoe.mirai.message.data.*
|
|||||||
import net.mamoe.mirai.utils.ExternalImage
|
import net.mamoe.mirai.utils.ExternalImage
|
||||||
import net.mamoe.mirai.utils.sendTo
|
import net.mamoe.mirai.utils.sendTo
|
||||||
import net.mamoe.mirai.utils.upload
|
import net.mamoe.mirai.utils.upload
|
||||||
import kotlin.jvm.JvmMultifileClass
|
import java.awt.image.BufferedImage
|
||||||
import kotlin.jvm.JvmName
|
import java.io.File
|
||||||
import kotlin.jvm.JvmSynthetic
|
import java.io.InputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个 (收到的) 消息事件.
|
* 一个 (收到的) 消息事件.
|
||||||
@ -158,9 +159,60 @@ public interface MessageEventExtensions<out TSender : User, out TSubject : Conta
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 一个消息事件在各平台的相关扩展. 请使用 [MessageEventExtensions] */
|
/** 一个消息事件在各平台的相关扩展. 请使用 [MessageEventExtensions] */
|
||||||
internal expect interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
|
|
||||||
|
/**
|
||||||
|
* 消息事件在 JVM 平台的扩展
|
||||||
|
* @see MessageEventExtensions
|
||||||
|
*/
|
||||||
|
internal interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
|
||||||
val subject: TSubject
|
val subject: TSubject
|
||||||
val sender: TSender
|
val sender: TSender
|
||||||
val message: MessageChain
|
val message: MessageChain
|
||||||
val bot: Bot
|
val bot: Bot
|
||||||
|
|
||||||
|
// region 上传图片
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun uploadImage(image: File): Image = subject.uploadImage(image)
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region 发送图片
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region 上传图片 (扩展)
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun BufferedImage.upload(): Image = upload(subject)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun File.uploadAsImage(): Image = uploadAsImage(subject)
|
||||||
|
// endregion 上传图片 (扩展)
|
||||||
|
|
||||||
|
// region 发送图片 (扩展)
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
suspend fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
||||||
|
// endregion 发送图片 (扩展)
|
||||||
|
|
||||||
}
|
}
|
@ -22,18 +22,18 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.message.data
|
package net.mamoe.mirai.message.data
|
||||||
|
|
||||||
|
import kotlinx.io.core.Input
|
||||||
import net.mamoe.mirai.Bot
|
import net.mamoe.mirai.Bot
|
||||||
import net.mamoe.mirai.IMirai
|
|
||||||
import net.mamoe.mirai.Mirai
|
import net.mamoe.mirai.Mirai
|
||||||
import net.mamoe.mirai.contact.Contact
|
import net.mamoe.mirai.contact.Contact
|
||||||
import net.mamoe.mirai.message.code.CodableMessage
|
import net.mamoe.mirai.message.code.CodableMessage
|
||||||
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.sendImage
|
import net.mamoe.mirai.utils.sendImage
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.net.URL
|
||||||
import kotlin.js.JsName
|
import kotlin.js.JsName
|
||||||
import kotlin.jvm.JvmMultifileClass
|
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
import kotlin.jvm.JvmSynthetic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义表情 (收藏的表情) 和普通图片.
|
* 自定义表情 (收藏的表情) 和普通图片.
|
||||||
@ -43,18 +43,24 @@ import kotlin.jvm.JvmSynthetic
|
|||||||
* 在上传时服务器会根据其缓存情况回复已有的图片 ID 或要求客户端上传. 详见 [Contact.uploadImage]
|
* 在上传时服务器会根据其缓存情况回复已有的图片 ID 或要求客户端上传. 详见 [Contact.uploadImage]
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* ### [toString] 和 [contentToString]
|
|
||||||
* - [toString] 固定返回 `[mirai:image:<ID>]` 格式字符串, 其中 `<ID>` 代表 [imageId].
|
|
||||||
* - [contentToString] 固定返回 "\[图片]"
|
|
||||||
*
|
|
||||||
* ### 上传和发送图片
|
* ### 上传和发送图片
|
||||||
* @see Contact.uploadImage 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
* @see Contact.uploadImage 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
||||||
* @see Contact.sendImage 上传 [图片文件][ExternalImage] 并发送返回的 [Image] 作为一条消息
|
* @see Contact.sendImage 上传 [图片文件][ExternalImage] 并发送返回的 [Image] 作为一条消息
|
||||||
* @see Image.sendTo 上传图片并得到 [Image] 消息
|
* @see Image.sendTo 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
||||||
|
*
|
||||||
|
* @see File.uploadAsImage
|
||||||
|
* @see InputStream.uploadAsImage
|
||||||
|
* @see Input.uploadAsImage
|
||||||
|
* @see URL.uploadAsImage
|
||||||
|
*
|
||||||
|
* @see File.sendAsImageTo
|
||||||
|
* @see InputStream.sendAsImageTo
|
||||||
|
* @see Input.sendAsImageTo
|
||||||
|
* @see URL.sendAsImageTo
|
||||||
*
|
*
|
||||||
* ### 下载图片
|
* ### 下载图片
|
||||||
* @see Image.queryUrl 扩展函数. 查询图片下载链接
|
* @see Image.queryUrl 扩展函数. 查询图片下载链接
|
||||||
* @see IMirai.queryImageUrl 查询图片下载链接 (Java 使用)
|
* @see Bot.queryImageUrl 查询图片下载链接 (Java 使用)
|
||||||
*
|
*
|
||||||
* 查看平台 `actual` 定义以获取上传方式扩展.
|
* 查看平台 `actual` 定义以获取上传方式扩展.
|
||||||
*
|
*
|
||||||
@ -64,11 +70,12 @@ import kotlin.jvm.JvmSynthetic
|
|||||||
* @see FlashImage 闪照
|
* @see FlashImage 闪照
|
||||||
* @see Image.flash 转换普通图片为闪照
|
* @see Image.flash 转换普通图片为闪照
|
||||||
*/
|
*/
|
||||||
public expect interface Image : Message, MessageContent, CodableMessage {
|
public interface Image : Message, MessageContent, CodableMessage {
|
||||||
public companion object Key : Message.Key<Image> {
|
public companion object Key : Message.Key<Image> {
|
||||||
public override val typeName: String
|
override val typeName: String get() = "Image"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图片的 id.
|
* 图片的 id.
|
||||||
*
|
*
|
||||||
@ -76,24 +83,16 @@ public expect interface Image : Message, MessageContent, CodableMessage {
|
|||||||
*
|
*
|
||||||
* ### 格式
|
* ### 格式
|
||||||
* 群图片:
|
* 群图片:
|
||||||
* - [GROUP_IMAGE_ID_REGEX], 示例: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.ext` (ext系扩展名)
|
* - [GROUP_IMAGE_ID_REGEX], 示例: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai` (后缀一定为 ".mirai")
|
||||||
*
|
*
|
||||||
* 好友图片:
|
* 好友图片:
|
||||||
* - [FRIEND_IMAGE_ID_REGEX_1], 示例: `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
|
* - [FRIEND_IMAGE_ID_REGEX_1], 示例: `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
|
||||||
* - [FRIEND_IMAGE_ID_REGEX_2], 示例: `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
|
* - [FRIEND_IMAGE_ID_REGEX_2], 示例: `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
|
||||||
*
|
*
|
||||||
* @see Image 使用 id 构造图片
|
* @see Image 使用 id 构造图片
|
||||||
* @see md5 得到图片文件 MD5
|
|
||||||
*/
|
*/
|
||||||
public val imageId: String
|
public val imageId: String
|
||||||
|
|
||||||
/* 实现:
|
|
||||||
final override fun toString(): String = _stringValue!!
|
|
||||||
|
|
||||||
final override fun contentToString(): String = "[图片]"
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有 [Image] 实现的基类.
|
* 所有 [Image] 实现的基类.
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
|
import net.mamoe.mirai.Bot
|
||||||
|
import net.mamoe.mirai.utils.internal.DeferredReusableInput
|
||||||
|
import net.mamoe.mirai.utils.internal.asReusableInput
|
||||||
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 将各类型图片容器转为 [ExternalImage]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 [BufferedImage] 保存为临时文件, 然后构造 [ExternalImage]
|
||||||
|
*/
|
||||||
|
@JvmOverloads
|
||||||
|
public fun BufferedImage.toExternalImage(formatName: String = "png"): ExternalImage =
|
||||||
|
ExternalImage(DeferredReusableInput(this, formatName))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将文件作为 [ExternalImage] 使用. 只会在需要的时候打开文件并读取数据.
|
||||||
|
* @param deleteOnClose 若为 `true`, 图片发送后将会删除这个文件
|
||||||
|
*/
|
||||||
|
@JvmOverloads
|
||||||
|
public fun File.toExternalImage(deleteOnClose: Boolean = false): ExternalImage {
|
||||||
|
require(this.isFile) { "File must be a file" }
|
||||||
|
require(this.exists()) { "File must exist" }
|
||||||
|
require(this.canRead()) { "File must can be read" }
|
||||||
|
return ExternalImage(asReusableInput(deleteOnClose))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 [InputStream] 委托为 [ExternalImage].
|
||||||
|
* 只会在上传图片时才读取 [InputStream] 的内容. 具体行为取决于相关 [Bot] 的 [FileCacheStrategy]
|
||||||
|
*/
|
||||||
|
public fun InputStream.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 [ByteArray] 委托为 [ExternalImage].
|
||||||
|
*/
|
||||||
|
public fun ByteArray.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
@ -9,10 +9,17 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.utils
|
package net.mamoe.mirai.utils
|
||||||
|
|
||||||
import kotlinx.io.core.Closeable
|
import kotlinx.io.core.*
|
||||||
import kotlinx.io.core.Input
|
import net.mamoe.mirai.Bot
|
||||||
import kotlinx.io.core.use
|
import net.mamoe.mirai.utils.internal.asReusableInput
|
||||||
import kotlinx.io.errors.IOException
|
import java.awt.image.BufferedImage
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.net.URL
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import javax.imageio.ImageIO
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
@ -20,43 +27,179 @@ import kotlin.contracts.contract
|
|||||||
* 缓存策略.
|
* 缓存策略.
|
||||||
*
|
*
|
||||||
* 图片上传时默认使用文件缓存.
|
* 图片上传时默认使用文件缓存.
|
||||||
|
*
|
||||||
|
* @see BotConfiguration.fileCacheStrategy 为 [Bot] 指定缓存策略
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
public expect interface FileCacheStrategy {
|
public interface FileCacheStrategy {
|
||||||
/**
|
/**
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
* 将 [input] 缓存为 [ExternalImage].
|
||||||
* 此函数应 close 这个 [Input]
|
* 此函数应 close 这个 [Input]
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
@Throws(IOException::class)
|
@Throws(java.io.IOException::class)
|
||||||
public fun newImageCache(input: Input): ExternalImage
|
public fun newImageCache(input: Input): ExternalImage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 [input] 缓存为 [ExternalImage].
|
||||||
|
* 此函数应 close 这个 [InputStream]
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
public fun newImageCache(input: InputStream): ExternalImage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
* 将 [input] 缓存为 [ExternalImage].
|
||||||
* 此 [input] 的内容应是不变的.
|
* 此 [input] 的内容应是不变的.
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
@Throws(IOException::class)
|
@Throws(java.io.IOException::class)
|
||||||
public fun newImageCache(input: ByteArray): ExternalImage
|
public fun newImageCache(input: ByteArray): ExternalImage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认的缓存方案. 在 JVM 平台使用系统临时文件.
|
* 将 [input] 缓存为 [ExternalImage].
|
||||||
|
* 此 [input] 的内容应是不变的.
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
public object PlatformDefault : FileCacheStrategy
|
@Throws(java.io.IOException::class)
|
||||||
|
public fun newImageCache(input: BufferedImage, format: String = "png"): ExternalImage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 [input] 缓存为 [ExternalImage].
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
public fun newImageCache(input: URL): ExternalImage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认的缓存方案, 使用系统临时文件夹存储.
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
public object PlatformDefault : FileCacheStrategy by TempCache(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用内存直接存储所有图片文件.
|
* 使用内存直接存储所有图片文件.
|
||||||
*/
|
*/
|
||||||
public object MemoryCache : FileCacheStrategy {
|
public object MemoryCache : FileCacheStrategy {
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
@Throws(IOException::class)
|
@Throws(java.io.IOException::class)
|
||||||
public override fun newImageCache(input: Input): ExternalImage
|
override fun newImageCache(input: Input): ExternalImage {
|
||||||
|
return newImageCache(input.readBytes())
|
||||||
|
}
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
@MiraiExperimentalApi
|
||||||
@Throws(IOException::class)
|
@Throws(java.io.IOException::class)
|
||||||
public override fun newImageCache(input: ByteArray): ExternalImage
|
override fun newImageCache(input: InputStream): ExternalImage {
|
||||||
|
return newImageCache(input.readBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
override fun newImageCache(input: ByteArray): ExternalImage {
|
||||||
|
return ExternalImage(input.asReusableInput())
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
override fun newImageCache(input: BufferedImage, format: String): ExternalImage {
|
||||||
|
val out = ByteArrayOutputStream()
|
||||||
|
ImageIO.write(input, format, out)
|
||||||
|
return newImageCache(out.toByteArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
override fun newImageCache(input: URL): ExternalImage {
|
||||||
|
val out = ByteArrayOutputStream()
|
||||||
|
input.openConnection().getInputStream().use { it.copyTo(out) }
|
||||||
|
return newImageCache(out.toByteArray())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用系统临时文件夹缓存图片文件. 在图片使用完毕后删除临时文件.
|
||||||
|
*/
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
public class TempCache @JvmOverloads constructor(
|
||||||
|
/**
|
||||||
|
* 缓存图片存放位置. 为 `null` 时使用主机系统的临时文件夹
|
||||||
|
*/
|
||||||
|
public val directory: File? = null
|
||||||
|
) : FileCacheStrategy {
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
override fun newImageCache(input: Input): ExternalImage {
|
||||||
|
return ExternalImage(createTempFile(directory = directory).apply {
|
||||||
|
deleteOnExit()
|
||||||
|
input.withOut(this.outputStream()) { copyTo(it) }
|
||||||
|
}.asReusableInput(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
override fun newImageCache(input: InputStream): ExternalImage {
|
||||||
|
return ExternalImage(createTempFile(directory = directory).apply {
|
||||||
|
deleteOnExit()
|
||||||
|
input.withOut(this.outputStream()) { copyTo(it) }
|
||||||
|
}.asReusableInput(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
override fun newImageCache(input: ByteArray): ExternalImage {
|
||||||
|
return ExternalImage(input.asReusableInput())
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
override fun newImageCache(input: BufferedImage, format: String): ExternalImage {
|
||||||
|
val file = createTempFile(directory = directory).apply { deleteOnExit() }
|
||||||
|
|
||||||
|
val digest = MessageDigest.getInstance("md5")
|
||||||
|
digest.reset()
|
||||||
|
|
||||||
|
file.outputStream().use { out ->
|
||||||
|
ImageIO.write(input, format, object : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
out.write(b)
|
||||||
|
digest.update(b.toByte())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(b: ByteArray) {
|
||||||
|
out.write(b)
|
||||||
|
digest.update(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun write(b: ByteArray, off: Int, len: Int) {
|
||||||
|
out.write(b, off, len)
|
||||||
|
digest.update(b, off, len)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION_ERROR")
|
||||||
|
return ExternalImage(file.asReusableInput(true, digest.digest()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@MiraiExperimentalApi
|
||||||
|
override fun newImageCache(input: URL): ExternalImage {
|
||||||
|
return ExternalImage(createTempFile(directory = directory).apply {
|
||||||
|
deleteOnExit()
|
||||||
|
input.openConnection().getInputStream().withOut(this.outputStream()) { copyTo(it) }
|
||||||
|
}.asReusableInput(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(java.io.IOException::class)
|
||||||
|
internal fun Input.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
|
||||||
|
var bytesCopied: Long = 0
|
||||||
|
val buffer = ByteArray(bufferSize)
|
||||||
|
var bytes = readAvailable(buffer)
|
||||||
|
while (bytes >= 0) {
|
||||||
|
out.write(buffer, 0, bytes)
|
||||||
|
bytesCopied += bytes
|
||||||
|
bytes = readAvailable(buffer)
|
||||||
|
}
|
||||||
|
return bytesCopied
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <I : Closeable, O : Closeable, R> I.withOut(output: O, block: I.(output: O) -> R): R {
|
internal inline fun <I : Closeable, O : Closeable, R> I.withOut(output: O, block: I.(output: O) -> R): R {
|
||||||
|
@ -1,193 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.event.internal
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import net.mamoe.mirai.event.*
|
|
||||||
import java.lang.reflect.Method
|
|
||||||
import java.util.function.Consumer
|
|
||||||
import java.util.function.Function
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
import kotlin.reflect.full.IllegalCallableAccessException
|
|
||||||
import kotlin.reflect.full.callSuspend
|
|
||||||
import kotlin.reflect.full.isSubclassOf
|
|
||||||
import kotlin.reflect.jvm.isAccessible
|
|
||||||
import kotlin.reflect.jvm.kotlinFunction
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <E : Event> Class<E>._subscribeEventForJaptOnly(
|
|
||||||
scope: CoroutineScope,
|
|
||||||
onEvent: Function<E, ListeningStatus>
|
|
||||||
): Listener<E> {
|
|
||||||
return this.kotlin.subscribeInternal(
|
|
||||||
scope.Handler(
|
|
||||||
scope.coroutineContext,
|
|
||||||
Listener.ConcurrencyKind.LOCKED
|
|
||||||
) { withContext(Dispatchers.IO) { onEvent.apply(it) } })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
internal fun <E : Event> Class<E>._subscribeEventForJaptOnly(scope: CoroutineScope, onEvent: Consumer<E>): Listener<E> {
|
|
||||||
return this.kotlin.subscribeInternal(
|
|
||||||
scope.Handler(
|
|
||||||
EmptyCoroutineContext,
|
|
||||||
Listener.ConcurrencyKind.LOCKED
|
|
||||||
) { withContext(Dispatchers.IO) { onEvent.accept(it) }; ListeningStatus.LISTENING; })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
internal fun Method.registerEvent(
|
|
||||||
owner: Any,
|
|
||||||
scope: CoroutineScope,
|
|
||||||
annotation: EventHandler,
|
|
||||||
coroutineContext: CoroutineContext
|
|
||||||
): Listener<Event> {
|
|
||||||
this.isAccessible = true
|
|
||||||
val kotlinFunction = kotlin.runCatching { this.kotlinFunction }.getOrNull()
|
|
||||||
return if (kotlinFunction != null) {
|
|
||||||
// kotlin functions
|
|
||||||
|
|
||||||
val param = kotlinFunction.parameters
|
|
||||||
when (param.size) {
|
|
||||||
3 -> { // ownerClass, receiver, event
|
|
||||||
check(param[1].type == param[2].type) { "Illegal kotlin function ${kotlinFunction.name}. Receiver and param must have same type" }
|
|
||||||
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
|
|
||||||
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2 -> { // ownerClass, event
|
|
||||||
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
|
|
||||||
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("function ${kotlinFunction.name} must have one Event param")
|
|
||||||
}
|
|
||||||
lateinit var listener: Listener<*>
|
|
||||||
kotlin.runCatching {
|
|
||||||
kotlinFunction.isAccessible = true
|
|
||||||
}
|
|
||||||
suspend fun callFunction(event: Event): Any? {
|
|
||||||
try {
|
|
||||||
return when (param.size) {
|
|
||||||
3 -> {
|
|
||||||
if (kotlinFunction.isSuspend) {
|
|
||||||
kotlinFunction.callSuspend(owner, event, event)
|
|
||||||
} else withContext(Dispatchers.IO) { // for safety
|
|
||||||
kotlinFunction.call(owner, event, event)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
2 -> {
|
|
||||||
if (kotlinFunction.isSuspend) {
|
|
||||||
kotlinFunction.callSuspend(owner, event)
|
|
||||||
} else withContext(Dispatchers.IO) { // for safety
|
|
||||||
kotlinFunction.call(owner, event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("stub")
|
|
||||||
}
|
|
||||||
} catch (e: IllegalCallableAccessException) {
|
|
||||||
listener.completeExceptionally(e)
|
|
||||||
return ListeningStatus.STOPPED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require(!kotlinFunction.returnType.isMarkedNullable) {
|
|
||||||
"Kotlin event handlers cannot have nullable return type."
|
|
||||||
}
|
|
||||||
require(kotlinFunction.parameters.none { it.type.isMarkedNullable }) {
|
|
||||||
"Kotlin event handlers cannot have nullable parameter type."
|
|
||||||
}
|
|
||||||
when (kotlinFunction.returnType.classifier) {
|
|
||||||
Unit::class, Nothing::class -> {
|
|
||||||
scope.subscribeAlways(
|
|
||||||
param[1].type.classifier as KClass<out Event>,
|
|
||||||
priority = annotation.priority,
|
|
||||||
concurrency = annotation.concurrency,
|
|
||||||
coroutineContext = coroutineContext
|
|
||||||
) {
|
|
||||||
if (annotation.ignoreCancelled) {
|
|
||||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
|
||||||
callFunction(this)
|
|
||||||
}
|
|
||||||
} else callFunction(this)
|
|
||||||
}.also { listener = it }
|
|
||||||
}
|
|
||||||
ListeningStatus::class -> {
|
|
||||||
scope.subscribe(
|
|
||||||
param[1].type.classifier as KClass<out Event>,
|
|
||||||
priority = annotation.priority,
|
|
||||||
concurrency = annotation.concurrency,
|
|
||||||
coroutineContext = coroutineContext
|
|
||||||
) {
|
|
||||||
if (annotation.ignoreCancelled) {
|
|
||||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
|
||||||
callFunction(this) as ListeningStatus
|
|
||||||
} else ListeningStatus.LISTENING
|
|
||||||
} else callFunction(this) as ListeningStatus
|
|
||||||
}.also { listener = it }
|
|
||||||
}
|
|
||||||
else -> error("Illegal method return type. Required Void, Nothing or ListeningStatus, found ${kotlinFunction.returnType.classifier}")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// java methods
|
|
||||||
|
|
||||||
val paramType = this.parameters[0].type
|
|
||||||
check(this.parameterCount == 1 && Event::class.java.isAssignableFrom(paramType)) {
|
|
||||||
"Illegal method parameter. Required one exact Event subclass. found $paramType"
|
|
||||||
}
|
|
||||||
when (this.returnType) {
|
|
||||||
Void::class.java, Void.TYPE, Nothing::class.java -> {
|
|
||||||
scope.subscribeAlways(
|
|
||||||
paramType.kotlin as KClass<out Event>,
|
|
||||||
priority = annotation.priority,
|
|
||||||
concurrency = annotation.concurrency,
|
|
||||||
coroutineContext = coroutineContext
|
|
||||||
) {
|
|
||||||
if (annotation.ignoreCancelled) {
|
|
||||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
this@registerEvent.invoke(owner, this@subscribeAlways)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else withContext(Dispatchers.IO) {
|
|
||||||
this@registerEvent.invoke(owner, this@subscribeAlways)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListeningStatus::class.java -> {
|
|
||||||
scope.subscribe(
|
|
||||||
paramType.kotlin as KClass<out Event>,
|
|
||||||
priority = annotation.priority,
|
|
||||||
concurrency = annotation.concurrency,
|
|
||||||
coroutineContext = coroutineContext
|
|
||||||
) {
|
|
||||||
if (annotation.ignoreCancelled) {
|
|
||||||
if ((this as? CancellableEvent)?.isCancelled != true) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
this@registerEvent.invoke(owner, this@subscribe) as ListeningStatus
|
|
||||||
}
|
|
||||||
} else ListeningStatus.LISTENING
|
|
||||||
} else withContext(Dispatchers.IO) {
|
|
||||||
this@registerEvent.invoke(owner, this@subscribe) as ListeningStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> error("Illegal method return type. Required Void or ListeningStatus, but found ${this.returnType.canonicalName}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.message
|
|
||||||
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.contact.User
|
|
||||||
import net.mamoe.mirai.message.data.Image
|
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息事件在 JVM 平台的扩展
|
|
||||||
* @see MessageEventExtensions
|
|
||||||
*/
|
|
||||||
internal actual interface MessageEventPlatformExtensions<out TSender : User, out TSubject : Contact> {
|
|
||||||
actual val subject: TSubject
|
|
||||||
actual val sender: TSender
|
|
||||||
actual val message: MessageChain
|
|
||||||
actual val bot: Bot
|
|
||||||
|
|
||||||
// region 上传图片
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun uploadImage(image: File): Image = subject.uploadImage(image)
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region 发送图片
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun sendImage(image: BufferedImage): MessageReceipt<TSubject> = subject.sendImage(image)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun sendImage(image: InputStream): MessageReceipt<TSubject> = subject.sendImage(image)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun sendImage(image: File): MessageReceipt<TSubject> = subject.sendImage(image)
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region 上传图片 (扩展)
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun BufferedImage.upload(): Image = upload(subject)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun File.uploadAsImage(): Image = uploadAsImage(subject)
|
|
||||||
// endregion 上传图片 (扩展)
|
|
||||||
|
|
||||||
// region 发送图片 (扩展)
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun BufferedImage.send(): MessageReceipt<TSubject> = sendTo(subject)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun InputStream.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun File.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
|
||||||
// endregion 发送图片 (扩展)
|
|
||||||
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@JvmSynthetic
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
suspend fun URL.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun Input.sendAsImage(): MessageReceipt<TSubject> = sendAsImageTo(subject)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@JvmSynthetic
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
suspend fun uploadImage(image: URL): Image = subject.uploadImage(image)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun uploadImage(image: Input): Image = subject.uploadImage(image)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun sendImage(image: URL): MessageReceipt<TSubject> = subject.sendImage(image)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun sendImage(image: Input): MessageReceipt<TSubject> = subject.sendImage(image)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun URL.uploadAsImage(): Image = uploadAsImage(subject)
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@JvmSynthetic
|
|
||||||
suspend fun Input.uploadAsImage(): Image = uploadAsImage(subject)
|
|
||||||
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:JvmMultifileClass
|
|
||||||
@file:JvmName("MessageUtils")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.message.data
|
|
||||||
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.message.code.CodableMessage
|
|
||||||
import net.mamoe.mirai.message.sendAsImageTo
|
|
||||||
import net.mamoe.mirai.message.sendImage
|
|
||||||
import net.mamoe.mirai.message.uploadAsImage
|
|
||||||
import net.mamoe.mirai.message.uploadImage
|
|
||||||
import net.mamoe.mirai.utils.ExternalImage
|
|
||||||
import net.mamoe.mirai.utils.sendImage
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义表情 (收藏的表情) 和普通图片.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* 最推荐的存储方式是存储图片原文件, 每次发送图片时都使用文件上传.
|
|
||||||
* 在上传时服务器会根据其缓存情况回复已有的图片 ID 或要求客户端上传. 详见 [Contact.uploadImage]
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### 上传和发送图片
|
|
||||||
* @see Contact.uploadImage 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
|
||||||
* @see Contact.sendImage 上传 [图片文件][ExternalImage] 并发送返回的 [Image] 作为一条消息
|
|
||||||
* @see Image.sendTo 上传 [图片文件][ExternalImage] 并得到 [Image] 消息
|
|
||||||
*
|
|
||||||
* @see File.uploadAsImage
|
|
||||||
* @see InputStream.uploadAsImage
|
|
||||||
* @see Input.uploadAsImage
|
|
||||||
* @see URL.uploadAsImage
|
|
||||||
*
|
|
||||||
* @see File.sendAsImageTo
|
|
||||||
* @see InputStream.sendAsImageTo
|
|
||||||
* @see Input.sendAsImageTo
|
|
||||||
* @see URL.sendAsImageTo
|
|
||||||
*
|
|
||||||
* ### 下载图片
|
|
||||||
* @see Image.queryUrl 扩展函数. 查询图片下载链接
|
|
||||||
* @see Bot.queryImageUrl 查询图片下载链接 (Java 使用)
|
|
||||||
*
|
|
||||||
* 查看平台 `actual` 定义以获取上传方式扩展.
|
|
||||||
*
|
|
||||||
* ## mirai 码支持
|
|
||||||
* 格式: [mirai:image:*[Image.imageId]*]
|
|
||||||
*
|
|
||||||
* @see FlashImage 闪照
|
|
||||||
* @see Image.flash 转换普通图片为闪照
|
|
||||||
*/
|
|
||||||
public actual interface Image : Message, MessageContent, CodableMessage {
|
|
||||||
public actual companion object Key : Message.Key<Image> {
|
|
||||||
actual override val typeName: String get() = "Image"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 图片的 id.
|
|
||||||
*
|
|
||||||
* 图片 id 不一定会长时间保存, 也可能在将来改变格式, 因此不建议使用 id 发送图片.
|
|
||||||
*
|
|
||||||
* ### 格式
|
|
||||||
* 群图片:
|
|
||||||
* - [GROUP_IMAGE_ID_REGEX], 示例: `{01E9451B-70ED-EAE3-B37C-101F1EEBF5B5}.mirai` (后缀一定为 ".mirai")
|
|
||||||
*
|
|
||||||
* 好友图片:
|
|
||||||
* - [FRIEND_IMAGE_ID_REGEX_1], 示例: `/f8f1ab55-bf8e-4236-b55e-955848d7069f`
|
|
||||||
* - [FRIEND_IMAGE_ID_REGEX_2], 示例: `/000000000-3814297509-BFB7027B9354B8F899A062061D74E206`
|
|
||||||
*
|
|
||||||
* @see Image 使用 id 构造图片
|
|
||||||
*/
|
|
||||||
public actual val imageId: String
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("DEPRECATION", "DEPRECATION_ERROR")
|
|
||||||
@file:JvmMultifileClass
|
|
||||||
@file:JvmName("SendImageUtilsJvmKt")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.message
|
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import net.mamoe.mirai.contact.Contact
|
|
||||||
import net.mamoe.mirai.message.data.Image
|
|
||||||
import net.mamoe.mirai.utils.OverFileSizeMaxException
|
|
||||||
import net.mamoe.mirai.utils.sendTo
|
|
||||||
import net.mamoe.mirai.utils.toExternalImage
|
|
||||||
import net.mamoe.mirai.utils.upload
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend fun <C : Contact> URL.sendAsImageTo(contact: C): MessageReceipt<C> =
|
|
||||||
toExternalImage().sendTo(contact)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend fun <C : Contact> Input.sendAsImageTo(contact: C): MessageReceipt<C> =
|
|
||||||
toExternalImage().sendTo(contact)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image]
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend fun URL.uploadAsImage(contact: Contact): Image =
|
|
||||||
toExternalImage().upload(contact)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend inline fun Contact.uploadImage(imageUrl: URL): Image = imageUrl.uploadAsImage(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend inline fun Contact.uploadImage(imageInput: Input): Image = imageInput.uploadAsImage(this)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().sendAsImageTo(contact)"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend inline fun <C : Contact> C.sendImage(imageUrl: URL): MessageReceipt<C> = imageUrl.sendAsImageTo(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend inline fun <C : Contact> C.sendImage(imageInput: Input): MessageReceipt<C> =
|
|
||||||
imageInput.sendAsImageTo(this)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image]
|
|
||||||
* @throws OverFileSizeMaxException
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
@Throws(OverFileSizeMaxException::class)
|
|
||||||
public suspend fun Input.uploadAsImage(contact: Contact): Image =
|
|
||||||
toExternalImage().upload(contact)
|
|
@ -1,104 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.utils
|
|
||||||
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.utils.internal.DeferredReusableInput
|
|
||||||
import net.mamoe.mirai.utils.internal.asReusableInput
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.File
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 将各类型图片容器转为 [ExternalImage]
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [BufferedImage] 保存为临时文件, 然后构造 [ExternalImage]
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
public fun BufferedImage.toExternalImage(formatName: String = "png"): ExternalImage =
|
|
||||||
ExternalImage(DeferredReusableInput(this, formatName))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将文件作为 [ExternalImage] 使用. 只会在需要的时候打开文件并读取数据.
|
|
||||||
* @param deleteOnClose 若为 `true`, 图片发送后将会删除这个文件
|
|
||||||
*/
|
|
||||||
@JvmOverloads
|
|
||||||
public fun File.toExternalImage(deleteOnClose: Boolean = false): ExternalImage {
|
|
||||||
require(this.isFile) { "File must be a file" }
|
|
||||||
require(this.exists()) { "File must exist" }
|
|
||||||
require(this.canRead()) { "File must can be read" }
|
|
||||||
return ExternalImage(asReusableInput(deleteOnClose))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [InputStream] 委托为 [ExternalImage].
|
|
||||||
* 只会在上传图片时才读取 [InputStream] 的内容. 具体行为取决于相关 [Bot] 的 [FileCacheStrategy]
|
|
||||||
*/
|
|
||||||
public fun InputStream.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [ByteArray] 委托为 [ExternalImage].
|
|
||||||
*/
|
|
||||||
public fun ByteArray.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [URL] 委托为 [ExternalImage].
|
|
||||||
*
|
|
||||||
* 只会在上传图片时才读取 [URL] 的内容. 具体行为取决于相关 [Bot] 的 [FileCacheStrategy]
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"请自行通过 URL.openConnection 得到 InputStream 后调用其扩展",
|
|
||||||
replaceWith = ReplaceWith("this.openConnection().toExternalImage"),
|
|
||||||
level = DeprecationLevel.WARNING
|
|
||||||
)
|
|
||||||
public fun URL.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [Input] 委托为 [ExternalImage].
|
|
||||||
* 只会在上传图片时才读取 [Input] 的内容. 具体行为取决于相关 [Bot] 的 [FileCacheStrategy]
|
|
||||||
*/
|
|
||||||
@Deprecated(
|
|
||||||
"已弃用对 kotlinx.io 的支持",
|
|
||||||
level = DeprecationLevel.ERROR
|
|
||||||
)
|
|
||||||
public fun Input.toExternalImage(): ExternalImage = ExternalImage(DeferredReusableInput(this, null))
|
|
||||||
|
|
||||||
|
|
||||||
@PlannedRemoval("1.2.0")
|
|
||||||
@Suppress("RedundantSuspendModifier", "DEPRECATION_ERROR")
|
|
||||||
@Deprecated("no need", ReplaceWith("toExternalImage()"), level = DeprecationLevel.HIDDEN)
|
|
||||||
public suspend fun Input.suspendToExternalImage(): ExternalImage = toExternalImage()
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier")
|
|
||||||
@PlannedRemoval("1.2.0")
|
|
||||||
@Deprecated("no need", ReplaceWith("toExternalImage()"), level = DeprecationLevel.HIDDEN)
|
|
||||||
public suspend fun InputStream.suspendToExternalImage(): ExternalImage = toExternalImage()
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier", "DEPRECATION")
|
|
||||||
@PlannedRemoval("1.2.0")
|
|
||||||
@Deprecated("no need", ReplaceWith("toExternalImage()"), level = DeprecationLevel.HIDDEN)
|
|
||||||
public suspend fun URL.suspendToExternalImage(): ExternalImage = toExternalImage()
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier")
|
|
||||||
@PlannedRemoval("1.2.0")
|
|
||||||
@Deprecated("no need", ReplaceWith("toExternalImage()"), level = DeprecationLevel.HIDDEN)
|
|
||||||
public suspend fun File.suspendToExternalImage(): ExternalImage = toExternalImage()
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier")
|
|
||||||
@PlannedRemoval("1.2.0")
|
|
||||||
@Deprecated("no need", ReplaceWith("toExternalImage()"), level = DeprecationLevel.HIDDEN)
|
|
||||||
public suspend fun BufferedImage.suspendToExternalImage(): ExternalImage = toExternalImage()
|
|
@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
|
||||||
*
|
|
||||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
|
||||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
|
||||||
*
|
|
||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package net.mamoe.mirai.utils
|
|
||||||
|
|
||||||
import kotlinx.io.core.Input
|
|
||||||
import kotlinx.io.core.readAvailable
|
|
||||||
import kotlinx.io.core.readBytes
|
|
||||||
import net.mamoe.mirai.Bot
|
|
||||||
import net.mamoe.mirai.utils.internal.asReusableInput
|
|
||||||
import java.awt.image.BufferedImage
|
|
||||||
import java.io.*
|
|
||||||
import java.net.URL
|
|
||||||
import java.security.MessageDigest
|
|
||||||
import javax.imageio.ImageIO
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存策略.
|
|
||||||
*
|
|
||||||
* 图片上传时默认使用文件缓存.
|
|
||||||
*
|
|
||||||
* @see BotConfiguration.fileCacheStrategy 为 [Bot] 指定缓存策略
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
public actual interface FileCacheStrategy {
|
|
||||||
/**
|
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
|
||||||
* 此函数应 close 这个 [Input]
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
public actual fun newImageCache(input: Input): ExternalImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
|
||||||
* 此函数应 close 这个 [InputStream]
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
public fun newImageCache(input: InputStream): ExternalImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
|
||||||
* 此 [input] 的内容应是不变的.
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
public actual fun newImageCache(input: ByteArray): ExternalImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
|
||||||
* 此 [input] 的内容应是不变的.
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
public fun newImageCache(input: BufferedImage, format: String = "png"): ExternalImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将 [input] 缓存为 [ExternalImage].
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
public fun newImageCache(input: URL): ExternalImage
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认的缓存方案, 使用系统临时文件夹存储.
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
public actual object PlatformDefault : FileCacheStrategy by TempCache(null)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用内存直接存储所有图片文件.
|
|
||||||
*/
|
|
||||||
public actual object MemoryCache : FileCacheStrategy {
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
actual override fun newImageCache(input: Input): ExternalImage {
|
|
||||||
return newImageCache(input.readBytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun newImageCache(input: InputStream): ExternalImage {
|
|
||||||
return newImageCache(input.readBytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
actual override fun newImageCache(input: ByteArray): ExternalImage {
|
|
||||||
return ExternalImage(input.asReusableInput())
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
override fun newImageCache(input: BufferedImage, format: String): ExternalImage {
|
|
||||||
val out = ByteArrayOutputStream()
|
|
||||||
ImageIO.write(input, format, out)
|
|
||||||
return newImageCache(out.toByteArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
override fun newImageCache(input: URL): ExternalImage {
|
|
||||||
val out = ByteArrayOutputStream()
|
|
||||||
input.openConnection().getInputStream().use { it.copyTo(out) }
|
|
||||||
return newImageCache(out.toByteArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用系统临时文件夹缓存图片文件. 在图片使用完毕后删除临时文件.
|
|
||||||
*/
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
public class TempCache @JvmOverloads constructor(
|
|
||||||
/**
|
|
||||||
* 缓存图片存放位置. 为 `null` 时使用主机系统的临时文件夹
|
|
||||||
*/
|
|
||||||
public val directory: File? = null
|
|
||||||
) : FileCacheStrategy {
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun newImageCache(input: Input): ExternalImage {
|
|
||||||
return ExternalImage(createTempFile(directory = directory).apply {
|
|
||||||
deleteOnExit()
|
|
||||||
input.withOut(this.outputStream()) { copyTo(it) }
|
|
||||||
}.asReusableInput(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun newImageCache(input: InputStream): ExternalImage {
|
|
||||||
return ExternalImage(createTempFile(directory = directory).apply {
|
|
||||||
deleteOnExit()
|
|
||||||
input.withOut(this.outputStream()) { copyTo(it) }
|
|
||||||
}.asReusableInput(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Throws(IOException::class)
|
|
||||||
override fun newImageCache(input: ByteArray): ExternalImage {
|
|
||||||
return ExternalImage(input.asReusableInput())
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
override fun newImageCache(input: BufferedImage, format: String): ExternalImage {
|
|
||||||
val file = createTempFile(directory = directory).apply { deleteOnExit() }
|
|
||||||
|
|
||||||
val digest = MessageDigest.getInstance("md5")
|
|
||||||
digest.reset()
|
|
||||||
|
|
||||||
file.outputStream().use { out ->
|
|
||||||
ImageIO.write(input, format, object : OutputStream() {
|
|
||||||
override fun write(b: Int) {
|
|
||||||
out.write(b)
|
|
||||||
digest.update(b.toByte())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(b: ByteArray) {
|
|
||||||
out.write(b)
|
|
||||||
digest.update(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun write(b: ByteArray, off: Int, len: Int) {
|
|
||||||
out.write(b, off, len)
|
|
||||||
digest.update(b, off, len)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
|
||||||
return ExternalImage(file.asReusableInput(true, digest.digest()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
override fun newImageCache(input: URL): ExternalImage {
|
|
||||||
return ExternalImage(createTempFile(directory = directory).apply {
|
|
||||||
deleteOnExit()
|
|
||||||
input.openConnection().getInputStream().withOut(this.outputStream()) { copyTo(it) }
|
|
||||||
}.asReusableInput(true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Throws(IOException::class)
|
|
||||||
internal fun Input.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
|
|
||||||
var bytesCopied: Long = 0
|
|
||||||
val buffer = ByteArray(bufferSize)
|
|
||||||
var bytes = readAvailable(buffer)
|
|
||||||
while (bytes >= 0) {
|
|
||||||
out.write(buffer, 0, bytes)
|
|
||||||
bytesCopied += bytes
|
|
||||||
bytes = readAvailable(buffer)
|
|
||||||
}
|
|
||||||
return bytesCopied
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user