diff --git a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt index deb3f571098..603d5244c58 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor import net.mamoe.mirai.internal.network.components.SsoProcessor import net.mamoe.mirai.internal.network.notice.group.GroupMessageProcessor import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore import net.mamoe.mirai.utils.assertUnreachable import net.mamoe.mirai.utils.context @@ -48,6 +49,16 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor(type 166, 167, // 单向好友 208, // friend ptt, maybe also support stranger -> { + data.msgBody.richText.ptt?.let { ptt -> + if (ptt.downPara.isEmpty()) { + val rsp = bot.network.sendAndExpect( + PttStore.C2CPttDown(bot.client, senderUin, ptt.fileUuid) + ) + if (rsp is PttStore.C2CPttDown.Response.Success) { + ptt.downPara = rsp.downloadUrl.encodeToByteArray() + } + } + } handlePrivateMessage( data, bot.getFriend(senderUin)?.impl() diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt index 87bfac2f9c5..e851a5a80bf 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt @@ -760,7 +760,7 @@ internal class ImMsgBody : ProtoBuf { @ProtoNumber(17) @JvmField val pttUrl: ByteArray = EMPTY_BYTE_ARRAY, @ProtoNumber(18) @JvmField val groupFileKey: ByteArray = EMPTY_BYTE_ARRAY, @ProtoNumber(19) @JvmField val time: Int = 0, - @ProtoNumber(20) @JvmField val downPara: ByteArray = EMPTY_BYTE_ARRAY, + @ProtoNumber(20) @JvmField var downPara: ByteArray = EMPTY_BYTE_ARRAY, @ProtoNumber(29) @JvmField val format: Int = 0, @ProtoNumber(30) @JvmField val pbReserve: ByteArray = EMPTY_BYTE_ARRAY, @ProtoNumber(31) @JvmField val bytesPttUrls: List = emptyList(), diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index dce8b1e5141..2bd2d04cf14 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -142,6 +142,7 @@ internal object KnownPacketFactories { ImgStore.GroupPicUp, PttStore.GroupPttUp, PttStore.GroupPttDown, + PttStore.C2CPttDown, LongConn.OffPicUp, // LongConn.OffPicDown, TroopManagement.EditSpecialTitle, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/voice/PttStore.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/voice/PttStore.kt index 1b7a4c61f41..99bc0dd8f92 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/voice/PttStore.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/voice/PttStore.kt @@ -195,6 +195,50 @@ internal class PttStore { } } + object C2CPttDown : OutgoingPacketFactory( + "PttCenterSvr.pb_pttCenter_CMD_REQ_APPLY_DOWNLOAD-1200" + ) { + operator fun invoke(client: QQAndroidClient, uin: Long, uuid: ByteArray) = + buildOutgoingUniPacket(client) { + writeProtoBuf( + Cmd0x346.ReqBody.serializer(), Cmd0x346.ReqBody( + msgApplyDownloadReq = Cmd0x346.ApplyDownloadReq( + uin = uin, + uuid = uuid, + needHttpsUrl = 1, + ), + clientType = 104, + cmd = 1200, + businessId = 17, // or 3? + ) + ) + } + + sealed class Response : Packet { + class Failed(val retMsg: String) : Response() { + override fun toString(): String { + return "PttCenterSvr.pb_pttCenter#download.Failed(retMsg=$retMsg)" + } + } + + class Success(val downloadUrl: String) : Response() { + override fun toString(): String { + return "PttCenterSvr.pb_pttCenter#download.Success" + } + } + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): C2CPttDown.Response { + val data = readProtoBuf(Cmd0x346.RspBody.serializer()) + val rsp = data.msgApplyDownloadRsp ?: return Response.Failed("Response not found") + if (rsp.retMsg != "success") { + return Response.Failed(rsp.retMsg) + } + val downloadInfo = rsp.msgDownloadInfo ?: return Response.Failed("Download info not found") + return Response.Success(downloadInfo.downloadUrl) + } + } + object C2C { fun createC2CPttStoreBDHExt( bot: QQAndroidBot,