mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-24 20:43:33 +08:00
initial support for ShortVideo message
This commit is contained in:
parent
bd3f50f848
commit
5a949d1f2f
mirai-core-api/src/commonMain/kotlin/message/data
mirai-core/src
commonMain
kotlin
resources/META-INF/services
commonTest/kotlin/message
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.message.data.visitor.MessageVisitor
|
||||
import net.mamoe.mirai.utils.MiraiInternalApi
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
public interface ShortVideo : MessageContent, ConstrainSingle {
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<MessageContent, ShortVideo>(MessageContent, { it.safeCast() })
|
||||
|
||||
/**
|
||||
* 文件 ID.
|
||||
*/
|
||||
public val fileId: String
|
||||
|
||||
/**
|
||||
* 文件 MD5. 16 bytes.
|
||||
*/
|
||||
public val fileMd5: ByteArray
|
||||
|
||||
|
||||
@MiraiInternalApi
|
||||
override fun <D, R> accept(visitor: MessageVisitor<D, R>, data: D): R {
|
||||
return visitor.visitShortVideo(this, data)
|
||||
}
|
||||
|
||||
override val key: MessageKey<*>
|
||||
get() = Key
|
||||
}
|
||||
|
||||
@NotStableForInheritance
|
||||
public interface OnlineShortVideo : ShortVideo {
|
||||
public val urlForDownload: String
|
||||
|
||||
public companion object Key :
|
||||
AbstractPolymorphicMessageKey<ShortVideo, OnlineShortVideo>(ShortVideo, { it.safeCast() }) {
|
||||
public const val SERIAL_NAME: String = "OnlineShortAudio"
|
||||
}
|
||||
}
|
@ -41,6 +41,8 @@ public interface MessageVisitor<in D, out R> {
|
||||
public fun visitVoice(message: net.mamoe.mirai.message.data.Voice, data: D): R
|
||||
public fun visitAudio(message: Audio, data: D): R
|
||||
|
||||
public fun visitShortVideo(message: ShortVideo, data: D): R
|
||||
|
||||
// region HummerMessage
|
||||
public fun visitHummerMessage(message: HummerMessage, data: D): R
|
||||
public fun visitFlashImage(message: FlashImage, data: D): R
|
||||
@ -164,6 +166,10 @@ public abstract class AbstractMessageVisitor<in D, out R> : MessageVisitor<D, R>
|
||||
return visitMessageContent(message, data)
|
||||
}
|
||||
|
||||
override fun visitShortVideo(message: ShortVideo, data: D): R {
|
||||
return visitMessageContent(message, data)
|
||||
}
|
||||
|
||||
public override fun visitHummerMessage(message: HummerMessage, data: D): R {
|
||||
return visitMessageContent(message, data)
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ package net.mamoe.mirai.internal.contact.roaming
|
||||
import kotlinx.coroutines.flow.*
|
||||
import net.mamoe.mirai.contact.roaming.RoamingMessageFilter
|
||||
import net.mamoe.mirai.internal.contact.CommonGroupImpl
|
||||
import net.mamoe.mirai.internal.message.SimpleRefineContext
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
|
||||
@ -65,7 +67,20 @@ internal class RoamingMessagesImplGroup(
|
||||
.sortedByDescending { it.msgHead.msgSeq } // Ensure caller receives newer messages first
|
||||
.filter { filter.apply(it) } // Call filter after sort
|
||||
.asFlow()
|
||||
.map { listOf(it).toMessageChainOnline(bot, contact.id, MessageSourceKind.GROUP) }
|
||||
.map {
|
||||
listOf(it).toMessageChainOnline(
|
||||
bot,
|
||||
contact.id,
|
||||
MessageSourceKind.GROUP,
|
||||
SimpleRefineContext(
|
||||
mutableMapOf(
|
||||
OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP,
|
||||
OnlineShortVideoMsgInternal.FromId to it.msgHead.fromUin,
|
||||
OnlineShortVideoMsgInternal.GroupIdOrZero to contact.uin,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
currentSeq = resp.msgElem.first().msgHead.msgSeq
|
||||
|
@ -16,9 +16,12 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.isActive
|
||||
import net.mamoe.mirai.contact.roaming.RoamingMessageFilter
|
||||
import net.mamoe.mirai.internal.message.SimpleRefineContext
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetRoamMsgReq
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.utils.cast
|
||||
|
||||
internal sealed class TimeBasedRoamingMessagesImpl : AbstractRoamingMessages() {
|
||||
override suspend fun getMessagesIn(
|
||||
@ -32,13 +35,34 @@ internal sealed class TimeBasedRoamingMessagesImpl : AbstractRoamingMessages() {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val resp = requestRoamMsg(timeStart, lastMessageTime, random)
|
||||
val messages = resp.messages ?: break
|
||||
|
||||
if (filter == null || filter === RoamingMessageFilter.ANY) {
|
||||
// fast path
|
||||
messages.forEach { emit(it.toMessageChainOnline(contact.bot)) }
|
||||
messages.forEach {
|
||||
emit(
|
||||
it.toMessageChainOnline(
|
||||
contact.bot,
|
||||
refineContext = SimpleRefineContext(
|
||||
mutableListOf(
|
||||
OnlineShortVideoMsgInternal.FromId to it.msgHead.fromUin
|
||||
).cast()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for (message in messages) {
|
||||
if (filter.invoke(createRoamingMessage(message, messages))) {
|
||||
emit(message.toMessageChainOnline(contact.bot))
|
||||
emit(
|
||||
message.toMessageChainOnline(
|
||||
contact.bot,
|
||||
refineContext = SimpleRefineContext(
|
||||
mutableListOf(
|
||||
OnlineShortVideoMsgInternal.FromId to message.msgHead.fromUin
|
||||
).cast()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.cleanupRubbish
|
||||
import net.mamoe.mirai.internal.message.ReceiveMessageTransformer.toAudio
|
||||
import net.mamoe.mirai.internal.message.data.LongMessageInternal
|
||||
import net.mamoe.mirai.internal.message.data.OnlineAudioImpl
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade
|
||||
import net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol.Companion.UNSUPPORTED_POKE_MESSAGE_PLAIN
|
||||
import net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol.Companion.UNSUPPORTED_MERGED_MESSAGE_PLAIN
|
||||
@ -24,6 +25,7 @@ import net.mamoe.mirai.internal.message.source.*
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.castOrNull
|
||||
import net.mamoe.mirai.utils.structureToString
|
||||
import net.mamoe.mirai.utils.toLongUnsigned
|
||||
|
||||
@ -34,12 +36,13 @@ internal fun ImMsgBody.SourceMsg.toMessageChainNoSource(
|
||||
bot: Bot,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
groupIdOrZero: Long,
|
||||
fromId: Long,
|
||||
refineContext: RefineContext = EmptyRefineContext,
|
||||
facade: MessageProtocolFacade = MessageProtocolFacade
|
||||
): MessageChain {
|
||||
val elements = this.elems
|
||||
return buildMessageChain(elements.size + 1) {
|
||||
facade.decode(elements, groupIdOrZero, messageSourceKind, bot, this, null)
|
||||
facade.decode(elements, groupIdOrZero, messageSourceKind, fromId, bot, this, null)
|
||||
}.cleanupRubbishMessageElements().refineLight(bot, refineContext)
|
||||
}
|
||||
|
||||
@ -78,7 +81,21 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline(
|
||||
MessageSourceKind.GROUP -> msgHead.groupInfo?.groupCode ?: 0
|
||||
else -> 0
|
||||
}
|
||||
return listOf(this).toMessageChainOnline(bot, groupId, kind, refineContext, facade)
|
||||
|
||||
val mutableRefineContextApplier: MutableRefineContext.() -> Unit = {
|
||||
set(OnlineShortVideoMsgInternal.MessageSourceKind, kind)
|
||||
set(OnlineShortVideoMsgInternal.GroupIdOrZero, groupId)
|
||||
}
|
||||
|
||||
return listOf(this).toMessageChainOnline(
|
||||
bot,
|
||||
groupId,
|
||||
kind,
|
||||
// TODO: it is better to add `RefineContext.merge(other, override)` to merge with another refine context
|
||||
(refineContext.castOrNull<MutableRefineContext>() ?: SimpleRefineContext(mutableMapOf()))
|
||||
.apply(mutableRefineContextApplier),
|
||||
facade
|
||||
)
|
||||
}
|
||||
|
||||
//internal fun List<MsgComm.Msg>.toMessageChainOffline(
|
||||
@ -129,13 +146,21 @@ private fun List<MsgComm.Msg>.toMessageChainImpl(
|
||||
|
||||
val builder = MessageChainBuilder(messageList.sumOf { it.msgBody.richText.elems.size })
|
||||
|
||||
if (onlineSource != null) {
|
||||
builder.add(ReceiveMessageTransformer.createMessageSource(bot, onlineSource, messageSourceKind, messageList))
|
||||
}
|
||||
|
||||
val source = if (onlineSource != null) {
|
||||
ReceiveMessageTransformer.createMessageSource(bot, onlineSource, messageSourceKind, messageList)
|
||||
} else null
|
||||
if (source != null) builder.add(source)
|
||||
|
||||
messageList.forEach { msg ->
|
||||
facade.decode(msg.msgBody.richText.elems, groupIdOrZero, messageSourceKind, bot, builder, msg)
|
||||
facade.decode(
|
||||
msg.msgBody.richText.elems,
|
||||
groupIdOrZero,
|
||||
messageSourceKind,
|
||||
source?.fromId ?: first().msgHead.fromUin,
|
||||
bot,
|
||||
builder,
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
for (msg in messageList) {
|
||||
|
142
mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt
Normal file
142
mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.message.data
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||
import net.mamoe.mirai.internal.message.RefinableMessage
|
||||
import net.mamoe.mirai.internal.message.RefineContext
|
||||
import net.mamoe.mirai.internal.message.RefineContextKey
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.video.PttCenterSvr
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.message.data.OnlineShortVideo
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.isSameType
|
||||
import net.mamoe.mirai.utils.toUHexString
|
||||
|
||||
/**
|
||||
* refine to [OnlineShortVideoImpl]
|
||||
*/
|
||||
internal class OnlineShortVideoMsgInternal(
|
||||
private val videoFile: ImMsgBody.VideoFile
|
||||
) : RefinableMessage {
|
||||
|
||||
override fun tryRefine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message? {
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun refine(bot: Bot, context: MessageChain, refineContext: RefineContext): Message? {
|
||||
bot.asQQAndroidBot()
|
||||
|
||||
val sourceKind = refineContext[MessageSourceKind] ?: return null
|
||||
val fromId = refineContext[FromId] ?: return null
|
||||
val groupId = refineContext[GroupIdOrZero] ?: return null
|
||||
|
||||
val contact = when (sourceKind) {
|
||||
net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId)
|
||||
net.mamoe.mirai.message.data.MessageSourceKind.GROUP -> bot.getGroup(groupId)
|
||||
else -> return null // don't process stranger's video message
|
||||
}.cast<Contact>()
|
||||
val sender = when (sourceKind) {
|
||||
net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId)
|
||||
net.mamoe.mirai.message.data.MessageSourceKind.GROUP -> {
|
||||
val group = bot.getGroup(groupId)
|
||||
checkNotNull(group).members[fromId]
|
||||
}
|
||||
|
||||
else -> return null // don't process stranger's video message
|
||||
}.cast<User>()
|
||||
|
||||
val shortVideoDownloadReq = bot.network.sendAndExpect(
|
||||
PttCenterSvr.ShortVideoDownReq(
|
||||
bot.client,
|
||||
contact,
|
||||
sender,
|
||||
videoFile.fileUuid.decodeToString(),
|
||||
videoFile.fileMd5
|
||||
)
|
||||
)
|
||||
|
||||
if (shortVideoDownloadReq !is PttCenterSvr.ShortVideoDownReq.Response.Success)
|
||||
throw IllegalStateException("failed to query short video download attributes.")
|
||||
|
||||
if (!shortVideoDownloadReq.fileMd5.contentEquals(videoFile.fileMd5))
|
||||
throw IllegalStateException(
|
||||
"queried short video download attributes doesn't match the requests. " +
|
||||
"message provides: ${videoFile.fileMd5.toUHexString("")}, " +
|
||||
"queried result: ${shortVideoDownloadReq.fileMd5.toUHexString("")}"
|
||||
)
|
||||
|
||||
return OnlineShortVideoImpl(
|
||||
videoFile.fileUuid.decodeToString(),
|
||||
shortVideoDownloadReq.fileMd5,
|
||||
shortVideoDownloadReq.urlV4
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun contentToString(): String {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
companion object {
|
||||
val MessageSourceKind = RefineContextKey<MessageSourceKind>("MessageSourceKind")
|
||||
val FromId = RefineContextKey<Long>("FromId")
|
||||
val GroupIdOrZero = RefineContextKey<Long>("GroupIdOrZero")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
@SerialName(OnlineShortVideo.SERIAL_NAME)
|
||||
@Serializable
|
||||
internal class OnlineShortVideoImpl(
|
||||
override val fileId: String,
|
||||
override val fileMd5: ByteArray,
|
||||
override val urlForDownload: String
|
||||
) : OnlineShortVideo {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (!isSameType(this, other)) return false
|
||||
|
||||
if (fileId != other.fileId) return false
|
||||
if (urlForDownload != other.urlForDownload) return false
|
||||
if (!fileMd5.contentEquals(other.fileMd5)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "[mirai:svideo:$fileId, md5=${fileMd5.toUHexString("")}]"
|
||||
}
|
||||
|
||||
override fun contentToString(): String {
|
||||
return "[视频]"
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = fileId.hashCode()
|
||||
result = 31 * result + fileMd5.contentHashCode()
|
||||
result = 31 * result + urlForDownload.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
@ -77,6 +77,7 @@ internal interface MessageProtocolFacade {
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
fromId: Long,
|
||||
bot: Bot,
|
||||
builder: MessageChainBuilder,
|
||||
containingMsg: MsgComm.Msg? = null,
|
||||
@ -134,8 +135,11 @@ internal interface MessageProtocolFacade {
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
fromId: Long,
|
||||
bot: Bot,
|
||||
): MessageChain = buildMessageChain { decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) }
|
||||
): MessageChain = buildMessageChain {
|
||||
decode(elements, groupIdOrZero, messageSourceKind, fromId, bot, this, null)
|
||||
}
|
||||
|
||||
|
||||
fun createSerializersModule(): SerializersModule = SerializersModule {
|
||||
@ -171,17 +175,19 @@ internal fun MessageProtocolFacade.decodeAndRefineLight(
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
fromId: Long,
|
||||
bot: Bot,
|
||||
refineContext: RefineContext = EmptyRefineContext
|
||||
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineLight(bot, refineContext)
|
||||
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, fromId, bot).refineLight(bot, refineContext)
|
||||
|
||||
internal suspend fun MessageProtocolFacade.decodeAndRefineDeep(
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
fromId: Long,
|
||||
bot: Bot,
|
||||
refineContext: RefineContext = EmptyRefineContext
|
||||
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineDeep(bot, refineContext)
|
||||
): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, fromId, bot).refineDeep(bot, refineContext)
|
||||
|
||||
|
||||
private const val errorTips =
|
||||
@ -288,6 +294,7 @@ internal class MessageProtocolFacadeImpl(
|
||||
elements: List<ImMsgBody.Elem>,
|
||||
groupIdOrZero: Long,
|
||||
messageSourceKind: MessageSourceKind,
|
||||
fromId: Long,
|
||||
bot: Bot,
|
||||
builder: MessageChainBuilder,
|
||||
containingMsg: MsgComm.Msg?
|
||||
@ -336,6 +343,7 @@ internal class MessageProtocolFacadeImpl(
|
||||
|
||||
return getSingleReceipt(result, message)
|
||||
}
|
||||
|
||||
override suspend fun <C : AbstractContact> preprocessAndSendOutgoing(
|
||||
target: C,
|
||||
message: Message,
|
||||
@ -378,6 +386,7 @@ internal class MessageProtocolFacadeImpl(
|
||||
"Internal error: no MessageReceipt was returned from OutgoingMessagePipeline for message",
|
||||
forDebug = message.structureToString()
|
||||
)
|
||||
|
||||
1 -> return result.single().castUp()
|
||||
else -> throw contextualBugReportException(
|
||||
"Internal error: multiple MessageReceipts were returned from OutgoingMessagePipeline: $result",
|
||||
|
@ -29,6 +29,7 @@ internal interface MessageDecoderContext : ProcessorPipelineContext<ImMsgBody.El
|
||||
val MESSAGE_SOURCE_KIND = TypeKey<MessageSourceKind>("messageSourceKind")
|
||||
val GROUP_ID = TypeKey<Long>("groupId") // zero if not group
|
||||
val CONTAINING_MSG = TypeKey<MsgComm.Msg?>("containingMsg")
|
||||
val FROM_ID = TypeKey<Long>("fromId") // group/temp = sender, friend/stranger = this
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.message.protocol.impl
|
||||
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoder
|
||||
import net.mamoe.mirai.internal.message.protocol.decode.MessageDecoderContext
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
|
||||
internal class ShortVideoProtocol : MessageProtocol() {
|
||||
override fun ProcessorCollector.collectProcessorsImpl() {
|
||||
add(Decoder())
|
||||
}
|
||||
|
||||
private class Decoder : MessageDecoder {
|
||||
override suspend fun MessageDecoderContext.process(data: ImMsgBody.Elem) {
|
||||
val videoFile = data.videoFile ?: return
|
||||
markAsConsumed()
|
||||
|
||||
collect(OnlineShortVideoMsgInternal(videoFile))
|
||||
}
|
||||
}
|
||||
}
|
@ -183,7 +183,9 @@ internal fun OfflineMessageSourceImplData(
|
||||
internalIds = delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer())
|
||||
.origUids?.mapToIntArray { it.toInt() } ?: intArrayOf(),
|
||||
time = delegate.time,
|
||||
originalMessageLazy = lazy { delegate.toMessageChainNoSource(bot, messageSourceKind, groupIdOrZero) },
|
||||
originalMessageLazy = lazy {
|
||||
delegate.toMessageChainNoSource(bot, messageSourceKind, groupIdOrZero, delegate.senderUin)
|
||||
},
|
||||
fromId = delegate.senderUin,
|
||||
targetId = when {
|
||||
groupIdOrZero != 0L -> groupIdOrZero
|
||||
@ -191,6 +193,7 @@ internal fun OfflineMessageSourceImplData(
|
||||
delegate.srcMsg != null -> runCatching {
|
||||
delegate.srcMsg.loadAs(MsgComm.Msg.serializer()).msgHead.toUin
|
||||
}.getOrElse { 0L }
|
||||
|
||||
else -> 0/*error("cannot find targetId. delegate=${delegate._miraiContentToString()}, delegate.srcMsg=${
|
||||
kotlin.runCatching { delegate.srcMsg?.loadAs(MsgComm.Msg.serializer())?._miraiContentToString() }
|
||||
.fold(
|
||||
|
@ -20,6 +20,8 @@ import net.mamoe.mirai.event.events.MemberCardChangeEvent
|
||||
import net.mamoe.mirai.event.events.MemberSpecialTitleChangeEvent
|
||||
import net.mamoe.mirai.internal.contact.*
|
||||
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
||||
import net.mamoe.mirai.internal.message.SimpleRefineContext
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
@ -159,7 +161,18 @@ internal class GroupMessageProcessor(
|
||||
GroupMessageSyncEvent(
|
||||
client = bot.otherClients.find { it.appId == msgHead.fromInstid }
|
||||
?: return, // don't compare with dstAppId. diff.
|
||||
message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP),
|
||||
message = msgs.map { it.msg }.toMessageChainOnline(
|
||||
bot,
|
||||
group.id,
|
||||
MessageSourceKind.GROUP,
|
||||
SimpleRefineContext(
|
||||
mutableMapOf(
|
||||
OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP,
|
||||
OnlineShortVideoMsgInternal.FromId to sender.uin,
|
||||
OnlineShortVideoMsgInternal.GroupIdOrZero to group.uin,
|
||||
)
|
||||
)
|
||||
),
|
||||
time = msgHead.msgTime,
|
||||
group = group,
|
||||
sender = sender,
|
||||
@ -174,7 +187,18 @@ internal class GroupMessageProcessor(
|
||||
GroupMessageEvent(
|
||||
senderName = nameCard.nick,
|
||||
sender = sender,
|
||||
message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP),
|
||||
message = msgs.map { it.msg }.toMessageChainOnline(
|
||||
bot,
|
||||
group.id,
|
||||
MessageSourceKind.GROUP,
|
||||
SimpleRefineContext(
|
||||
mutableMapOf(
|
||||
OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP,
|
||||
OnlineShortVideoMsgInternal.FromId to sender.uin,
|
||||
OnlineShortVideoMsgInternal.GroupIdOrZero to group.uin,
|
||||
)
|
||||
)
|
||||
),
|
||||
permission = sender.permission,
|
||||
time = msgHead.msgTime,
|
||||
),
|
||||
|
@ -15,6 +15,8 @@ import net.mamoe.mirai.event.Event
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.internal.contact.*
|
||||
import net.mamoe.mirai.internal.getGroupByUinOrCode
|
||||
import net.mamoe.mirai.internal.message.SimpleRefineContext
|
||||
import net.mamoe.mirai.internal.message.data.OnlineShortVideoMsgInternal
|
||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.components.NoticePipelineContext
|
||||
@ -25,6 +27,7 @@ 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.message.data.MessageSourceKind
|
||||
import net.mamoe.mirai.utils.assertUnreachable
|
||||
import net.mamoe.mirai.utils.context
|
||||
|
||||
@ -114,6 +117,7 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type
|
||||
val group = bot.getGroupByUinOrCode(tmpHead.groupUin) ?: return
|
||||
handlePrivateMessage(data, group[senderUin] ?: return)
|
||||
}
|
||||
|
||||
else -> markNotConsumed()
|
||||
}
|
||||
|
||||
@ -129,7 +133,18 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type
|
||||
val msgs = user.fragmentedMessageMerger.tryMerge(this)
|
||||
if (msgs.isEmpty()) return
|
||||
|
||||
val chain = msgs.toMessageChainOnline(bot, 0, user.correspondingMessageSourceKind)
|
||||
val chain = msgs.toMessageChainOnline(
|
||||
bot,
|
||||
0,
|
||||
user.correspondingMessageSourceKind,
|
||||
SimpleRefineContext(
|
||||
mutableMapOf(
|
||||
OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.FRIEND,
|
||||
OnlineShortVideoMsgInternal.FromId to user.uin,
|
||||
OnlineShortVideoMsgInternal.GroupIdOrZero to 0L,
|
||||
)
|
||||
)
|
||||
)
|
||||
val time = msgHead.msgTime
|
||||
|
||||
collected += if (fromSync) {
|
||||
|
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import net.mamoe.mirai.internal.utils.io.ProtoBuf
|
||||
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideo : ProtoBuf {
|
||||
@Serializable
|
||||
internal class ServerListInfo(
|
||||
@JvmField @ProtoNumber(1) val upIp: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val upPort: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class CodecConfigReq(
|
||||
@JvmField @ProtoNumber(1) val platformChipinfo: String = "",
|
||||
@JvmField @ProtoNumber(2) val osVersion: String = "",
|
||||
@JvmField @ProtoNumber(3) val deviceName: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class DataHole(
|
||||
@JvmField @ProtoNumber(1) val begin: Long = 0L,
|
||||
@JvmField @ProtoNumber(2) val end: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ExtensionReq(
|
||||
@JvmField @ProtoNumber(1) val subBusiType: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val userCnt: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoAddr(
|
||||
@JvmField @ProtoNumber(1) val hostType: Int = 0,
|
||||
@JvmField @ProtoNumber(10) val strHost: List<String> = emptyList(),
|
||||
@JvmField @ProtoNumber(11) val urlArgs: String = "",
|
||||
@JvmField @ProtoNumber(21) val strHostIpv6: List<String> = emptyList(),
|
||||
@JvmField @ProtoNumber(22) val strDomain: List<String> = emptyList()
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoDeleteReq(
|
||||
@JvmField @ProtoNumber(1) val fromuin: Long = 0L,
|
||||
@JvmField @ProtoNumber(2) val touin: Long = 0L,
|
||||
@JvmField @ProtoNumber(3) val chatType: Int = 0,
|
||||
@JvmField @ProtoNumber(4) val clientType: Int = 0,
|
||||
@JvmField @ProtoNumber(5) val fileid: String = "",
|
||||
@JvmField @ProtoNumber(6) val groupCode: Long = 0L,
|
||||
@JvmField @ProtoNumber(7) val agentType: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(9) val businessType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoDeleteResp(
|
||||
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val retMsg: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoDownloadReq(
|
||||
@JvmField @ProtoNumber(1) val fromuin: Long = 0L,
|
||||
@JvmField @ProtoNumber(2) val touin: Long = 0L,
|
||||
@JvmField @ProtoNumber(3) val chatType: Int = 0,
|
||||
@JvmField @ProtoNumber(4) val clientType: Int = 0,
|
||||
@JvmField @ProtoNumber(5) val fileid: String = "",
|
||||
@JvmField @ProtoNumber(6) val groupCode: Long = 0L,
|
||||
@JvmField @ProtoNumber(7) val agentType: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(9) val businessType: Int = 0,
|
||||
@JvmField @ProtoNumber(10) val fileType: Int = 0,
|
||||
@JvmField @ProtoNumber(11) val downType: Int = 0,
|
||||
@JvmField @ProtoNumber(12) val sceneType: Int = 0,
|
||||
@JvmField @ProtoNumber(13) val needInnerAddr: Int = 0,
|
||||
@JvmField @ProtoNumber(14) val reqTransferType: Int = 0,
|
||||
@JvmField @ProtoNumber(15) val reqHostType: Int = 0,
|
||||
@JvmField @ProtoNumber(20) val flagSupportLargeSize: Int = 0,
|
||||
@JvmField @ProtoNumber(30) val flagClientQuicProtoEnable: Int = 0,
|
||||
@JvmField @ProtoNumber(31) val targetCodecFormat: Int = 0,
|
||||
@JvmField @ProtoNumber(32) val msgCodecConfig: CodecConfigReq? = null,
|
||||
@JvmField @ProtoNumber(33) val sourceCodecFormat: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoDownloadResp(
|
||||
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val retMsg: String = "",
|
||||
@JvmField @ProtoNumber(3) val sameAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(4) val diffAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(5) val downloadkey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(6) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(7) val sameAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(8) val diffAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(9) val msgDownloadAddr: PttShortVideoAddr? = null,
|
||||
@JvmField @ProtoNumber(10) val encryptKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(30) val flagServerQuicProtoEnable: Int = 0,
|
||||
@JvmField @ProtoNumber(31) val serverQuicPara: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(32) val codecFormat: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoFileInfo(
|
||||
@JvmField @ProtoNumber(1) val fileName: String = "",
|
||||
@JvmField @ProtoNumber(2) val fileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(3) val thumbFileMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(4) val fileSize: Long = 0L,
|
||||
@JvmField @ProtoNumber(5) val fileResLength: Int = 0,
|
||||
@JvmField @ProtoNumber(6) val fileResWidth: Int = 0,
|
||||
@JvmField @ProtoNumber(7) val fileFormat: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val fileTime: Int = 0,
|
||||
@JvmField @ProtoNumber(9) val thumbFileSize: Long = 0L,
|
||||
@JvmField @ProtoNumber(10) val decryptVideoMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(11) val decryptFileSize: Long = 0L,
|
||||
@JvmField @ProtoNumber(12) val decryptThumbMd5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(13) val decryptThumbSize: Long = 0L,
|
||||
@JvmField @ProtoNumber(14) val extend: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoFileInfoExtend(
|
||||
@JvmField @ProtoNumber(1) val bitRate: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoIpList(
|
||||
@JvmField @ProtoNumber(1) val ip: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val port: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoRetweetReq(
|
||||
@JvmField @ProtoNumber(1) val fromUin: Long = 0L,
|
||||
@JvmField @ProtoNumber(2) val toUin: Long = 0L,
|
||||
@JvmField @ProtoNumber(3) val fromChatType: Int = 0,
|
||||
@JvmField @ProtoNumber(4) val toChatType: Int = 0,
|
||||
@JvmField @ProtoNumber(5) val fromBusiType: Int = 0,
|
||||
@JvmField @ProtoNumber(6) val toBusiType: Int = 0,
|
||||
@JvmField @ProtoNumber(7) val clientType: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val msgPttShortVideoFileInfo: PttShortVideoFileInfo? = null,
|
||||
@JvmField @ProtoNumber(9) val agentType: Int = 0,
|
||||
@JvmField @ProtoNumber(10) val fileid: String = "",
|
||||
@JvmField @ProtoNumber(11) val groupCode: Long = 0L,
|
||||
@JvmField @ProtoNumber(20) val flagSupportLargeSize: Int = 0,
|
||||
@JvmField @ProtoNumber(21) val codecFormat: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoRetweetResp(
|
||||
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val retMsg: String = "",
|
||||
@JvmField @ProtoNumber(3) val sameAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(4) val diffAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(5) val fileid: String = "",
|
||||
@JvmField @ProtoNumber(6) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(7) val fileExist: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val sameAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(9) val diffAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(10) val dataHole: List<DataHole> = emptyList(),
|
||||
@JvmField @ProtoNumber(11) val isHotFile: Int = 0,
|
||||
@JvmField @ProtoNumber(12) val longVideoCarryWatchPointType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoUploadReq(
|
||||
@JvmField @ProtoNumber(1) val fromuin: Long = 0L,
|
||||
@JvmField @ProtoNumber(2) val touin: Long = 0L,
|
||||
@JvmField @ProtoNumber(3) val chatType: Int = 0,
|
||||
@JvmField @ProtoNumber(4) val clientType: Int = 0,
|
||||
@JvmField @ProtoNumber(5) val msgPttShortVideoFileInfo: PttShortVideoFileInfo? = null,
|
||||
@JvmField @ProtoNumber(6) val groupCode: Long = 0L,
|
||||
@JvmField @ProtoNumber(7) val agentType: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val businessType: Int = 0,
|
||||
@JvmField @ProtoNumber(9) val encryptKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(10) val subBusinessType: Int = 0,
|
||||
@JvmField @ProtoNumber(20) val flagSupportLargeSize: Int = 0,
|
||||
@JvmField @ProtoNumber(21) val codecFormat: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class PttShortVideoUploadResp(
|
||||
@JvmField @ProtoNumber(1) val int32RetCode: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val retMsg: String = "",
|
||||
@JvmField @ProtoNumber(3) val sameAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(4) val diffAreaOutAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(5) val fileid: String = "",
|
||||
@JvmField @ProtoNumber(6) val ukey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(7) val fileExist: Int = 0,
|
||||
@JvmField @ProtoNumber(8) val sameAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(9) val diffAreaInnerAddr: List<PttShortVideoIpList> = emptyList(),
|
||||
@JvmField @ProtoNumber(10) val dataHole: List<DataHole> = emptyList(),
|
||||
@JvmField @ProtoNumber(11) val encryptKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@JvmField @ProtoNumber(12) val isHotFile: Int = 0,
|
||||
@JvmField @ProtoNumber(13) val longVideoCarryWatchPointType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class QuicParameter(
|
||||
@JvmField @ProtoNumber(1) val enableQuic: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val encryptionVer: Int = 1,
|
||||
@JvmField @ProtoNumber(3) val fecVer: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class ReqBody(
|
||||
@JvmField @ProtoNumber(1) val cmd: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val seq: Int = 0,
|
||||
@JvmField @ProtoNumber(3) val msgPttShortVideoUploadReq: PttShortVideoUploadReq? = null,
|
||||
@JvmField @ProtoNumber(4) val msgPttShortVideoDownloadReq: PttShortVideoDownloadReq? = null,
|
||||
@JvmField @ProtoNumber(5) val msgShortVideoRetweetReq: List<PttShortVideoRetweetReq> = emptyList(),
|
||||
@JvmField @ProtoNumber(6) val msgShortVideoDeleteReq: List<PttShortVideoDeleteReq> = emptyList(),
|
||||
@JvmField @ProtoNumber(100) val msgExtensionReq: List<ExtensionReq> = emptyList()
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
internal class RspBody(
|
||||
@JvmField @ProtoNumber(1) val cmd: Int = 0,
|
||||
@JvmField @ProtoNumber(2) val seq: Int = 0,
|
||||
@JvmField @ProtoNumber(3) val msgPttShortVideoUploadResp: PttShortVideoUploadResp? = null,
|
||||
@JvmField @ProtoNumber(4) val msgPttShortVideoDownloadResp: PttShortVideoDownloadResp? = null,
|
||||
@JvmField @ProtoNumber(5) val msgShortVideoRetweetResp: List<PttShortVideoRetweetResp> = emptyList(),
|
||||
@JvmField @ProtoNumber(6) val msgShortVideoDeleteResp: List<PttShortVideoDeleteResp> = emptyList(),
|
||||
@JvmField @ProtoNumber(100) val changeChannel: Int = 0,
|
||||
@JvmField @ProtoNumber(101) val allowRetry: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
@ -18,6 +18,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.*
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.video.PttCenterSvr
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
|
||||
@ -151,6 +152,7 @@ internal object KnownPacketFactories {
|
||||
PttStore.GroupPttUp,
|
||||
PttStore.GroupPttDown,
|
||||
PttStore.C2CPttDown,
|
||||
PttCenterSvr.ShortVideoDownReq,
|
||||
LongConn.OffPicUp,
|
||||
// LongConn.OffPicDown,
|
||||
TroopManagement.EditSpecialTitle,
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2019-2023 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/dev/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.internal.network.protocol.packet.chat.video
|
||||
|
||||
import io.ktor.utils.io.core.*
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.internal.QQAndroidBot
|
||||
import net.mamoe.mirai.internal.contact.uin
|
||||
import net.mamoe.mirai.internal.network.Packet
|
||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.PttShortVideo
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
|
||||
|
||||
internal class PttCenterSvr {
|
||||
object ShortVideoDownReq : OutgoingPacketFactory<ShortVideoDownReq.Response>("PttCenterSvr.ShortVideoDownReq") {
|
||||
sealed class Response : Packet {
|
||||
class Success(val fileMd5: ByteArray, val urlV4: String, val urlV6: String?) : Response() {
|
||||
override fun toString(): String {
|
||||
return "PttCenterSvr.ShortVideoDownReq.Response.Success(" +
|
||||
"urlV4=$urlV4, urlV6=$urlV6)"
|
||||
}
|
||||
}
|
||||
|
||||
object Failed : Response() {
|
||||
override fun toString(): String {
|
||||
return "PttCenterSvr.ShortVideoDownReq.Response.Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
|
||||
val resp = readProtoBuf(PttShortVideo.RspBody.serializer())
|
||||
|
||||
val shortVideoDownloadResp = resp.msgPttShortVideoDownloadResp ?: return Response.Failed
|
||||
val attr = shortVideoDownloadResp.msgDownloadAddr ?: return Response.Failed
|
||||
|
||||
val fileMd5 = shortVideoDownloadResp.fileMd5
|
||||
val urlV4 = attr.strHost.first() + attr.urlArgs
|
||||
val urlV6 = attr.strHostIpv6.firstOrNull()?.plus(attr.urlArgs)
|
||||
|
||||
return Response.Success(fileMd5, urlV4, urlV6)
|
||||
}
|
||||
|
||||
// Lcom/tencent/mobileqq/transfile/protohandler/ShortVideoDownHandler;constructReqBody(Ljava/util/List;)[B
|
||||
operator fun invoke(
|
||||
client: QQAndroidClient,
|
||||
contact: Contact,
|
||||
sender: User,
|
||||
videoFIleId: String,
|
||||
videoFileMd5: ByteArray,
|
||||
) = buildOutgoingUniPacket(client) { sequenceId ->
|
||||
writeProtoBuf(
|
||||
PttShortVideo.ReqBody.serializer(),
|
||||
PttShortVideo.ReqBody(
|
||||
cmd = 400,
|
||||
seq = sequenceId,
|
||||
msgPttShortVideoDownloadReq = PttShortVideo.PttShortVideoDownloadReq(
|
||||
fromuin = sender.uin,
|
||||
touin = client.uin,
|
||||
chatType = if (sender is Friend) 0 else 1,
|
||||
clientType = 7,
|
||||
fileid = videoFIleId,
|
||||
groupCode = if (contact is Group) contact.uin else 0L,
|
||||
fileMd5 = videoFileMd5,
|
||||
businessType = 1,
|
||||
flagSupportLargeSize = 1,
|
||||
flagClientQuicProtoEnable = 1,
|
||||
fileType = 2, // maybe 1 = newly uploaded video, unverified
|
||||
downType = 2,
|
||||
sceneType = 2, // hooked 0 and 1, but unknown
|
||||
reqTransferType = 1,
|
||||
reqHostType = 11,
|
||||
),
|
||||
msgExtensionReq = listOf(
|
||||
PttShortVideo.ExtensionReq(subBusiType = 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -81,6 +81,10 @@ internal object MiraiCoreServices {
|
||||
msgProtocol,
|
||||
"net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol"
|
||||
) { net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol() }
|
||||
Services.register(
|
||||
msgProtocol,
|
||||
"net.mamoe.mirai.internal.message.protocol.impl.ShortVideoProtocol"
|
||||
) { net.mamoe.mirai.internal.message.protocol.impl.ShortVideoProtocol() }
|
||||
Services.register(
|
||||
msgProtocol,
|
||||
"net.mamoe.mirai.internal.message.protocol.impl.TextProtocol"
|
||||
|
@ -20,6 +20,7 @@ net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.PttMessageProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.QuoteReplyProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.ShortVideoProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.TextProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.VipFaceProtocol
|
||||
net.mamoe.mirai.internal.message.protocol.impl.ForwardMessageProtocol
|
||||
|
@ -293,7 +293,10 @@ internal class MessageRefineTest : AbstractTestWithMiraiImpl() {
|
||||
1234567890, 1617378549, "群垃圾,时不时来被gc", PlainText("5")
|
||||
),
|
||||
ForwardMessage.Node(
|
||||
1234567890, 1617382639, "群垃圾,时不时来被gc", redefined[2].messageChain[QuoteReply]!! + PlainText("aseff")
|
||||
1234567890,
|
||||
1617382639,
|
||||
"群垃圾,时不时来被gc",
|
||||
redefined[2].messageChain[QuoteReply]!! + PlainText("aseff")
|
||||
),
|
||||
),
|
||||
redefined,
|
||||
@ -313,7 +316,7 @@ private fun sourceStub(
|
||||
|
||||
private suspend fun testRecursiveRefine(list: List<ImMsgBody.Elem>, expected: MessageChain, isLight: Boolean) {
|
||||
val actual = buildMessageChain {
|
||||
MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, bot, this, null)
|
||||
MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, 0L, bot, this, null)
|
||||
}.let { c ->
|
||||
if (isLight) {
|
||||
c.refineLight(bot)
|
||||
@ -370,10 +373,12 @@ private fun assertMessageChainEquals(expected: MessageChain, actual: MessageChai
|
||||
if (a !is QuoteReply) return false
|
||||
if (!compare(e.source.originalMessage, a.source.originalMessage)) return false
|
||||
}
|
||||
|
||||
is MessageSource -> {
|
||||
if (a !is MessageSource) return false
|
||||
if (!compare(e.originalMessage, a.originalMessage)) return false
|
||||
}
|
||||
|
||||
is ForwardMessage -> {
|
||||
if (a !is ForwardMessage) return false
|
||||
if (e.brief != a.brief) return false
|
||||
@ -383,10 +388,12 @@ private fun assertMessageChainEquals(expected: MessageChain, actual: MessageChai
|
||||
if (e.preview != a.preview) return false
|
||||
assertNodesEquals(e.nodeList, a.nodeList)
|
||||
}
|
||||
|
||||
is Image -> {
|
||||
if (a !is Image) return false
|
||||
if (e.imageId != a.imageId) return false
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (e != a) return false
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ internal class MessageProtocolFacadeTest : AbstractTest() {
|
||||
PokeMessageProtocol
|
||||
PttMessageProtocol
|
||||
RichMessageProtocol
|
||||
ShortVideoMessageProtocol
|
||||
TextProtocol
|
||||
VipFaceProtocol
|
||||
ForwardMessageProtocol
|
||||
|
@ -236,7 +236,13 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
protected open fun Deferred<ChecksConfiguration>.doDecoderChecks() {
|
||||
val config = this.getCompleted()
|
||||
doDecoderChecks(config.messageChain, protocols) {
|
||||
decodeAndRefineLight(config.elems, config.groupIdOrZero, config.messageSourceKind, bot)
|
||||
decodeAndRefineLight(
|
||||
config.elems,
|
||||
config.groupIdOrZero,
|
||||
config.messageSourceKind,
|
||||
config.target?.id ?: 0L,
|
||||
bot
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,6 +286,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
sender = bot,
|
||||
target = defaultTarget
|
||||
)
|
||||
|
||||
is Friend -> OnlineMessageSourceToFriendImpl(
|
||||
sequenceIds = intArrayOf(1),
|
||||
internalIds = intArrayOf(1),
|
||||
@ -288,6 +295,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler
|
||||
sender = bot,
|
||||
target = defaultTarget
|
||||
)
|
||||
|
||||
else -> error("Unexpected target: $defaultTarget")
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() {
|
||||
),
|
||||
groupIdOrZero = 0,
|
||||
MessageSourceKind.GROUP,
|
||||
fromId = 0L,
|
||||
bot,
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user