2
0
Эх сурвалжийг харах

simple api for send group voice message #423

mzdluo123 5 жил өмнө
parent
commit
f96c20767d

+ 70 - 36
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt

@@ -12,9 +12,7 @@
 
 package net.mamoe.mirai.qqandroid.contact
 
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.*
 import kotlinx.io.core.Closeable
 import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.contact.*
@@ -33,9 +31,13 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.createToGroup
+import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.voice.PttStore
 import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService
+import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
 import net.mamoe.mirai.qqandroid.utils.estimateLength
+import net.mamoe.mirai.qqandroid.utils.toUHexString
 import net.mamoe.mirai.utils.*
+import java.io.InputStream
 import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 import kotlin.jvm.JvmSynthetic
@@ -130,18 +132,18 @@ internal class GroupImpl(
             set(newValue) {
                 checkBotPermission(MemberPermission.ADMINISTRATOR)
                 //if (_announcement != newValue) {
-                    val oldValue = _announcement
-                    _announcement = newValue
-                    launch {
-                        bot.network.run {
-                            TroopManagement.GroupOperation.memo(
-                                client = bot.client,
-                                groupCode = id,
-                                newMemo = newValue
-                            ).sendWithoutExpect()
-                        }
-                        GroupEntranceAnnouncementChangeEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                val oldValue = _announcement
+                _announcement = newValue
+                launch {
+                    bot.network.run {
+                        TroopManagement.GroupOperation.memo(
+                            client = bot.client,
+                            groupCode = id,
+                            newMemo = newValue
+                        ).sendWithoutExpect()
                     }
+                    GroupEntranceAnnouncementChangeEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                }
                 //}
             }
 
@@ -151,18 +153,18 @@ internal class GroupImpl(
             set(newValue) {
                 checkBotPermission(MemberPermission.ADMINISTRATOR)
                 //if (_allowMemberInvite != newValue) {
-                    val oldValue = _allowMemberInvite
-                    _allowMemberInvite = newValue
-                    launch {
-                        bot.network.run {
-                            TroopManagement.GroupOperation.allowMemberInvite(
-                                client = bot.client,
-                                groupCode = id,
-                                switch = newValue
-                            ).sendWithoutExpect()
-                        }
-                        GroupAllowMemberInviteEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                val oldValue = _allowMemberInvite
+                _allowMemberInvite = newValue
+                launch {
+                    bot.network.run {
+                        TroopManagement.GroupOperation.allowMemberInvite(
+                            client = bot.client,
+                            groupCode = id,
+                            switch = newValue
+                        ).sendWithoutExpect()
                     }
+                    GroupAllowMemberInviteEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                }
                 //}
             }
 
@@ -208,18 +210,18 @@ internal class GroupImpl(
             set(newValue) {
                 checkBotPermission(MemberPermission.ADMINISTRATOR)
                 //if (_muteAll != newValue) {
-                    val oldValue = _muteAll
-                    _muteAll = newValue
-                    launch {
-                        bot.network.run {
-                            TroopManagement.GroupOperation.muteAll(
-                                client = bot.client,
-                                groupCode = id,
-                                switch = newValue
-                            ).sendWithoutExpect()
-                        }
-                        GroupMuteAllEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                val oldValue = _muteAll
+                _muteAll = newValue
+                launch {
+                    bot.network.run {
+                        TroopManagement.GroupOperation.muteAll(
+                            client = bot.client,
+                            groupCode = id,
+                            switch = newValue
+                        ).sendWithoutExpect()
                     }
+                    GroupMuteAllEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
+                }
                 //}
             }
     }
@@ -443,5 +445,37 @@ internal class GroupImpl(
         (image.input as? Closeable)?.close()
     }
 
+    /**
+     * 上传一个语音消息以备发送.
+     * 请注意,这是一个实验性api且随时会被删除
+     * @throws EventCancelledException 当发送消息事件被取消
+     * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
+     */
+    @JvmSynthetic
+    @MiraiExperimentalAPI
+    override suspend fun uploadGroupVoice(input: InputStream): Voice {
+        val content = ByteArray(input.available())
+        input.read(content)
+        if (content.size > 1048576) {
+            throw  OverFileSizeMaxException()
+        }
+        val md5 = MiraiPlatformUtils.md5(content)
+        return bot.network.run {
+            val response: PttStore.GroupPttUp.Response.RequireUpload =
+                PttStore.GroupPttUp(bot.client, bot.id, 0L, md5, content.size.toLong()).sendAndExpect()
+            HighwayHelper.uploadPttToServers(
+                bot,
+                response.uploadIpList.zip(response.uploadPortList),
+                content,
+                md5,
+                response.uKey,
+                response.fileKey
+            )
+            Voice("${md5.toUHexString("")}.amr", md5, content.size.toLong(), "")
+        }
+
+    }
+
+
     override fun toString(): String = "Group($id)"
 }

+ 4 - 5
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/voice/PttStore.kt

@@ -37,9 +37,8 @@ internal class PttStore {
             uin: Long,
             groupCode: Long,
             md5: ByteArray,
-            size: Long = 0,
-            voiceLength: Int = 0,
-            fileId: Long = 0
+            size: Long,
+            codec: Int = 0
         ): OutgoingPacket {
             val pack = Cmd0x388.ReqBody(
                 netType = 3, // wifi
@@ -57,8 +56,8 @@ internal class PttStore {
                         buType = 4,
                         innerIp = 0,
                         buildVer = "6.5.5.663".encodeToByteArray(),
-                        voiceLength = voiceLength,
-                        codec = 0,
+                        voiceLength = 1,
+                        codec = codec,
                         voiceType = 1,
                         boolNewUpChan = true
                     )

+ 15 - 4
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt

@@ -18,13 +18,11 @@ import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.data.MemberInfo
 import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.message.MessageReceipt
-import net.mamoe.mirai.message.data.Image
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.isContentEmpty
-import net.mamoe.mirai.message.data.toMessage
+import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.message.recall
 import net.mamoe.mirai.utils.*
 import net.mamoe.mirai.utils.internal.runBlocking
+import java.io.InputStream
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmStatic
 import kotlin.jvm.JvmSynthetic
@@ -174,6 +172,19 @@ public abstract class Group : Contact(), CoroutineScope {
     @JvmSynthetic
     public abstract override suspend fun uploadImage(image: ExternalImage): Image
 
+    /**
+     * 上传一个语音消息以备发送.
+     * 请手动关闭输入流
+     * 请使用mar格式
+     * 请注意,这是一个实验性api且随时会被删除
+     * @throws EventCancelledException 当发送消息事件被取消
+     * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 1 MB)
+     */
+    @JvmSynthetic
+    @MiraiExperimentalAPI
+    public abstract suspend fun uploadGroupVoice(input: InputStream): Voice
+
+
     public companion object {
         /**
          * 使用 groupCode 计算 groupUin. 这两个值仅在 mirai 内部协议区分, 一般人使用时无需在意.

+ 5 - 2
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt

@@ -159,8 +159,11 @@ public open class BotConfigurationBase internal constructor() {
     @MiraiExperimentalAPI
     public var json: Json = kotlin.runCatching {
         @OptIn(UnstableDefault::class)
-        Json(JsonConfiguration(isLenient = true, ignoreUnknownKeys = true))
-    }.getOrElse { Json(JsonConfiguration.Stable) }
+        Json {
+            isLenient = true
+            ignoreUnknownKeys = true
+        }
+    }.getOrElse { Json {} }
 
     /**
      * 不显示网络日志. 不推荐.

+ 16 - 5
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/SendImageUtilsJvm.kt

@@ -12,14 +12,12 @@
 package net.mamoe.mirai.message
 
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
 import kotlinx.io.core.Input
 import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.contact.Group
 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 net.mamoe.mirai.message.data.Voice
+import net.mamoe.mirai.utils.*
 import java.awt.image.BufferedImage
 import java.io.File
 import java.io.InputStream
@@ -120,6 +118,19 @@ public suspend fun File.uploadAsImage(contact: Contact): Image {
     return toExternalImage().upload(contact)
 }
 
+/**
+ * 在 [Dispatchers.IO] 中将文件作为语音上传后构造 [Image]
+ * 请手动关闭输入流
+ * 请使用mar格式
+ * 注意,这只是个实验性功能且随时可能会删除
+ * @throws OverFileSizeMaxException
+ */
+@Throws(OverFileSizeMaxException::class)
+@MiraiExperimentalAPI
+public suspend fun InputStream.uploadAsGroupVoice(group: Group): Voice {
+    return group.uploadGroupVoice(this)
+}
+
 // endregion
 
 // region Contact.sendImage(IMAGE)