From 26210e7845d6a3287925882fb27a7228eed0cb1a Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 7 Mar 2020 11:29:04 +0800 Subject: [PATCH] Generify functions --- .../net.mamoe.mirai/utils/ExternalImage.kt | 3 +- .../kotlin/net.mamoe.mirai/utils/channels.kt | 33 ++++++++++++++ .../net/mamoe/mirai/message/MessagePacket.kt | 45 +++++++++++++------ .../net/mamoe/mirai/message/MessageReceipt.kt | 10 +---- .../mamoe/mirai/message/SendImageUtilsJvm.kt | 43 +++++++++--------- 5 files changed, 90 insertions(+), 44 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt index 527c76ec5..59a07668c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt @@ -20,6 +20,7 @@ import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.Image +import net.mamoe.mirai.message.data.OfflineImage import net.mamoe.mirai.message.data.sendTo import net.mamoe.mirai.utils.io.toUHexString @@ -159,7 +160,7 @@ suspend fun ExternalImage.sendTo(contact: C): MessageReceipt = * * @see contact 图片上传对象. 由于好友图片与群图片不通用, 上传时必须提供目标联系人 */ -suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) { +suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) { is Group -> contact.uploadImage(this) is QQ -> contact.uploadImage(this) else -> error("unreachable") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt index cc0bcfef3..e2c480e16 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/channels.kt @@ -15,6 +15,8 @@ package net.mamoe.mirai.utils import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.ByteWriteChannel +import io.ktor.utils.io.close import io.ktor.utils.io.core.Output import io.ktor.utils.io.core.writeFully import io.ktor.utils.io.pool.useInstance @@ -52,6 +54,19 @@ suspend fun ByteReadChannel.copyTo(dst: Output) { } } +/** + * 从接收者管道读取所有数据并写入 [dst]. 不会关闭 [dst] + */ +suspend fun ByteReadChannel.copyTo(dst: ByteWriteChannel) { + @UseExperimental(MiraiInternalAPI::class) + ByteArrayPool.useInstance { buffer -> + var size: Int + while (this.readAvailable(buffer).also { size = it } > 0) { + dst.writeFully(buffer, 0, size) + } + } +} + /* // 垃圾 kotlin, Unresolved reference: ByteWriteChannel /** @@ -104,6 +119,24 @@ suspend fun ByteReadChannel.copyAndClose(dst: Output) { } } +/** + * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst] + */ +suspend fun ByteReadChannel.copyAndClose(dst: ByteWriteChannel) { + try { + @UseExperimental(MiraiInternalAPI::class) + ByteArrayPool.useInstance { buffer -> + var size: Int + while (this.readAvailable(buffer).also { size = it } > 0) { + dst.writeFully(buffer, 0, size) + } + } + } finally { + @Suppress("DuplicatedCode") + dst.close() + } +} + /*// 垃圾 kotlin, Unresolved reference: ByteWriteChannel /** * 从接收者管道读取所有数据并写入 [dst], 最终关闭 [dst] diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt index 60faf7d90..e36d3bd19 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt @@ -11,7 +11,9 @@ package net.mamoe.mirai.message +import io.ktor.utils.io.ByteWriteChannel import io.ktor.utils.io.core.Input +import io.ktor.utils.io.core.Output import io.ktor.utils.io.core.use import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.QQ @@ -41,17 +43,15 @@ actual abstract class MessagePacket actual con // endregion // region 发送图片 - suspend inline fun sendImage(image: BufferedImage) = subject.sendImage(image) - - suspend inline fun sendImage(image: URL) = subject.sendImage(image) - suspend inline fun sendImage(image: Input) = subject.sendImage(image) - suspend inline fun sendImage(image: InputStream) = subject.sendImage(image) - suspend inline fun sendImage(image: File) = subject.sendImage(image) + suspend inline fun sendImage(image: BufferedImage): MessageReceipt = subject.sendImage(image) + suspend inline fun sendImage(image: URL): MessageReceipt = subject.sendImage(image) + suspend inline fun sendImage(image: Input): MessageReceipt = subject.sendImage(image) + suspend inline fun sendImage(image: InputStream): MessageReceipt = subject.sendImage(image) + suspend inline fun sendImage(image: File): MessageReceipt = subject.sendImage(image) // endregion // region 上传图片 (扩展) suspend inline fun BufferedImage.upload(): Image = upload(subject) - suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject) suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject) suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject) @@ -59,12 +59,11 @@ actual abstract class MessagePacket actual con // endregion 上传图片 (扩展) // region 发送图片 (扩展) - suspend inline fun BufferedImage.send() = sendTo(subject) - - suspend inline fun URL.sendAsImage() = sendAsImageTo(subject) - suspend inline fun Input.sendAsImage() = sendAsImageTo(subject) - suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject) - suspend inline fun File.sendAsImage() = sendAsImageTo(subject) + suspend inline fun BufferedImage.send(): MessageReceipt = sendTo(subject) + suspend inline fun URL.sendAsImage(): MessageReceipt = sendAsImageTo(subject) + suspend inline fun Input.sendAsImage(): MessageReceipt = sendAsImageTo(subject) + suspend inline fun InputStream.sendAsImage(): MessageReceipt = sendAsImageTo(subject) + suspend inline fun File.sendAsImage(): MessageReceipt = sendAsImageTo(subject) // endregion 发送图片 (扩展) // region 下载图片 (扩展) @@ -80,6 +79,26 @@ actual abstract class MessagePacket actual con */ suspend inline fun Image.downloadAndClose(output: OutputStream) = channel().copyAndClose(output) + /** + * 下载图片到 [output] 但不关闭这个 [output] + */ + suspend inline fun Image.downloadTo(output: Output) = channel().copyTo(output) + + /** + * 下载图片到 [output] 并关闭这个 [output] + */ + suspend inline fun Image.downloadAndClose(output: Output) = channel().copyAndClose(output) + + /** + * 下载图片到 [output] 但不关闭这个 [output] + */ + suspend inline fun Image.downloadTo(output: ByteWriteChannel) = channel().copyTo(output) + + /** + * 下载图片到 [output] 并关闭这个 [output] + */ + suspend inline fun Image.downloadAndClose(output: ByteWriteChannel) = channel().copyAndClose(output) + /* suspend inline fun Image.downloadAsStream(): InputStream = channel().asInputStream() suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { downloadAsStream().toExternalImage() } diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt index 3d708e3ed..0e07a3372 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessageReceipt.kt @@ -59,15 +59,7 @@ actual open class MessageReceipt actual constructor( actual suspend fun recall() { @Suppress("BooleanLiteralArgument") if (_isRecalled.compareAndSet(false, true)) { - when (val contact = target) { - is Group -> { - contact.bot.recall(source) - } - is QQ -> { - TODO() - } - else -> error("Unknown contact type") - } + target.bot.recall(source) } else error("message is already or planned to be recalled") } diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt index 4da850fcc..63e246166 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.data.Image +import net.mamoe.mirai.message.data.OfflineImage import net.mamoe.mirai.utils.OverFileSizeMaxException import net.mamoe.mirai.utils.sendTo import net.mamoe.mirai.utils.toExternalImage @@ -36,37 +37,37 @@ import java.net.URL * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun BufferedImage.sendTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) +suspend fun BufferedImage.sendTo(contact: C): MessageReceipt = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) /** * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun URL.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) +suspend fun URL.sendAsImageTo(contact: C): MessageReceipt = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) /** * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun Input.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) +suspend fun Input.sendAsImageTo(contact: C): MessageReceipt = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) /** * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun InputStream.sendAsImageTo(contact: Contact) = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) +suspend fun InputStream.sendAsImageTo(contact: C): MessageReceipt = withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) /** * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun File.sendAsImageTo(contact: Contact) { +suspend fun File.sendAsImageTo(contact: C): MessageReceipt { require(this.exists() && this.canRead()) - withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) + return withContext(Dispatchers.IO) { toExternalImage() }.sendTo(contact) } // endregion @@ -78,35 +79,35 @@ suspend fun File.sendAsImageTo(contact: Contact) { * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun BufferedImage.upload(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) +suspend fun BufferedImage.upload(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) /** * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传后构造 [Image] * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun URL.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) +suspend fun URL.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) /** * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传后构造 [Image] * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun Input.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) +suspend fun Input.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) /** * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传后构造 [Image] * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun InputStream.uploadAsImage(contact: Contact): Image = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) +suspend fun InputStream.uploadAsImage(contact: Contact): OfflineImage = withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) /** * 在 [Dispatchers.IO] 中将文件作为图片上传后构造 [Image] * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend fun File.uploadAsImage(contact: Contact): Image { +suspend fun File.uploadAsImage(contact: Contact): OfflineImage { require(this.exists() && this.canRead()) return withContext(Dispatchers.IO) { toExternalImage() }.upload(contact) } @@ -120,35 +121,35 @@ suspend fun File.uploadAsImage(contact: Contact): Image { * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.sendImage(bufferedImage: BufferedImage) = bufferedImage.sendTo(this) +suspend inline fun C.sendImage(bufferedImage: BufferedImage): MessageReceipt = bufferedImage.sendTo(this) /** * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.sendImage(imageUrl: URL) = imageUrl.sendAsImageTo(this) +suspend inline fun C.sendImage(imageUrl: URL): MessageReceipt = imageUrl.sendAsImageTo(this) /** * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.sendImage(imageInput: Input) = imageInput.sendAsImageTo(this) +suspend inline fun C.sendImage(imageInput: Input): MessageReceipt = imageInput.sendAsImageTo(this) /** * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.sendImage(imageStream: InputStream) = imageStream.sendAsImageTo(this) +suspend inline fun C.sendImage(imageStream: InputStream): MessageReceipt = imageStream.sendAsImageTo(this) /** * 在 [Dispatchers.IO] 中将文件作为图片发送到指定联系人 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this) +suspend inline fun C.sendImage(file: File): MessageReceipt = file.sendAsImageTo(this) // endregion @@ -159,34 +160,34 @@ suspend inline fun Contact.sendImage(file: File) = file.sendAsImageTo(this) * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): Image = bufferedImage.upload(this) +suspend inline fun Contact.uploadImage(bufferedImage: BufferedImage): OfflineImage = bufferedImage.upload(this) /** * 在 [Dispatchers.IO] 中下载 [URL] 到临时文件并将其作为图片上传, 但不发送 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.uploadImage(imageUrl: URL): Image = imageUrl.uploadAsImage(this) +suspend inline fun Contact.uploadImage(imageUrl: URL): OfflineImage = imageUrl.uploadAsImage(this) /** * 在 [Dispatchers.IO] 中读取 [Input] 到临时文件并将其作为图片上传, 但不发送 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.uploadImage(imageInput: Input): Image = imageInput.uploadAsImage(this) +suspend inline fun Contact.uploadImage(imageInput: Input): OfflineImage = imageInput.uploadAsImage(this) /** * 在 [Dispatchers.IO] 中读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.uploadImage(imageStream: InputStream): Image = imageStream.uploadAsImage(this) +suspend inline fun Contact.uploadImage(imageStream: InputStream): OfflineImage = imageStream.uploadAsImage(this) /** * 在 [Dispatchers.IO] 中将文件作为图片上传, 但不发送 * @throws OverFileSizeMaxException */ @Throws(OverFileSizeMaxException::class) -suspend inline fun Contact.uploadImage(file: File): Image = file.uploadAsImage(this) +suspend inline fun Contact.uploadImage(file: File): OfflineImage = file.uploadAsImage(this) // endregion \ No newline at end of file