From f96c20767d489b2fb1eda50e04f8d96447ef6059 Mon Sep 17 00:00:00 2001 From: mzdluo123 Date: Mon, 17 Aug 2020 22:43:22 +0800 Subject: [PATCH] simple api for send group voice message #423 --- .../mirai/qqandroid/contact/GroupImpl.kt | 106 ++++++++++++------ .../protocol/packet/chat/voice/PttStore.kt | 9 +- .../kotlin/net.mamoe.mirai/contact/Group.kt | 19 +++- .../utils/BotConfiguration.common.kt | 7 +- .../mamoe/mirai/message/SendImageUtilsJvm.kt | 21 +++- 5 files changed, 110 insertions(+), 52 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt index aa84741b5..19c57f841 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt +++ b/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)" } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/voice/PttStore.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/voice/PttStore.kt index 41e3447b5..58839aaa6 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/voice/PttStore.kt +++ b/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 ) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt index 45fe6d781..5dea700df 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt +++ b/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 内部协议区分, 一般人使用时无需在意. diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt index 9d1aadcce..1e65da681 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt +++ b/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 {} } /** * 不显示网络日志. 不推荐. 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 4ec7a9380..5ba1a4580 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 @@ -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)