Sfoglia il codice sorgente

Integrate Voice APIs with ExternalResource

Him188 5 anni fa
parent
commit
42f824d516

+ 5 - 5
mirai-core-api/src/commonMain/kotlin/contact/Group.kt

@@ -18,9 +18,9 @@ import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.MessageReceipt.Companion.recall
 import net.mamoe.mirai.message.data.*
+import net.mamoe.mirai.utils.ExternalResource
 import net.mamoe.mirai.utils.MiraiExperimentalApi
 import net.mamoe.mirai.utils.OverFileSizeMaxException
-import java.io.InputStream
 
 /**
  * 群.
@@ -154,8 +154,9 @@ public interface Group : Contact, CoroutineScope {
 
     /**
      * 上传一个语音消息以备发送.
-     * 请手动关闭输入流
-     * 请使用 amr 或 silk 格式
+     *
+     * - 请手动关闭输入流
+     * - 请使用 amr 或 silk 格式
      *
      * @suppress 这是一个实验性 API 且随时会被删除
      *
@@ -163,8 +164,7 @@ public interface Group : Contact, CoroutineScope {
      * @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
      */
     @JvmBlockingBridge
-    @MiraiExperimentalApi
-    public suspend fun uploadVoice(input: InputStream): Voice
+    public suspend fun uploadVoice(resource: ExternalResource): Voice
 
     public companion object
 }

+ 2 - 1
mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt

@@ -39,7 +39,8 @@ public class Voice @MiraiInternalApi constructor(
     public override val fileName: String,
     public override val md5: ByteArray,
     public override val fileSize: Long,
-    public val codec: Int = 0,
+
+    @MiraiInternalApi public val codec: Int = 0,
     private val _url: String
 ) : PttMessage() {
 

+ 4 - 4
mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt

@@ -239,11 +239,11 @@ public interface ExternalResource : Closeable {
          * @suppress 注意,这只是个实验性功能且随时可能会删除
          * @throws OverFileSizeMaxException
          */
-        @Throws(OverFileSizeMaxException::class)
-        @MiraiExperimentalApi("语音支持处于实验性阶段")
         @JvmBlockingBridge
         @JvmStatic
-        public suspend fun InputStream.uploadAsGroupVoice(group: Group): Voice {
+        @Throws(OverFileSizeMaxException::class)
+        @MiraiExperimentalApi
+        public suspend fun ExternalResource.uploadAsVoice(group: Group): Voice {
             return group.uploadVoice(this)
         }
     }
@@ -251,7 +251,7 @@ public interface ExternalResource : Closeable {
 
 
 private fun InputStream.detectFileTypeAndClose(): String? {
-    val buffer = ByteArray(8)
+    val buffer = ByteArray(10)
     return use {
         kotlin.runCatching { it.read(buffer) }.onFailure { return null }
         getFileType(buffer)

+ 4 - 0
mirai-core-utils/src/commonMain/kotlin/Files.kt

@@ -22,6 +22,10 @@ public val FILE_TYPES: MutableMap<String, String> = mutableMapOf(
     "49492A00" to "tif",
     "424D" to "bmp",
     "57415645" to "wav",
+
+
+    "2321414D52" to "amr",
+    "02232153494C4B5F5633" to "silk",
 )
 
 /*

+ 13 - 19
mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt

@@ -30,12 +30,12 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
 import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
 import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToGroup
 import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
+import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
 import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
 import net.mamoe.mirai.internal.utils.GroupPkgMsgParsingCache
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.*
-import java.io.InputStream
 import java.util.concurrent.ConcurrentLinkedQueue
 import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
@@ -273,34 +273,28 @@ internal class GroupImpl(
      * @throws EventCancelledException 当发送消息事件被取消
      * @throws OverFileSizeMaxException 当语音文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
      */
-    @MiraiExperimentalApi
-    override suspend fun uploadVoice(input: InputStream): Voice {
-        val content = ByteArray(input.available())
-        input.read(content)
-        if (content.size > 1048576) {
+    override suspend fun uploadVoice(resource: ExternalResource): Voice {
+        if (resource.size > 1048576) {
             throw  OverFileSizeMaxException()
         }
-        val md5 = content.md5()
-        val codec = with(content.copyOfRange(0, 10).toUHexString("")) {
-            when {
-                startsWith("2321414D52") -> 0             // amr
-                startsWith("02232153494C4B5F5633") -> 1  // silk V3
-                else -> 0                               // use amr by default
-            }
-        }
         return bot.network.run {
             val response: PttStore.GroupPttUp.Response.RequireUpload =
-                PttStore.GroupPttUp(bot.client, bot.id, id, md5, content.size.toLong(), codec).sendAndExpect()
+                PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect()
+
             HighwayHelper.uploadPttToServers(
                 bot,
                 response.uploadIpList.zip(response.uploadPortList),
-                content,
-                md5,
+                resource,
                 response.uKey,
                 response.fileKey,
-                codec
             )
-            Voice("${md5.toUHexString("")}.amr", md5, content.size.toLong(), codec, "")
+            Voice(
+                "${resource.md5.toUHexString("")}.${resource.formatName}",
+                resource.md5,
+                resource.size,
+                resource.voiceCodec,
+                ""
+            )
         }
 
     }

+ 10 - 13
mirai-core/src/commonMain/kotlin/network/highway/HighwayHelper.kt

@@ -24,6 +24,7 @@ import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.network.QQAndroidClient
 import net.mamoe.mirai.internal.network.protocol.data.proto.CSDataHighwayHead
 import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
+import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
 import net.mamoe.mirai.internal.utils.PlatformSocket
 import net.mamoe.mirai.internal.utils.SocketException
 import net.mamoe.mirai.internal.utils.addSuppressedMirai
@@ -174,23 +175,21 @@ internal object HighwayHelper {
     suspend fun uploadPttToServers(
         bot: QQAndroidBot,
         servers: List<Pair<Int, Int>>,
-        content: ByteArray,
-        md5: ByteArray,
+        resource: ExternalResource,
         uKey: ByteArray,
         fileKey: ByteArray,
-        codec: Int
     ) {
         servers.retryWithServers(10 * 1000, {
             throw IllegalStateException("cannot upload ptt, failed on all servers.", it)
         }, { s: String, i: Int ->
             bot.network.logger.verbose {
-                "[Highway] Uploading ptt to ${s}:$i, size=${content.size.toLong().sizeToString()}"
+                "[Highway] Uploading ptt to ${s}:$i, size=${resource.size.sizeToString()}"
             }
             val time = measureTime {
-                uploadPttToServer(s, i, content, md5, uKey, fileKey, codec)
+                uploadPttToServer(s, i, resource, uKey, fileKey)
             }
             bot.network.logger.verbose {
-                "[Highway] Uploading ptt: succeed at ${(content.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
+                "[Highway] Uploading ptt: succeed at ${(resource.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
             }
 
         })
@@ -200,22 +199,20 @@ internal object HighwayHelper {
     private suspend fun uploadPttToServer(
         serverIp: String,
         serverPort: Int,
-        content: ByteArray,
-        md5: ByteArray,
+        resource: ExternalResource,
         uKey: ByteArray,
         fileKey: ByteArray,
-        codec: Int
     ) {
         MiraiPlatformUtils.Http.post<String> {
             url("http://$serverIp:$serverPort")
             parameter("ver", 4679)
             parameter("ukey", uKey.toUHexString(""))
             parameter("filekey", fileKey.toUHexString(""))
-            parameter("filesize", content.size)
-            parameter("bmd5", md5.toUHexString(""))
+            parameter("filesize", resource.size)
+            parameter("bmd5", resource.md5.toUHexString(""))
             parameter("mType", "pttDu")
-            parameter("voice_encodec", codec)
-            body = content
+            parameter("voice_encodec", resource.voiceCodec)
+            body = resource.inputStream()
         }
     }
 }

+ 15 - 7
mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/voice/PttStore.kt

@@ -20,8 +20,18 @@ import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
 import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
 import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
 import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
+import net.mamoe.mirai.utils.ExternalResource
 import net.mamoe.mirai.utils.encodeToString
 
+internal val ExternalResource.voiceCodec: Int
+    get() {
+        return when (formatName) {
+            "amr" -> 0  // amr
+            "silk" -> 1  // silk V3
+            else -> 0     // use amr by default
+        }
+    }
+
 internal class PttStore {
     object GroupPttUp : OutgoingPacketFactory<GroupPttUp.Response>("PttStore.GroupPttUp") {
 
@@ -45,9 +55,7 @@ internal class PttStore {
             client: QQAndroidClient,
             uin: Long,
             groupCode: Long,
-            md5: ByteArray,
-            size: Long,
-            codec: Int = 0
+            resource: ExternalResource
         ): OutgoingPacket {
             val pack = Cmd0x388.ReqBody(
                 netType = 3, // wifi
@@ -57,16 +65,16 @@ internal class PttStore {
                         srcUin = uin,
                         groupCode = groupCode,
                         fileId = 0,
-                        fileSize = size,
-                        fileMd5 = md5,
-                        fileName = md5,
+                        fileSize = resource.size,
+                        fileMd5 = resource.md5,
+                        fileName = resource.md5,
                         srcTerm = 5,
                         platformType = 9,
                         buType = 4,
                         innerIp = 0,
                         buildVer = "6.5.5.663".encodeToByteArray(),
                         voiceLength = 1,
-                        codec = codec,
+                        codec = resource.voiceCodec,
                         voiceType = 1,
                         boolNewUpChan = true
                     )