Him188 пре 6 година
родитељ
комит
49c80c8eaf

+ 0 - 67
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/message/SendImageUtilsAndroid.kt

@@ -1,67 +0,0 @@
-@file:Suppress("unused")
-
-package net.mamoe.mirai.message
-
-import android.graphics.Bitmap
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.withContext
-import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.network.protocol.tim.packet.action.OverFileSizeMaxException
-import net.mamoe.mirai.utils.ExternalImage
-import net.mamoe.mirai.utils.sendTo
-import net.mamoe.mirai.utils.toExternalImage
-import net.mamoe.mirai.utils.upload
-import java.io.File
-import java.io.IOException
-import java.io.InputStream
-
-/*
- * 发送图片的一些扩展函数.
- */
-/**
- * 保存为临时文件然后调用 [File.toExternalImage]
- */
-@Throws(IOException::class)
-fun Bitmap.toExternalImage(): ExternalImage {
-    val file = createTempFile().apply { deleteOnExit() }
-    file.outputStream().use {
-        this.compress(Bitmap.CompressFormat.PNG, 100, it)
-    }
-    return file.toExternalImage()
-}
-
-/**
- * 在 [IO] 中进行 [InputStream.toExternalImage]
- */
-@Suppress("unused")
-suspend fun InputStream.suspendToExternalImage() = withContext(IO) { toExternalImage() }
-
-
-/**
- * 在 [Dispatchers.IO] 中将图片发送到指定联系人. 会创建临时文件
- * @throws OverFileSizeMaxException
- */
-@Throws(OverFileSizeMaxException::class)
-suspend fun Bitmap.sendTo(contact: Contact) = withContext(IO) { toExternalImage() }.sendTo(contact)
-
-/**
- * 在 [Dispatchers.IO] 中将图片上传后构造 [Image]. 会创建临时文件
- * @throws OverFileSizeMaxException
- */
-@Throws(OverFileSizeMaxException::class)
-suspend fun Bitmap.upload(contact: Contact): Image = withContext(IO) { toExternalImage() }.upload(contact)
-
-/**
- * 在 [Dispatchers.IO] 中将图片发送到指定联系人. 会保存临时文件
- * @throws OverFileSizeMaxException
- */
-@Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.sendImage(bitmap: Bitmap) = bitmap.sendTo(this)
-
-/**
- * 在 [Dispatchers.IO] 中将图片发送到指定联系人. 会保存临时文件
- * @throws OverFileSizeMaxException
- */
-@Throws(OverFileSizeMaxException::class)
-suspend inline fun Contact.uploadImage(bitmap: Bitmap): Image = bitmap.upload(this)

+ 1 - 7
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/network/BotSession.kt

@@ -11,12 +11,8 @@ import net.mamoe.mirai.Bot
 import net.mamoe.mirai.message.Image
 import net.mamoe.mirai.network.protocol.tim.handler.DataPacketSocketAdapter
 import net.mamoe.mirai.network.protocol.tim.packet.SessionKey
-import net.mamoe.mirai.utils.ExternalImage
 import net.mamoe.mirai.utils.InternalAPI
-import net.mamoe.mirai.utils.toExternalImage
-import java.io.File
 import java.io.InputStream
-import java.io.OutputStream
 
 /**
  * Android 平台相关扩展. 详情查看 [BotSessionBase]
@@ -33,8 +29,6 @@ actual class BotSession actual constructor(
 
     suspend inline fun Image.downloadAsStream(): InputStream = download().inputStream()
     suspend inline fun Image.downloadAsBitmap(): Bitmap = withContext(Dispatchers.IO) { downloadAsStream().use { BitmapFactory.decodeStream(it) } }
-    suspend inline fun Image.downloadAsExternalImage(): ExternalImage = download().use { it.toExternalImage() }
+    //suspend inline fun Image.downloadAsExternalImage(): ExternalImage = download().use { it.toExternalImage() }
 
-    suspend inline fun Image.downloadTo(file: File) = file.outputStream().use { downloadTo(it) }
-    suspend inline fun Image.downloadTo(output: OutputStream) = download().inputStream().use { input -> withContext(Dispatchers.IO) { input.copyTo(output) } }
 }

+ 5 - 6
mirai-core/src/androidMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/event/MessagePacket.kt

@@ -1,6 +1,5 @@
 package net.mamoe.mirai.network.protocol.tim.packet.event
 
-import android.graphics.Bitmap
 import kotlinx.io.core.Input
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.*
@@ -13,26 +12,26 @@ import java.net.URL
  * 平台相关扩展
  */
 @UseExperimental(InternalAPI::class)
-actual sealed class MessagePacket<TSubject : Contact> : MessagePacketBase<TSubject>() {
-    suspend inline fun uploadImage(image: Bitmap): Image = subject.uploadImage(image)
+actual abstract class MessagePacket<TSubject : Contact> : MessagePacketBase<TSubject>() {
+    //   suspend inline fun uploadImage(image: Bitmap): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
 
-    suspend inline fun sendImage(image: Bitmap) = subject.sendImage(image)
+    // suspend inline fun sendImage(image: Bitmap) = 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 Bitmap.upload(): Image = upload(subject)
+    //  suspend inline fun Bitmap.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)
     suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
 
-    suspend inline fun Bitmap.send() = sendTo(subject)
+    //  suspend inline fun Bitmap.send() = sendTo(subject)
     suspend inline fun URL.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)

+ 11 - 11
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt

@@ -203,17 +203,17 @@ data class Profile(
     override fun toString(): String = "Profile(qq=$qq, " +
             "nickname=$nickname, " +
             "gender=$gender, " +
-            (englishName?.plus("englishName=$englishName, ") ?: "") +
-            (chineseName?.plus("chineseName=$chineseName, ") ?: "") +
-            (qAge?.toString()?.plus("qAge=$qAge, ") ?: "") +
-            (zipCode?.plus("zipCode=$zipCode, ") ?: "") +
-            (phone?.plus("phone=$phone, ") ?: "") +
-            (birthday?.toString()?.plus("birthday=$birthday, ") ?: "") +
-            (personalStatement?.plus("personalStatement=$personalStatement, ") ?: "") +
-            (school?.plus("school=$school, ") ?: "") +
-            (homepage?.plus("homepage=$homepage, ") ?: "") +
-            (email?.plus("email=$email, ") ?: "") +
-            (company?.plus("company=$company?,") ?: "") +
+            (englishName?.let { "englishName=$englishName, " } ?: "") +
+            (chineseName?.let { "chineseName=$chineseName, " } ?: "") +
+            (qAge?.toString()?.let { "qAge=$qAge, " } ?: "") +
+            (zipCode?.let { "zipCode=$zipCode, " } ?: "") +
+            (phone?.let { "phone=$phone, " } ?: "") +
+            (birthday?.toString()?.let { "birthday=$birthday, " } ?: "") +
+            (personalStatement?.let { "personalStatement=$personalStatement, " } ?: "") +
+            (school?.let { "school=$school, " } ?: "") +
+            (homepage?.let { "homepage=$homepage, " } ?: "") +
+            (email?.let { "email=$email, " } ?: "") +
+            (company?.let { "company=$company?," } ?: "") +
             ")"// 最终会是 ", )", 但这并不影响什么.
 }
 

+ 11 - 4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/Message.kt

@@ -4,9 +4,11 @@ package net.mamoe.mirai.message
 
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.QQ
+import net.mamoe.mirai.network.protocol.tim.packet.action.FriendImagePacket
 import net.mamoe.mirai.utils.ExternalImage
 import kotlin.js.JsName
 import kotlin.jvm.Volatile
+import kotlin.reflect.KProperty
 
 // region Message Base
 /**
@@ -156,6 +158,8 @@ inline class Image(inline val id: ImageId) : Message {
     companion object Key : Message.Key<Image>
 }
 
+inline val Image.idValue: String get() = id.value
+
 /**
  * 图片的标识符. 由图片的数据产生.
  * 对于群, [value] 类似于 `{F61593B5-5B98-1798-3F47-2A91D32ED2FC}.jpg`, 由图片文件 MD5 直接产生.
@@ -171,7 +175,7 @@ fun ImageId.requireLength() = require(value.length == 37 || value.length == 42)
 
 fun ImageId.image(): Image = Image(this)
 
-suspend fun ImageId.sendTo(contact: Contact) = contact.sendMessage(this.image())
+suspend inline fun ImageId.sendTo(contact: Contact) = contact.sendMessage(this.image())
 
 // endregion
 
@@ -281,18 +285,18 @@ fun List<Message>.toMessageChain(): MessageChain = MessageChain(this)
 /**
  * 获取第一个 [M] 类型的 [Message] 实例
  */
-inline fun <reified M : Message> MessageChain.firstOrNull(): Message? = this.firstOrNull { M::class.isInstance(it) }
+inline fun <reified M : Message> MessageChain.firstOrNull(): Message? = this.firstOrNull { it is M }
 
 /**
  * 获取第一个 [M] 类型的 [Message] 实例
  * @throws [NoSuchElementException] 如果找不到该类型的实例
  */
-inline fun <reified M : Message> MessageChain.first(): Message = this.first { M::class.isInstance(it) }
+inline fun <reified M : Message> MessageChain.first(): Message = this.first { it is M }
 
 /**
  * 获取第一个 [M] 类型的 [Message] 实例
  */
-inline fun <reified M : Message> MessageChain.any(): Boolean = this.firstOrNull { M::class.isInstance(it) } !== null
+inline fun <reified M : Message> MessageChain.any(): Boolean = this.firstOrNull { it is M } !== null
 
 
 /**
@@ -355,8 +359,11 @@ interface MessageChain : Message, MutableList<Message> {
      */
     @Suppress("UNCHECKED_CAST")
     operator fun <M : Message> get(key: Message.Key<M>): M = first(key)
+
 }
 
+inline operator fun <reified T : Message> MessageChain.getValue(thisRef: Any?, property: KProperty<*>): T = this.first<T>() as T
+
 /**
  * 空的 [Message].
  *

+ 1 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Profile.kt

@@ -67,7 +67,7 @@ object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsR
             nickname = map[0x4E22u]?.encodeToString() ?: "",//error("Cannot determine nickname")
             englishName = map[0x4E54u]?.encodeToString(),
             chineseName = map[0x4E2Au]?.encodeToString(),
-            qAge = map[0x6597u]?.toUInt()?.toInt(),
+            qAge = map[0x6597u]?.get(0)?.toInt(),
             zipCode = map[0x4E25u]?.encodeToString(),
             phone = map[0x4E27u]?.encodeToString(),
             gender = when (map[0x4E29u]?.let { it[0] }?.toUInt()) {

+ 3 - 1
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/UploadImage.kt

@@ -227,6 +227,7 @@ object ImageOverFileSizeMax : ImageResponse {
 @AnnotatedId(KnownPacketId.FRIEND_IMAGE_ID)
 @PacketVersion(date = "2019.11.16", timVersion = "2.3.2 (21173)")
 object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
+    @Suppress("FunctionName")
     fun RequestImageId(
         bot: UInt,
         sessionKey: SessionKey,
@@ -272,6 +273,7 @@ object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
 
     }
 
+    @Suppress("FunctionName")
     fun RequestImageLink(
         bot: UInt,
         sessionKey: SessionKey,
@@ -416,7 +418,7 @@ object FriendImagePacket : SessionPacketFactory<ImageResponse>() {
                     discardExact(1)
                     discardExact(2)// [A4 04] 后文长度
                     check(readUByte().toUInt() == 0x0Au) { "Illegal identity. Required 0x0Au" }
-                    val imageId = ImageId(readString(readUByte().toInt()))
+                    /* val imageId = */ImageId(readString(readUByte().toInt()))
 
                     check(readUByte().toUInt() == 0x18u) { "Illegal identity. Required 0x18u" }
                     check(readUShort().toUInt() == 0x0032u) { "Illegal identity. Required 0x0032u" }

+ 13 - 3
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/event/Message.kt

@@ -23,12 +23,13 @@ import net.mamoe.mirai.utils.io.printTLVMap
 import net.mamoe.mirai.utils.io.read
 import net.mamoe.mirai.utils.io.readTLVMap
 import net.mamoe.mirai.utils.io.readUShortLVByteArray
+import kotlin.jvm.JvmName
 
 /**
  * 平台相关扩展
  */
 @UseExperimental(InternalAPI::class)
-expect sealed class MessagePacket<TSubject : Contact>() : MessagePacketBase<TSubject>
+expect abstract class MessagePacket<TSubject : Contact>() : MessagePacketBase<TSubject>
 
 @InternalAPI
 abstract class MessagePacketBase<TSubject : Contact> : EventPacket, BotEvent() {
@@ -63,8 +64,17 @@ abstract class MessagePacketBase<TSubject : Contact> : EventPacket, BotEvent() {
      */
     suspend inline fun reply(message: MessageChain) = subject.sendMessage(message)
 
-    suspend fun reply(message: Message) = subject.sendMessage(message.singleChain())
-    suspend fun reply(plain: String) = subject.sendMessage(plain.toMessage())
+    suspend inline fun reply(message: Message) = subject.sendMessage(message.singleChain())
+    suspend inline fun reply(plain: String) = subject.sendMessage(plain.toMessage())
+
+    @JvmName("reply1")
+    suspend inline fun String.reply() = reply(this)
+
+    @JvmName("reply1")
+    suspend inline fun Message.reply() = reply(this)
+
+    @JvmName("reply1")
+    suspend inline fun MessageChain.reply() = reply(this)
 
     suspend inline fun ExternalImage.send() = this.sendTo(subject)
 

+ 18 - 2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/network/protocol/tim/packet/event/MessagePacket.kt

@@ -2,20 +2,28 @@
 
 package net.mamoe.mirai.network.protocol.tim.packet.event
 
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
 import kotlinx.io.core.Input
+import kotlinx.io.core.use
+import kotlinx.io.streams.inputStream
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.*
+import net.mamoe.mirai.utils.ExternalImage
 import net.mamoe.mirai.utils.InternalAPI
+import net.mamoe.mirai.utils.toExternalImage
 import java.awt.image.BufferedImage
 import java.io.File
 import java.io.InputStream
+import java.io.OutputStream
 import java.net.URL
+import javax.imageio.ImageIO
 
 /**
- * 平台相关扩展
+ * JVM 平台相关扩展
  */
 @UseExperimental(InternalAPI::class)
-actual sealed class MessagePacket<TSubject : Contact> : MessagePacketBase<TSubject>() {
+actual abstract class MessagePacket<TSubject : Contact> : MessagePacketBase<TSubject>() {
     suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
@@ -39,4 +47,12 @@ actual sealed class MessagePacket<TSubject : Contact> : MessagePacketBase<TSubje
     suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun File.sendAsImage() = sendAsImageTo(subject)
+
+    suspend inline fun Image.downloadTo(file: File): Long = file.outputStream().use { downloadTo(it) }
+    suspend inline fun Image.downloadTo(output: OutputStream): Long =
+        download().inputStream().use { input -> withContext(Dispatchers.IO) { input.copyTo(output) } }
+
+    suspend inline fun Image.downloadAsStream(): InputStream = download().inputStream()
+    suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { download().toExternalImage() }
+    suspend inline fun Image.downloadAsBufferedImage(): BufferedImage = withContext(Dispatchers.IO) { ImageIO.read(downloadAsStream()) }
 }

+ 1 - 1
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt

@@ -121,7 +121,7 @@ suspend fun InputStream.suspendToExternalImage(): ExternalImage = withContext(IO
 fun Input.toExternalImage(): ExternalImage {
     val file = createTempFile().apply { deleteOnExit() }
     file.outputStream().use {
-        this.asStream()
+        this.asStream().transferTo(it)
     }
     return file.toExternalImage()
 }

+ 11 - 9
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt

@@ -12,14 +12,12 @@ import net.mamoe.mirai.event.Subscribable
 import net.mamoe.mirai.event.subscribeAlways
 import net.mamoe.mirai.event.subscribeMessages
 import net.mamoe.mirai.message.Image
+import net.mamoe.mirai.message.getValue
 import net.mamoe.mirai.message.sendAsImageTo
-import net.mamoe.mirai.network.protocol.tim.packet.action.download
-import net.mamoe.mirai.network.protocol.tim.packet.action.downloadAsByteArray
-import net.mamoe.mirai.network.protocol.tim.packet.action.downloadTo
 import net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
 import net.mamoe.mirai.network.protocol.tim.packet.login.requireSuccess
-import net.mamoe.mirai.utils.currentTime
 import java.io.File
+import java.util.*
 import javax.swing.filechooser.FileSystemView
 import kotlin.random.Random
 
@@ -62,16 +60,17 @@ suspend fun main() {
                 bot.getQQ(account.toUInt())
             } else {
                 sender
-            }.profile.await().toString()
+            }.profile.await().toString().reply()
         }
 
         has<Image> {
             if (this is FriendMessage) {
                 withContext(IO) {
-                    reply(message[Image] + " downloading")
+                    val image: Image by message
 
-                    sender.downloadTo(message[Image], File(System.getProperty("user.dir"), "testDownloadedImage${currentTime}.png").also { it.createNewFile() })
-                    reply(message[Image].id.value + " downloaded")
+                    reply(image + " downloading")
+                    image.downloadTo(newTestTempFile(suffix = ".png").also { reply("Temp file: ${it.absolutePath}") })
+                    reply(image.id.value + " downloaded")
                 }
             }
         }
@@ -103,4 +102,7 @@ suspend fun main() {
     }
 
     bot.network.awaitDisconnection()//等到直到断开连接
-}
+}
+
+private fun newTestTempFile(filename: String = "${UUID.randomUUID()}", suffix: String = ".tmp"): File =
+    File(System.getProperty("user.dir"), filename + suffix).also { it.createNewFile(); it.deleteOnExit() }