diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt index c2f921bee..0fa413a0c 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt @@ -11,7 +11,6 @@ package net.mamoe.mirai.qqandroid -import io.ktor.client.HttpClient import io.ktor.client.request.* import io.ktor.client.request.forms.MultiPartFormDataContent import io.ktor.client.request.forms.formData @@ -45,6 +44,7 @@ import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.LongMsg import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.* +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.voice.PttStore import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils import net.mamoe.mirai.qqandroid.utils.encodeToString @@ -56,6 +56,7 @@ import kotlin.coroutines.CoroutineContext import kotlin.jvm.JvmField import kotlin.jvm.JvmSynthetic import kotlin.math.absoluteValue +import kotlin.math.log import kotlin.random.Random import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo @@ -560,7 +561,7 @@ internal abstract class QQAndroidBotBase constructor( @MiraiExperimentalAPI override suspend fun _lowLevelGetAnnouncement(groupId: Long, fid: String): GroupAnnouncement { val data = network.async { - HttpClient().post { + MiraiPlatformUtils.Http.post { url("https://web.qun.qq.com/cgi-bin/announce/get_feed") body = MultiPartFormDataContent(formData { append("qid", groupId) @@ -587,7 +588,7 @@ internal abstract class QQAndroidBotBase constructor( @MiraiExperimentalAPI override suspend fun _lowLevelGetGroupActiveData(groupId: Long, page: Int): GroupActiveData { val data = network.async { - HttpClient().get { + MiraiPlatformUtils.Http.get { url("https://qqweb.qq.com/c/activedata/get_mygroup_data") parameter("bkn", bkn) parameter("gc", groupId) @@ -792,6 +793,21 @@ internal abstract class QQAndroidBotBase constructor( } } + @ExperimentalStdlibApi + @MiraiExperimentalAPI + @LowLevelAPI + override suspend fun _lowLevelQueryGroupVoiceDownloadUrl( + md5: ByteArray, + groupId: Long, + dstUin: Long + ): String { + network.run { + val response: PttStore.GroupPttDown.Response.DownLoadInfo = + PttStore.GroupPttDown(client, groupId, dstUin,md5).sendAndExpect() + return "http://${response.strDomain}${response.downPara.encodeToString()}" + } + } + @Suppress("DEPRECATION", "OverridingDeprecatedMember") override suspend fun queryImageUrl(image: Image): String = when (image) { is ConstOriginUrlAware -> image.originUrl diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/conversions.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/conversions.kt index 321022267..3041ae388 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/conversions.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/conversions.kt @@ -32,7 +32,9 @@ import kotlin.contracts.contract private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。") private val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。") private val UNSUPPORTED_FLASH_MESSAGE_PLAIN = PlainText("[闪照]请使用新版手机QQ查看闪照。") +private val UNSUPPORTED_VOICE_MESSAGE_PLAIN = PlainText("收到语音消息,你需要升级到最新版QQ才能接收,升级地址https://im.qq.com") +@OptIn(ExperimentalStdlibApi::class) @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: Boolean): MutableList { val elements = ArrayList(this.size) @@ -156,7 +158,20 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B is VipFace -> { transformOneMessage(PlainText(it.contentToString())) } - is PttMessage, + is PttMessage -> { + elements.add( + ImMsgBody.Elem( + extraInfo = ImMsgBody.ExtraInfo(flags = 16, groupMask = 1) + ) + ) + elements.add( + ImMsgBody.Elem( + elemFlags2 = ImMsgBody.ElemFlags2( + vipStatus = 1 + ) + ) + ) + } is ForwardMessage, is MessageSource, // mirai metadata only is RichMessage // already transformed above @@ -218,10 +233,11 @@ internal fun MsgComm.Msg.toMessageChain( val ptt = this.msgBody.richText.ptt val pptMsg = ptt?.run { - when(fileType) { - 4 -> Voice(String(fileName), fileMd5, String(downPara)) - else -> null - } +// when (fileType) { +// 4 -> Voice(String(fileName), fileMd5, fileSize.toLong(),String(downPara)) +// else -> null +// } + Voice(String(fileName), fileMd5, fileSize.toLong(),String(downPara)) } return buildMessageChain(elements.size + 1 + if (pptMsg == null) 0 else 1) { @@ -274,13 +290,13 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain { return@forEach } } - if (last is FlashImage && element is PlainText) { - if (element == UNSUPPORTED_FLASH_MESSAGE_PLAIN) { + // 解决tim发送的语音无法正常识别 + if (element is PlainText) { + if (element == UNSUPPORTED_VOICE_MESSAGE_PLAIN) { last = element return@forEach } } - add(element) last = element } @@ -431,7 +447,8 @@ internal fun List.joinToMessageChain(groupIdOrZero: Long, bot: B .orEmpty(), proto.pokeType, proto.vaspokeId - )) + ) + ) } 3 -> { val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer()) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt index e3fa1df69..ade0c396b 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt @@ -12,7 +12,10 @@ package net.mamoe.mirai.qqandroid.network.highway import io.ktor.client.HttpClient +import io.ktor.client.request.parameter +import io.ktor.client.request.port import io.ktor.client.request.post +import io.ktor.client.request.url import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.http.URLProtocol @@ -29,8 +32,9 @@ import kotlinx.io.core.use import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x388 +import net.mamoe.mirai.qqandroid.utils.* import net.mamoe.mirai.qqandroid.utils.PlatformSocket -import net.mamoe.mirai.qqandroid.utils.SocketException import net.mamoe.mirai.qqandroid.utils.addSuppressedMirai import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.withUse @@ -180,6 +184,50 @@ internal object HighwayHelper { } } } + + suspend fun uploadPttToServers( + bot: QQAndroidBot, + servers: List>, + content: ByteArray, + md5: ByteArray, + uKey: ByteArray, fileKey: ByteArray + ) { + 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()}" + } + val time = measureTime { + uploadPttToServer(s, i, content, md5, uKey, fileKey) + } + bot.network.logger.verbose { + "[Highway] Uploading ptt: succeed at ${(content.size.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s" + } + + }) + + } + + private suspend fun uploadPttToServer( + serverIp: String, + serverPort: Int, + content: ByteArray, + md5: ByteArray, + uKey: ByteArray, fileKey: ByteArray + ) { + MiraiPlatformUtils.Http.post { + url("http://$serverIp:$serverPort") + parameter("ver", 4679) + parameter("ukey", uKey.toUHexString("")) + parameter("filekey", fileKey.toUHexString("")) + parameter("filesize", content.size) + parameter("bmd5", md5.toUHexString("")) + parameter("mType", "pttDu") + parameter("voice_encodec", 0) + body = content + } + } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x388.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x388.kt index 8796bd66b..5cc60bbda 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x388.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/Cmd0x388.kt @@ -273,7 +273,7 @@ internal class TryUpPttReq( internal class TryUpPttRsp( @ProtoId(1) @JvmField val fileId: Long = 0L, @ProtoId(2) @JvmField val result: Int = 0, - @ProtoId(3) @JvmField val failMsg: ByteArray = EMPTY_BYTE_ARRAY, + @ProtoId(3) @JvmField val failMsg: ByteArray? = null, @ProtoId(4) @JvmField val boolFileExit: Boolean = false, @ProtoId(5) @JvmField val uint32UpIp: List? = null, @ProtoId(6) @JvmField val uint32UpPort: List? = null, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index b6784ddc7..7fdceb627 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -20,6 +20,7 @@ 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.image.LongConn import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.* +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.voice.PttStore import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc @@ -139,6 +140,8 @@ internal object KnownPacketFactories { FriendList.GetTroopListSimplify, FriendList.GetTroopMemberList, ImgStore.GroupPicUp, + PttStore.GroupPttUp, + PttStore.GroupPttDown, LongConn.OffPicUp, LongConn.OffPicDown, TroopManagement.EditSpecialTitle, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt index 956725041..6e3a8610c 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt @@ -30,6 +30,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.qqandroid.utils.hexToBytes import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf @@ -138,7 +139,14 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory("Onlin // 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00 if (!bot.firstLoginSucceed) return null val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) + // bot.logger.debug(pbPushMsg._miraiContentToString()) if (pbPushMsg.msg.msgHead.fromUin == bot.id) { return SendGroupMessageReceipt( 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 new file mode 100644 index 000000000..2b7f46630 --- /dev/null +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/voice/PttStore.kt @@ -0,0 +1,160 @@ +package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.voice + +import kotlinx.io.core.ByteReadPacket +import net.mamoe.mirai.qqandroid.EMPTY_BYTE_ARRAY +import net.mamoe.mirai.qqandroid.QQAndroidBot +import net.mamoe.mirai.qqandroid.network.Packet +import net.mamoe.mirai.qqandroid.network.QQAndroidClient +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x388 +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory +import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.getRandomString +import net.mamoe.mirai.qqandroid.utils._miraiContentToString +import net.mamoe.mirai.qqandroid.utils.encodeToString +import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf +import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.qqandroid.utils.toUHexString + +internal class PttStore { + object GroupPttUp : OutgoingPacketFactory("PttStore.GroupPttUp") { + + sealed class Response : Packet { + + class RequireUpload( + val fileId: Long, + val uKey: ByteArray, + val uploadIpList: List, + val uploadPortList: List, + val fileKey: ByteArray + ) : GroupPttUp.Response() { + override fun toString(): String { + return "RequireUpload(fileId=$fileId, uKey=${uKey.contentToString()})" + } + } + } + + + @ExperimentalStdlibApi + operator fun invoke( + client: QQAndroidClient, + uin: Long, + groupCode: Long, + md5: ByteArray, + size: Long, + voiceLength: Int, + fileId: Long = 0 + ): OutgoingPacket { + val pack = Cmd0x388.ReqBody( + netType = 3, // wifi + subcmd = 3, + msgTryupPttReq = listOf( + Cmd0x388.TryUpPttReq( + srcUin = uin, + groupCode = groupCode, + fileId = fileId, + fileSize = size, + fileMd5 = md5, + fileName = md5, + srcTerm = 5, + platformType = 9, + buType = 4, + innerIp = 0, + buildVer = "6.5.5.663".encodeToByteArray(), + voiceLength = voiceLength, + codec = 0, + voiceType = 1, + boolNewUpChan = true + ) + ) + ) + return buildOutgoingUniPacket(client) { + writeProtoBuf(Cmd0x388.ReqBody.serializer(), pack) + } + } + + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val resp0 = readProtoBuf(Cmd0x388.RspBody.serializer()) + resp0.msgTryupPttRsp ?: error("cannot find `msgTryupPttRsp` from `Cmd0x388.RspBody`") + val resp = resp0.msgTryupPttRsp.first() + if (resp.failMsg != null) { + throw IllegalStateException(resp.failMsg.encodeToString()) + } + return Response.RequireUpload( + fileId = resp.fileid, + uKey = resp.upUkey, + uploadIpList = resp.uint32UpIp!!, + uploadPortList = resp.uint32UpPort!!, + fileKey = resp.fileKey + ) + + } + + } + + object GroupPttDown : OutgoingPacketFactory("PttStore.GroupPttDown") { + + sealed class Response() : Packet { + class DownLoadInfo( + val downDomain: ByteArray, + val downPara:ByteArray, + val strDomain:String, + val uint32DownIp:List, + val uint32DownPort:List + ) : GroupPttDown.Response() { + override fun toString(): String { + return "GroupPttDown(downPara=${downPara.encodeToString()},strDomain=$strDomain})" + } + } + + } + + @ExperimentalStdlibApi + operator fun invoke( + client: QQAndroidClient, + groupCode: Long, + dstUin:Long, + md5: ByteArray + + ): OutgoingPacket = buildOutgoingUniPacket(client) { + writeProtoBuf( + Cmd0x388.ReqBody.serializer(), Cmd0x388.ReqBody( + netType = 3, // wifi + subcmd = 4, + msgGetpttUrlReq = listOf( + Cmd0x388.GetPttUrlReq( + groupCode = groupCode, + fileMd5 = md5, + dstUin = dstUin, + buType = 4, + innerIp = 0, + buildVer = "6.5.5.663".encodeToByteArray(), + codec = 0, + reqTerm = 5, + reqPlatformType = 9 + ) + ) + ) + ) + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val resp0 = readProtoBuf(Cmd0x388.RspBody.serializer()) + resp0.msgGetpttUrlRsp ?: error("cannot find `msgGetpttUrlRsp` from `Cmd0x388.RspBody`") + val resp = resp0.msgGetpttUrlRsp.first() + if (!resp.failMsg.contentEquals(EMPTY_BYTE_ARRAY)){ + throw IllegalStateException(resp.failMsg.encodeToString()) + } + return Response.DownLoadInfo( + downDomain = resp.downDomain, + downPara = resp.downPara, + uint32DownIp = resp.uint32DownIp!!, + uint32DownPort = resp.uint32DownPort!!, + strDomain = resp.strDomain + ) + } + } + +} diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt index ec3feace9..3cc35053a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt @@ -16,6 +16,7 @@ import net.mamoe.mirai.data.* import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent import net.mamoe.mirai.event.events.MemberJoinRequestEvent import net.mamoe.mirai.event.events.NewFriendRequestEvent +import net.mamoe.mirai.message.data.Voice import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.WeakRef @@ -159,4 +160,13 @@ interface LowLevelBotAPIAccessor { blackList: Boolean, message: String = "" ) + + /** + * 查询语音的下载连接 + * + * */ + + @LowLevelAPI + @MiraiExperimentalAPI + suspend fun _lowLevelQueryGroupVoiceDownloadUrl(md5: ByteArray, groupId: Long, dstUin: Long): String } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Voice.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Voice.kt index 444c3f8a8..949d7e18b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Voice.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Voice.kt @@ -15,6 +15,7 @@ abstract class PttMessage : MessageContent { abstract val fileName: String abstract val md5: ByteArray + abstract val fileSize: Long } @@ -25,6 +26,7 @@ abstract class PttMessage : MessageContent { class Voice( override val fileName: String, override val md5: ByteArray, + override val fileSize: Long, private val _url: String ) : PttMessage() { @@ -33,9 +35,9 @@ class Voice( get() = "Voice" } - val url: String + val url: String? get() = if (_url.startsWith("http")) _url - else "http://grouptalk.c2c.qq.com$_url" + else null private var _stringValue: String? = null get() = field ?: kotlin.run {