Commit 522bcba3 authored by Him188's avatar Him188

Merge remote-tracking branch 'origin/dev' into dev

parents 1d0c3c2e 2ae91bb2
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
package net.mamoe.mirai.qqandroid package net.mamoe.mirai.qqandroid
import io.ktor.client.HttpClient
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.request.forms.MultiPartFormDataContent import io.ktor.client.request.forms.MultiPartFormDataContent
import io.ktor.client.request.forms.formData import io.ktor.client.request.forms.formData
...@@ -45,6 +44,7 @@ import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper ...@@ -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.ImMsgBody
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.LongMsg 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.*
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.FriendList
import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import net.mamoe.mirai.qqandroid.utils.encodeToString import net.mamoe.mirai.qqandroid.utils.encodeToString
...@@ -56,6 +56,7 @@ import kotlin.coroutines.CoroutineContext ...@@ -56,6 +56,7 @@ import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
import kotlin.math.log
import kotlin.random.Random import kotlin.random.Random
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
...@@ -560,7 +561,7 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -560,7 +561,7 @@ internal abstract class QQAndroidBotBase constructor(
@MiraiExperimentalAPI @MiraiExperimentalAPI
override suspend fun _lowLevelGetAnnouncement(groupId: Long, fid: String): GroupAnnouncement { override suspend fun _lowLevelGetAnnouncement(groupId: Long, fid: String): GroupAnnouncement {
val data = network.async { val data = network.async {
HttpClient().post<String> { MiraiPlatformUtils.Http.post<String> {
url("https://web.qun.qq.com/cgi-bin/announce/get_feed") url("https://web.qun.qq.com/cgi-bin/announce/get_feed")
body = MultiPartFormDataContent(formData { body = MultiPartFormDataContent(formData {
append("qid", groupId) append("qid", groupId)
...@@ -587,7 +588,7 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -587,7 +588,7 @@ internal abstract class QQAndroidBotBase constructor(
@MiraiExperimentalAPI @MiraiExperimentalAPI
override suspend fun _lowLevelGetGroupActiveData(groupId: Long, page: Int): GroupActiveData { override suspend fun _lowLevelGetGroupActiveData(groupId: Long, page: Int): GroupActiveData {
val data = network.async { val data = network.async {
HttpClient().get<String> { MiraiPlatformUtils.Http.get<String> {
url("https://qqweb.qq.com/c/activedata/get_mygroup_data") url("https://qqweb.qq.com/c/activedata/get_mygroup_data")
parameter("bkn", bkn) parameter("bkn", bkn)
parameter("gc", groupId) parameter("gc", groupId)
...@@ -792,6 +793,21 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -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") @Suppress("DEPRECATION", "OverridingDeprecatedMember")
override suspend fun queryImageUrl(image: Image): String = when (image) { override suspend fun queryImageUrl(image: Image): String = when (image) {
is ConstOriginUrlAware -> image.originUrl is ConstOriginUrlAware -> image.originUrl
......
...@@ -32,7 +32,9 @@ import kotlin.contracts.contract ...@@ -32,7 +32,9 @@ import kotlin.contracts.contract
private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。") private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
private val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。") private val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。")
private val UNSUPPORTED_FLASH_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") @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: Boolean): MutableList<ImMsgBody.Elem> { internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: Boolean): MutableList<ImMsgBody.Elem> {
val elements = ArrayList<ImMsgBody.Elem>(this.size) val elements = ArrayList<ImMsgBody.Elem>(this.size)
...@@ -156,7 +158,20 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B ...@@ -156,7 +158,20 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
is VipFace -> { is VipFace -> {
transformOneMessage(PlainText(it.contentToString())) 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 ForwardMessage,
is MessageSource, // mirai metadata only is MessageSource, // mirai metadata only
is RichMessage // already transformed above is RichMessage // already transformed above
...@@ -218,10 +233,11 @@ internal fun MsgComm.Msg.toMessageChain( ...@@ -218,10 +233,11 @@ internal fun MsgComm.Msg.toMessageChain(
val ptt = this.msgBody.richText.ptt val ptt = this.msgBody.richText.ptt
val pptMsg = ptt?.run { val pptMsg = ptt?.run {
when(fileType) { // when (fileType) {
4 -> Voice(String(fileName), fileMd5, String(downPara)) // 4 -> Voice(String(fileName), fileMd5, fileSize.toLong(),String(downPara))
else -> null // else -> null
} // }
Voice(String(fileName), fileMd5, fileSize.toLong(),String(downPara))
} }
return buildMessageChain(elements.size + 1 + if (pptMsg == null) 0 else 1) { return buildMessageChain(elements.size + 1 + if (pptMsg == null) 0 else 1) {
...@@ -274,13 +290,13 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain { ...@@ -274,13 +290,13 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain {
return@forEach return@forEach
} }
} }
if (last is FlashImage && element is PlainText) { // 解决tim发送的语音无法正常识别
if (element == UNSUPPORTED_FLASH_MESSAGE_PLAIN) { if (element is PlainText) {
if (element == UNSUPPORTED_VOICE_MESSAGE_PLAIN) {
last = element last = element
return@forEach return@forEach
} }
} }
add(element) add(element)
last = element last = element
} }
...@@ -431,7 +447,8 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B ...@@ -431,7 +447,8 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(groupIdOrZero: Long, bot: B
.orEmpty(), .orEmpty(),
proto.pokeType, proto.pokeType,
proto.vaspokeId proto.vaspokeId
)) )
)
} }
3 -> { 3 -> {
val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer()) val proto = element.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype3.serializer())
......
...@@ -12,7 +12,10 @@ ...@@ -12,7 +12,10 @@
package net.mamoe.mirai.qqandroid.network.highway package net.mamoe.mirai.qqandroid.network.highway
import io.ktor.client.HttpClient 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.post
import io.ktor.client.request.url
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.URLProtocol import io.ktor.http.URLProtocol
...@@ -29,8 +32,9 @@ import kotlinx.io.core.use ...@@ -29,8 +32,9 @@ import kotlinx.io.core.use
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.QQAndroidClient 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.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.PlatformSocket
import net.mamoe.mirai.qqandroid.utils.SocketException
import net.mamoe.mirai.qqandroid.utils.addSuppressedMirai import net.mamoe.mirai.qqandroid.utils.addSuppressedMirai
import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.withUse import net.mamoe.mirai.qqandroid.utils.io.withUse
...@@ -180,6 +184,50 @@ internal object HighwayHelper { ...@@ -180,6 +184,50 @@ internal object HighwayHelper {
} }
} }
} }
suspend fun uploadPttToServers(
bot: QQAndroidBot,
servers: List<Pair<Int, Int>>,
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<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("mType", "pttDu")
parameter("voice_encodec", 0)
body = content
}
}
} }
......
...@@ -273,7 +273,7 @@ internal class TryUpPttReq( ...@@ -273,7 +273,7 @@ internal class TryUpPttReq(
internal class TryUpPttRsp( internal class TryUpPttRsp(
@ProtoId(1) @JvmField val fileId: Long = 0L, @ProtoId(1) @JvmField val fileId: Long = 0L,
@ProtoId(2) @JvmField val result: Int = 0, @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(4) @JvmField val boolFileExit: Boolean = false,
@ProtoId(5) @JvmField val uint32UpIp: List<Int>? = null, @ProtoId(5) @JvmField val uint32UpIp: List<Int>? = null,
@ProtoId(6) @JvmField val uint32UpPort: List<Int>? = null, @ProtoId(6) @JvmField val uint32UpPort: List<Int>? = null,
......
...@@ -20,6 +20,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement ...@@ -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.ImgStore
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn 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.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.FriendList
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService import net.mamoe.mirai.qqandroid.network.protocol.packet.list.ProfileService
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.login.ConfigPushSvc
...@@ -139,6 +140,8 @@ internal object KnownPacketFactories { ...@@ -139,6 +140,8 @@ internal object KnownPacketFactories {
FriendList.GetTroopListSimplify, FriendList.GetTroopListSimplify,
FriendList.GetTroopMemberList, FriendList.GetTroopMemberList,
ImgStore.GroupPicUp, ImgStore.GroupPicUp,
PttStore.GroupPttUp,
PttStore.GroupPttDown,
LongConn.OffPicUp, LongConn.OffPicUp,
LongConn.OffPicDown, LongConn.OffPicDown,
TroopManagement.EditSpecialTitle, TroopManagement.EditSpecialTitle,
......
...@@ -30,6 +30,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY ...@@ -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.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory 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.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.readProtoBuf
import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
...@@ -138,7 +139,14 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg. ...@@ -138,7 +139,14 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
richText = ImMsgBody.RichText( richText = ImMsgBody.RichText(
elems = message.toRichTextElems(forGroup = true, withGeneralFlags = true), elems = message.toRichTextElems(forGroup = true, withGeneralFlags = true),
ptt = message.firstOrNull(PttMessage)?.run { ptt = message.firstOrNull(PttMessage)?.run {
ImMsgBody.Ptt(fileName = fileName.toByteArray(), fileMd5 = md5) ImMsgBody.Ptt(
fileName = fileName.toByteArray(),
fileMd5 = md5,
boolValid = true,
fileSize = fileSize.toInt(),
fileType = 4,
pbReserve = byteArrayOf(0)
)
} }
) )
), ),
......
...@@ -53,6 +53,7 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("Onlin ...@@ -53,6 +53,7 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory<Packet?>("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 // 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 if (!bot.firstLoginSucceed) return null
val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer())
// bot.logger.debug(pbPushMsg._miraiContentToString())
if (pbPushMsg.msg.msgHead.fromUin == bot.id) { if (pbPushMsg.msg.msgHead.fromUin == bot.id) {
return SendGroupMessageReceipt( return SendGroupMessageReceipt(
......
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<GroupPttUp.Response>("PttStore.GroupPttUp") {
sealed class Response : Packet {
class RequireUpload(
val fileId: Long,
val uKey: ByteArray,
val uploadIpList: List<Int>,
val uploadPortList: List<Int>,
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<GroupPttDown.Response>("PttStore.GroupPttDown") {
sealed class Response() : Packet {
class DownLoadInfo(
val downDomain: ByteArray,
val downPara:ByteArray,
val strDomain:String,
val uint32DownIp:List<Int>,
val uint32DownPort:List<Int>
) : 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
)
}
}
}
...@@ -16,6 +16,7 @@ import net.mamoe.mirai.data.* ...@@ -16,6 +16,7 @@ import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.event.events.NewFriendRequestEvent 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.MiraiExperimentalAPI
import net.mamoe.mirai.utils.WeakRef import net.mamoe.mirai.utils.WeakRef
...@@ -159,4 +160,13 @@ interface LowLevelBotAPIAccessor { ...@@ -159,4 +160,13 @@ interface LowLevelBotAPIAccessor {
blackList: Boolean, blackList: Boolean,
message: String = "" message: String = ""
) )
/**
* 查询语音的下载连接
*
* */
@LowLevelAPI
@MiraiExperimentalAPI
suspend fun _lowLevelQueryGroupVoiceDownloadUrl(md5: ByteArray, groupId: Long, dstUin: Long): String
} }
...@@ -15,6 +15,7 @@ abstract class PttMessage : MessageContent { ...@@ -15,6 +15,7 @@ abstract class PttMessage : MessageContent {
abstract val fileName: String abstract val fileName: String
abstract val md5: ByteArray abstract val md5: ByteArray
abstract val fileSize: Long
} }
...@@ -25,6 +26,7 @@ abstract class PttMessage : MessageContent { ...@@ -25,6 +26,7 @@ abstract class PttMessage : MessageContent {
class Voice( class Voice(
override val fileName: String, override val fileName: String,
override val md5: ByteArray, override val md5: ByteArray,
override val fileSize: Long,
private val _url: String private val _url: String
) : PttMessage() { ) : PttMessage() {
...@@ -33,9 +35,9 @@ class Voice( ...@@ -33,9 +35,9 @@ class Voice(
get() = "Voice" get() = "Voice"
} }
val url: String val url: String?
get() = if (_url.startsWith("http")) _url get() = if (_url.startsWith("http")) _url
else "http://grouptalk.c2c.qq.com$_url" else null
private var _stringValue: String? = null private var _stringValue: String? = null
get() = field ?: kotlin.run { get() = field ?: kotlin.run {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment