From 5a949d1f2fb15c8a85a4fb92e13eb76b04ac1f69 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Mon, 17 Jul 2023 12:20:48 +0800 Subject: [PATCH 01/43] initial support for ShortVideo message --- .../kotlin/message/data/ShortVideo.kt | 49 ++++ .../message/data/visitor/MessageVisitor.kt | 6 + .../roaming/RoamingMessagesImplGroup.kt | 17 +- .../roaming/TimeBasedRoamingMessagesImpl.kt | 28 ++- .../kotlin/message/ReceiveMessageHandler.kt | 39 ++- .../kotlin/message/data/shortVideo.kt | 142 +++++++++++ .../message/protocol/MessageProtocolFacade.kt | 15 +- .../protocol/decode/MessageDecoderPipeline.kt | 1 + .../protocol/impl/ShortVideoProtocol.kt | 32 +++ .../message/source/offlineSourceImpl.kt | 5 +- .../notice/group/GroupMessageProcessor.kt | 28 ++- .../notice/priv/PrivateMessageProcessor.kt | 17 +- .../protocol/data/proto/PttShortVideo.kt | 237 ++++++++++++++++++ .../network/protocol/packet/PacketFactory.kt | 2 + .../packet/chat/shortvideo/PttCenterSvr.kt | 94 +++++++ .../kotlin/utils/MiraiCoreServices.kt | 4 + ....internal.message.protocol.MessageProtocol | 1 + .../kotlin/message/data/MessageRefineTest.kt | 11 +- .../protocol/MessageProtocolFacadeTest.kt | 1 + .../impl/AbstractMessageProtocolTest.kt | 10 +- .../message/protocol/impl/FaceProtocolTest.kt | 1 + 21 files changed, 720 insertions(+), 20 deletions(-) create mode 100644 mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt create mode 100644 mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt create mode 100644 mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt create mode 100644 mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PttShortVideo.kt create mode 100644 mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt new file mode 100644 index 0000000000..01af399ea8 --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -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, { it.safeCast() }) + + /** + * 文件 ID. + */ + public val fileId: String + + /** + * 文件 MD5. 16 bytes. + */ + public val fileMd5: ByteArray + + + @MiraiInternalApi + override fun accept(visitor: MessageVisitor, 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, { it.safeCast() }) { + public const val SERIAL_NAME: String = "OnlineShortAudio" + } +} \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/visitor/MessageVisitor.kt b/mirai-core-api/src/commonMain/kotlin/message/data/visitor/MessageVisitor.kt index 3c2b633918..de1bae7905 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/visitor/MessageVisitor.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/visitor/MessageVisitor.kt @@ -41,6 +41,8 @@ public interface MessageVisitor { 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 : MessageVisitor 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) } diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt index 4745b29908..29dc3f9a75 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt @@ -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 diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt index 16bf47b79d..fcc593822b 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt @@ -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() + ) + ) + ) } } } diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index f968140ace..ebed777826 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -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() ?: SimpleRefineContext(mutableMapOf())) + .apply(mutableRefineContextApplier), + facade + ) } //internal fun List.toMessageChainOffline( @@ -129,13 +146,21 @@ private fun List.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) { diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt new file mode 100644 index 0000000000..cc8ff39e2f --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -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() + 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() + + 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") + val FromId = RefineContextKey("FromId") + val GroupIdOrZero = RefineContextKey("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 + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt index 4220967880..f2905115ed 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt @@ -77,6 +77,7 @@ internal interface MessageProtocolFacade { elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, + fromId: Long, bot: Bot, builder: MessageChainBuilder, containingMsg: MsgComm.Msg? = null, @@ -134,8 +135,11 @@ internal interface MessageProtocolFacade { elements: List, 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, 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, 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, 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 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", diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/decode/MessageDecoderPipeline.kt b/mirai-core/src/commonMain/kotlin/message/protocol/decode/MessageDecoderPipeline.kt index ef0a27c4de..6170a636ca 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/decode/MessageDecoderPipeline.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/decode/MessageDecoderPipeline.kt @@ -29,6 +29,7 @@ internal interface MessageDecoderContext : ProcessorPipelineContext("messageSourceKind") val GROUP_ID = TypeKey("groupId") // zero if not group val CONTAINING_MSG = TypeKey("containingMsg") + val FROM_ID = TypeKey("fromId") // group/temp = sender, friend/stranger = this } } diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt new file mode 100644 index 0000000000..394b815fad --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -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)) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt index b429c320ce..c06bb954ca 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt @@ -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( diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt index 0d46b26bb3..8999e073c4 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt @@ -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, ), 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 99dadfc656..f9f10c25af 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt @@ -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(type val group = bot.getGroupByUinOrCode(tmpHead.groupUin) ?: return handlePrivateMessage(data, group[senderUin] ?: return) } + else -> markNotConsumed() } @@ -129,7 +133,18 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor(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) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PttShortVideo.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PttShortVideo.kt new file mode 100644 index 0000000000..c6d8576c6e --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PttShortVideo.kt @@ -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 = emptyList(), + @JvmField @ProtoNumber(11) val urlArgs: String = "", + @JvmField @ProtoNumber(21) val strHostIpv6: List = emptyList(), + @JvmField @ProtoNumber(22) val strDomain: List = 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 = emptyList(), + @JvmField @ProtoNumber(4) val diffAreaOutAddr: List = 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 = emptyList(), + @JvmField @ProtoNumber(8) val diffAreaInnerAddr: List = 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 = emptyList(), + @JvmField @ProtoNumber(4) val diffAreaOutAddr: List = 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 = emptyList(), + @JvmField @ProtoNumber(9) val diffAreaInnerAddr: List = emptyList(), + @JvmField @ProtoNumber(10) val dataHole: List = 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 = emptyList(), + @JvmField @ProtoNumber(4) val diffAreaOutAddr: List = 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 = emptyList(), + @JvmField @ProtoNumber(9) val diffAreaInnerAddr: List = emptyList(), + @JvmField @ProtoNumber(10) val dataHole: List = 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 = emptyList(), + @JvmField @ProtoNumber(6) val msgShortVideoDeleteReq: List = emptyList(), + @JvmField @ProtoNumber(100) val msgExtensionReq: List = 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 = emptyList(), + @JvmField @ProtoNumber(6) val msgShortVideoDeleteResp: List = emptyList(), + @JvmField @ProtoNumber(100) val changeChannel: Int = 0, + @JvmField @ProtoNumber(101) val allowRetry: Int = 0 + ) : ProtoBuf +} \ No newline at end of file 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 e0219ced6b..5ba59b2815 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -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, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt new file mode 100644 index 0000000000..44346edc20 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt @@ -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("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) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt index d83a597ee1..352b39bdc4 100644 --- a/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt +++ b/mirai-core/src/commonMain/kotlin/utils/MiraiCoreServices.kt @@ -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" diff --git a/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.internal.message.protocol.MessageProtocol b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.internal.message.protocol.MessageProtocol index 8cabda9dc5..ef877583ef 100644 --- a/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.internal.message.protocol.MessageProtocol +++ b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.internal.message.protocol.MessageProtocol @@ -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 diff --git a/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt b/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt index 72106cdc7e..718165d0f5 100644 --- a/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt @@ -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, 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 } diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt index 004ffbc2ce..42ca9dad18 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt @@ -32,6 +32,7 @@ internal class MessageProtocolFacadeTest : AbstractTest() { PokeMessageProtocol PttMessageProtocol RichMessageProtocol + ShortVideoMessageProtocol TextProtocol VipFaceProtocol ForwardMessageProtocol diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt index 1d2c10ef8b..7b0ae24890 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt @@ -236,7 +236,13 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler protected open fun Deferred.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") } } diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt index 2980b4ec8a..245d9c7df1 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt @@ -57,6 +57,7 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() { ), groupIdOrZero = 0, MessageSourceKind.GROUP, + fromId = 0L, bot, ) } From 3c75356552346ce7c8eebaab8ffbf372b41c8414 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Mon, 17 Jul 2023 12:21:09 +0800 Subject: [PATCH 02/43] dump api --- .../compatibility-validation/jvm/api/jvm.api | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index bcf29e720c..2e69105e46 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -4978,6 +4978,16 @@ public abstract class net/mamoe/mirai/message/data/OnlineMessageSource$Outgoing$ public final class net/mamoe/mirai/message/data/OnlineMessageSource$Outgoing$ToTemp$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/OnlineShortVideo : net/mamoe/mirai/message/data/ShortVideo { + public static final field Key Lnet/mamoe/mirai/message/data/OnlineShortVideo$Key; + public static final field SERIAL_NAME Ljava/lang/String; + public abstract fun getUrlForDownload ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/OnlineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { + public static final field SERIAL_NAME Ljava/lang/String; +} + public final class net/mamoe/mirai/message/data/OrNullDelegate { public static final synthetic fun box-impl (Ljava/lang/Object;)Lnet/mamoe/mirai/message/data/OrNullDelegate; public static fun constructor-impl (Ljava/lang/Object;)Ljava/lang/Object; @@ -5223,6 +5233,16 @@ public abstract interface class net/mamoe/mirai/message/data/ServiceMessage : ne public final class net/mamoe/mirai/message/data/ServiceMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { + public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; + public abstract fun getFileId ()Ljava/lang/String; + public abstract fun getFileMd5 ()[B + public fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { +} + public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { public static final field INSTANCE Lnet/mamoe/mirai/message/data/ShowImageFlag; public static final field SERIAL_NAME Ljava/lang/String; From 4c94b4563aaf96139d8595498be5803ee7ad298a Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Tue, 1 Aug 2023 23:33:02 +0800 Subject: [PATCH 03/43] [core] upload protocol --- .../src/commonMain/kotlin/contact/Contact.kt | 11 +- .../kotlin/message/data/ShortVideo.kt | 36 ++++- .../src/jvmBaseMain/kotlin/contact/Contact.kt | 8 +- .../internal/contact/AbstractMockContact.kt | 9 +- .../kotlin/contact/AbstractContact.kt | 86 ++++++++++++ .../kotlin/message/data/shortVideo.kt | 126 ++++++++++++++++-- .../protocol/impl/ShortVideoProtocol.kt | 47 +++++++ .../kotlin/network/highway/Highway.kt | 3 +- .../network/protocol/packet/PacketFactory.kt | 1 + .../packet/chat/shortvideo/PttCenterSvr.kt | 89 +++++++++++++ 10 files changed, 387 insertions(+), 29 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index fe1704a18a..ed5d88989d 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -25,8 +25,6 @@ import net.mamoe.mirai.recallMessage import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage import kotlin.coroutines.cancellation.CancellationException -import kotlin.jvm.JvmStatic -import kotlin.jvm.JvmSynthetic /** * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. @@ -88,6 +86,15 @@ public expect interface Contact : ContactOrBot, CoroutineScope { */ public suspend fun uploadImage(resource: ExternalResource): Image + /** + * TODO: doc + * + * @param thumbnail 短视频封面图,为图片资源 + * @param video 视频文件 + */ + + public suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo + @JvmBlockingBridge public companion object { /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 01af399ea8..603667a2be 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -15,9 +15,6 @@ import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.safeCast public interface ShortVideo : MessageContent, ConstrainSingle { - public companion object Key : - AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) - /** * 文件 ID. */ @@ -28,6 +25,16 @@ public interface ShortVideo : MessageContent, ConstrainSingle { */ public val fileMd5: ByteArray + /* + * 文件大小 + */ + public val fileSize: Long + + /** + * 文件类型 + */ + public val fileFormat: String + @MiraiInternalApi override fun accept(visitor: MessageVisitor, data: D): R { @@ -36,14 +43,35 @@ public interface ShortVideo : MessageContent, ConstrainSingle { override val key: MessageKey<*> get() = Key + + + public companion object Key : + AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) } @NotStableForInheritance public interface OnlineShortVideo : ShortVideo { + /* + * 文件名 + */ + public val fileName: String + + /** + * 下载链接 + */ public val urlForDownload: String public companion object Key : AbstractPolymorphicMessageKey(ShortVideo, { it.safeCast() }) { - public const val SERIAL_NAME: String = "OnlineShortAudio" + public const val SERIAL_NAME: String = "OnlineShortVideo" + } +} + +@NotStableForInheritance +public interface OfflineShortVideo : ShortVideo { + + public companion object Key : + AbstractPolymorphicMessageKey(ShortVideo, { it.safeCast() }) { + public const val SERIAL_NAME: String = "OfflineShortVideo" } } \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt index dcd2bad0a7..ea187e55f8 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -16,10 +16,7 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.event.events.* import net.mamoe.mirai.message.MessageReceipt -import net.mamoe.mirai.message.data.Image -import net.mamoe.mirai.message.data.Message -import net.mamoe.mirai.message.data.isContentEmpty -import net.mamoe.mirai.message.data.toPlainText +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage @@ -90,6 +87,9 @@ public actual interface Contact : ContactOrBot, CoroutineScope { */ public actual suspend fun uploadImage(resource: ExternalResource): Image + // TODO: doc + public actual suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo + public actual companion object { /** * 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人 diff --git a/mirai-core-mock/src/internal/contact/AbstractMockContact.kt b/mirai-core-mock/src/internal/contact/AbstractMockContact.kt index de810a9c3a..ef1b40dc7e 100644 --- a/mirai-core-mock/src/internal/contact/AbstractMockContact.kt +++ b/mirai-core-mock/src/internal/contact/AbstractMockContact.kt @@ -16,10 +16,7 @@ import net.mamoe.mirai.event.events.MessagePreSendEvent import net.mamoe.mirai.internal.contact.broadcastMessagePreSendEvent import net.mamoe.mirai.internal.contact.replaceMagicCodes import net.mamoe.mirai.message.MessageReceipt -import net.mamoe.mirai.message.data.Image -import net.mamoe.mirai.message.data.Message -import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.message.data.OnlineMessageSource +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.mock.MockBot import net.mamoe.mirai.mock.contact.MockContact import net.mamoe.mirai.utils.* @@ -61,6 +58,10 @@ internal abstract class AbstractMockContact( return bot.uploadMockImage(resource) } + override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { + TODO("mock upload short video") + } + override fun toString(): String { return "$id" } diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index 7d67d1968a..d9f6f5cd14 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -9,9 +9,20 @@ package net.mamoe.mirai.internal.contact +import io.ktor.utils.io.core.* import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.internal.network.highway.Highway +import net.mamoe.mirai.internal.network.highway.ResourceKind +import net.mamoe.mirai.internal.network.protocol.data.proto.PttShortVideo +import net.mamoe.mirai.internal.network.protocol.packet.chat.video.PttCenterSvr +import net.mamoe.mirai.internal.utils.io.serialization.loadAs +import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.message.data.ShortVideo +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.childScopeContext import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext @@ -21,6 +32,81 @@ internal abstract class AbstractContact( parentCoroutineContext: CoroutineContext, ) : Contact { final override val coroutineContext: CoroutineContext = parentCoroutineContext.childScopeContext() + + override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { + // TODO: add interrupt uploading video event, just like upload image + // TODO: check mp4 file + // TODO: check contact is group or friend + + val uploadResp = bot.network.sendAndExpect( + PttCenterSvr.GroupShortVideoUpReq( + client = bot.client, + contact = this, + thumbnailFileMd5 = thumbnail.md5, + thumbnailFileSize = thumbnail.size, + videoFileMd5 = video.md5, + videoFileSize = video.size + ) + ) + + if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { + return OfflineShortVideoImpl( + uploadResp.fileId, + video.md5, + video.size, + "mp4", // TODO: support more formats to upload + thumbnail.md5, + thumbnail.size, + 1280, // TODO: read thumbnail image width + 720 // TODO: read thumbnail image height + ) + } + + val resource = buildPacket { + thumbnail.input().use { it.copyTo(this) } + video.input().use { it.copyTo(this) } + }.readBytes().toExternalResource("mp4") + + val highwayResp = Highway.uploadResourceBdh( + bot = bot, + resource = resource, + kind = ResourceKind.SHORT_VIDEO, + commandId = 25, + extendInfo = buildPacket { + writeProtoBuf( + PttShortVideo.PttShortVideoUploadReq.serializer(), + PttCenterSvr.GroupShortVideoUpReq.buildShortVideoFileInfo( + client = bot.client, + contact = this@AbstractContact, + thumbnailFileMd5 = thumbnail.md5, + thumbnailFileSize = thumbnail.size, + videoFileMd5 = video.md5, + videoFileSize = video.size + ) + ) + }.readBytes(), + encrypt = true + ) + + resource.close() + + if (highwayResp.extendInfo == null) { + error("highway upload short video failed, extendInfo is null.") + } + + val highwayUploadResp = highwayResp.extendInfo!!.loadAs(PttShortVideo.PttShortVideoUploadResp.serializer()) + + return OfflineShortVideoImpl( + highwayUploadResp.fileid, + video.md5, + video.size, + "mp4", // TODO: support more formats to upload + thumbnail.md5, + thumbnail.size, + 1280, // TODO: read thumbnail image width + 720 // TODO: read thumbnail image height + ) + } } internal val Contact.userIdOrNull: Long? get() = if (this is User) this.id else null diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index cc8ff39e2f..5826a51f1e 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -20,16 +20,12 @@ 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.message.data.* import net.mamoe.mirai.utils.cast -import net.mamoe.mirai.utils.isSameType import net.mamoe.mirai.utils.toUHexString /** - * refine to [OnlineShortVideoImpl] + * receive from pipeline and refine to [OnlineShortVideoImpl] */ internal class OnlineShortVideoMsgInternal( private val videoFile: ImMsgBody.VideoFile @@ -49,7 +45,7 @@ internal class OnlineShortVideoMsgInternal( 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 + else -> return null // TODO: ignore processing stranger's video message }.cast() val sender = when (sourceKind) { net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId) @@ -58,7 +54,7 @@ internal class OnlineShortVideoMsgInternal( checkNotNull(group).members[fromId] } - else -> return null // don't process stranger's video message + else -> return null // TODO: ignore processing stranger's video message }.cast() val shortVideoDownloadReq = bot.network.sendAndExpect( @@ -84,7 +80,14 @@ internal class OnlineShortVideoMsgInternal( return OnlineShortVideoImpl( videoFile.fileUuid.decodeToString(), shortVideoDownloadReq.fileMd5, - shortVideoDownloadReq.urlV4 + videoFile.fileName.decodeToString(), + videoFile.fileSize.toLong(), + videoFile.fileFormat.toVideoFormat(), + shortVideoDownloadReq.urlV4, + videoFile.thumbFileMd5, + videoFile.thumbFileSize.toLong(), + videoFile.thumbWidth, + videoFile.thumbHeight ) } @@ -102,7 +105,13 @@ internal class OnlineShortVideoMsgInternal( val FromId = RefineContextKey("FromId") val GroupIdOrZero = RefineContextKey("GroupIdOrZero") } +} +internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { + abstract val thumbMd5: ByteArray + abstract val thumbSize: Long + abstract val thumbWidth: Int + abstract val thumbHeight: Int } @Suppress("DuplicatedCode") @@ -111,20 +120,71 @@ internal class OnlineShortVideoMsgInternal( internal class OnlineShortVideoImpl( override val fileId: String, override val fileMd5: ByteArray, - override val urlForDownload: String -) : OnlineShortVideo { + override val fileName: String, + override val fileSize: Long, + override val fileFormat: String, + override val urlForDownload: String, + override val thumbMd5: ByteArray, + override val thumbSize: Long, + override val thumbWidth: Int, + override val thumbHeight: Int +) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { + + override fun toString(): String { + return "[mirai:svideo:$fileId, name=$fileName, md5=${fileMd5.toUHexString("")}]" + } + + override fun contentToString(): String { + return "[视频]" + } override fun equals(other: Any?): Boolean { if (this === other) return true - if (!isSameType(this, other)) return false + if (javaClass != other?.javaClass) return false + + other as OnlineShortVideoImpl if (fileId != other.fileId) return false - if (urlForDownload != other.urlForDownload) return false if (!fileMd5.contentEquals(other.fileMd5)) return false + if (fileName != other.fileName) return false + if (fileSize != other.fileSize) return false + if (fileFormat != other.fileFormat) return false + if (urlForDownload != other.urlForDownload) return false + if (!thumbMd5.contentEquals(other.thumbMd5)) return false + if (thumbSize != other.thumbSize) return false + if (thumbWidth != other.thumbWidth) return false + if (thumbHeight != other.thumbHeight) return false return true } + override fun hashCode(): Int { + var result = fileId.hashCode() + result = 31 * result + fileMd5.contentHashCode() + result = 31 * result + fileName.hashCode() + result = 31 * result + fileSize.hashCode() + result = 31 * result + fileFormat.hashCode() + result = 31 * result + urlForDownload.hashCode() + result = 31 * result + thumbMd5.contentHashCode() + result = 31 * result + thumbSize.hashCode() + result = 31 * result + thumbWidth + result = 31 * result + thumbHeight + return result + } +} + +@Serializable +internal class OfflineShortVideoImpl( + override val fileId: String, + override val fileMd5: ByteArray, + override val fileSize: Long, + override val fileFormat: String, + override val thumbMd5: ByteArray, + override val thumbSize: Long, + override val thumbWidth: Int, + override val thumbHeight: Int +) : OfflineShortVideo, AbstractShortVideoWithThumbnail() { + override fun toString(): String { return "[mirai:svideo:$fileId, md5=${fileMd5.toUHexString("")}]" } @@ -133,10 +193,48 @@ internal class OnlineShortVideoImpl( return "[视频]" } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as OfflineShortVideoImpl + + if (fileId != other.fileId) return false + if (!fileMd5.contentEquals(other.fileMd5)) return false + if (fileSize != other.fileSize) return false + if (fileFormat != other.fileFormat) return false + if (!thumbMd5.contentEquals(other.thumbMd5)) return false + if (thumbSize != other.thumbSize) return false + if (thumbWidth != other.thumbWidth) return false + if (thumbHeight != other.thumbHeight) return false + + return true + } + override fun hashCode(): Int { var result = fileId.hashCode() result = 31 * result + fileMd5.contentHashCode() - result = 31 * result + urlForDownload.hashCode() + result = 31 * result + fileSize.hashCode() + result = 31 * result + fileFormat.hashCode() + result = 31 * result + thumbMd5.contentHashCode() + result = 31 * result + thumbSize.hashCode() + result = 31 * result + thumbWidth + result = 31 * result + thumbHeight return result } +} + +private fun Int.toVideoFormat() = when (this) { + 1 -> "ts" + 2 -> "avi" + 3 -> "mp4" + 4 -> "wmv" + 5 -> "mkv" + 6 -> "rmvb" + 7 -> "rm" + 8 -> "afs" + 9 -> "mov" + 10 -> "mod" + 11 -> "mts" + else -> "mirai" // unknown to default } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 394b815fad..36fc2a0a32 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -9,16 +9,32 @@ package net.mamoe.mirai.internal.message.protocol.impl +import net.mamoe.mirai.internal.message.data.AbstractShortVideoWithThumbnail +import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.internal.message.data.OnlineShortVideoImpl 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.message.protocol.encode.MessageEncoder +import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderContext +import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.message.data.MessageContent +import net.mamoe.mirai.message.data.ShortVideo +import net.mamoe.mirai.message.data.SingleMessage +import net.mamoe.mirai.utils.toUHexString internal class ShortVideoProtocol : MessageProtocol() { override fun ProcessorCollector.collectProcessorsImpl() { add(Decoder()) + add(Encoder()) + + MessageSerializer.superclassesScope(ShortVideo::class, MessageContent::class, SingleMessage::class) { + add(MessageSerializer(OfflineShortVideoImpl::class, OfflineShortVideoImpl.serializer())) + add(MessageSerializer(OnlineShortVideoImpl::class, OnlineShortVideoImpl.serializer())) + } } private class Decoder : MessageDecoder { @@ -29,4 +45,35 @@ internal class ShortVideoProtocol : MessageProtocol() { collect(OnlineShortVideoMsgInternal(videoFile)) } } + + private class Encoder : MessageEncoder { + override suspend fun MessageEncoderContext.process(data: AbstractShortVideoWithThumbnail) { + markAsConsumed() + + collect(ImMsgBody.Elem(text = ImMsgBody.Text("你的QQ暂不支持查看视频短片,请期待后续版本。"))) + collect( + ImMsgBody.Elem( + videoFile = ImMsgBody.VideoFile( + fileUuid = data.fileId.encodeToByteArray(), + fileMd5 = data.fileMd5, + fileName = (data.fileMd5.toUHexString("") + ".mp4").encodeToByteArray(), + fileFormat = 3, // mp4, + fileTime = 10, + fileSize = data.fileSize.toInt(), + thumbWidth = data.thumbWidth, + thumbHeight = data.thumbHeight, + thumbFileMd5 = data.thumbMd5, + thumbFileSize = data.thumbSize.toInt(), + busiType = 0, + fromChatType = -1, + toChatType = -1, + boolSupportProgressive = true, + fileWidth = data.thumbWidth, + fileHeight = data.thumbHeight + ) + ) + ) + } + + } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt index c8665cb4c0..93d52fd157 100644 --- a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt +++ b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt @@ -32,7 +32,6 @@ import net.mamoe.mirai.internal.utils.sizeToString import net.mamoe.mirai.utils.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract -import kotlin.jvm.Volatile import kotlin.math.roundToInt internal object Highway { @@ -126,6 +125,8 @@ internal enum class ResourceKind( FORWARD_MESSAGE("forward message"), ANNOUNCEMENT_IMAGE("announcement image"), + + SHORT_VIDEO("short video") ; override fun toString(): String = display 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 5ba59b2815..2c4d7a886c 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -152,6 +152,7 @@ internal object KnownPacketFactories { PttStore.GroupPttUp, PttStore.GroupPttDown, PttStore.C2CPttDown, + PttCenterSvr.GroupShortVideoUpReq, PttCenterSvr.ShortVideoDownReq, LongConn.OffPicUp, // LongConn.OffPicDown, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt index 44346edc20..8ff176cc60 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt @@ -23,8 +23,97 @@ 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 +import net.mamoe.mirai.utils.toUHexString internal class PttCenterSvr { + object GroupShortVideoUpReq : + OutgoingPacketFactory("PttCenterSvr.GroupShortVideoUpReq") { + sealed class Response : Packet { + class FileExists(val fileId: String) : Response() { + override fun toString(): String { + return "PttCenterSvr.GroupShortVideoUpReq.Response.FileExists(fileId=${fileId})" + } + } + + object RequireUpload : Response() { + override fun toString(): String { + return "PttCenterSvr.GroupShortVideoUpReq.Response.RequireUpload" + } + } + } + + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { + val resp = readProtoBuf(PttShortVideo.RspBody.serializer()) + val upResp = resp.msgPttShortVideoUploadResp ?: return Response.RequireUpload + + return if (upResp.fileExist == 1) { + Response.FileExists(upResp.fileid) + } else { + Response.RequireUpload + } + } + + // TODO: only support mp4 video now. + operator fun invoke( + client: QQAndroidClient, + contact: Contact, + thumbnailFileMd5: ByteArray, + thumbnailFileSize: Long, + videoFileMd5: ByteArray, + videoFileSize: Long + ) = buildOutgoingUniPacket(client) { sequenceId -> + writeProtoBuf( + PttShortVideo.ReqBody.serializer(), + PttShortVideo.ReqBody( + cmd = 300, + seq = sequenceId, + msgPttShortVideoUploadReq = buildShortVideoFileInfo( + client, + contact, + thumbnailFileMd5, + thumbnailFileSize, + videoFileMd5, + videoFileSize + ), + msgExtensionReq = listOf( + PttShortVideo.ExtensionReq( + subBusiType = 0, + userCnt = 1 + ) + ) + ) + ) + } + + internal fun buildShortVideoFileInfo( + client: QQAndroidClient, + contact: Contact, + thumbnailFileMd5: ByteArray, + thumbnailFileSize: Long, + videoFileMd5: ByteArray, + videoFileSize: Long + ) = PttShortVideo.PttShortVideoUploadReq( + fromuin = client.uin, + touin = contact.uin, + chatType = 1, // guild channel = 4, others = 1 + clientType = 2, + msgPttShortVideoFileInfo = PttShortVideo.PttShortVideoFileInfo( + fileName = videoFileMd5.toUHexString("") + ".mp4", + fileMd5 = videoFileMd5, + fileSize = videoFileSize, + fileResLength = 1280, + fileResWidth = 720, + // Lcom/tencent/mobileqq/transfile/ShortVideoUploadProcessor;getFormat(Ljava/lang/String;)I + fileFormat = 3, + fileTime = 120, + thumbFileMd5 = thumbnailFileMd5, + thumbFileSize = thumbnailFileSize + ), + groupCode = if (contact is Group) contact.uin else 0, + flagSupportLargeSize = 1 + ) + } + object ShortVideoDownReq : OutgoingPacketFactory("PttCenterSvr.ShortVideoDownReq") { sealed class Response : Packet { class Success(val fileMd5: ByteArray, val urlV4: String, val urlV6: String?) : Response() { From 4ee4751f4f983c4094c900856146ca58352b361a Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 09:01:38 +0800 Subject: [PATCH 04/43] [core] short video upload event --- .../event/events/ShortVideoUploadEvent.kt | 93 +++++++++++++++++++ .../kotlin/contact/AbstractContact.kt | 32 +++++-- 2 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt new file mode 100644 index 0000000000..8786bdfb01 --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt @@ -0,0 +1,93 @@ +/* + * 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 + */ + +@file:JvmMultifileClass +@file:JvmName("BotEventsKt") + +package net.mamoe.mirai.event.events + +import net.mamoe.mirai.Bot +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.contact.Contact.Companion.uploadImage +import net.mamoe.mirai.event.AbstractEvent +import net.mamoe.mirai.event.CancellableEvent +import net.mamoe.mirai.event.events.ShortVideoUploadEvent.Failed +import net.mamoe.mirai.event.events.ShortVideoUploadEvent.Succeed +import net.mamoe.mirai.internal.event.VerboseEvent +import net.mamoe.mirai.message.data.ShortVideo +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.MiraiInternalApi + + +/** + * 短视频上传前. 可以阻止上传. + * + * 此事件总是在 [ShortVideoUploadEvent] 之前广播. + * 若此事件被取消, [ShortVideoUploadEvent] 不会广播. + * + * @see Contact.uploadShortVideo 上传短视频. 为广播这个事件的唯一途径 + */ +@OptIn(MiraiInternalApi::class) +public data class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( + public val target: Contact, + public val thumbnailSource: ExternalResource, + public val videoSource: ExternalResource +) : BotEvent, BotActiveEvent, AbstractEvent(), CancellableEvent, VerboseEvent { + public override val bot: Bot + get() = target.bot +} + +/** + * 短视频上传完成. + * + * 此事件总是在 [BeforeImageUploadEvent] 之后广播. + * 若 [BeforeImageUploadEvent] 被取消, 此事件不会广播. + * + * @see Contact.uploadImage 上传图片. 为广播这个事件的唯一途径 + * + * @see Succeed + * @see Failed + */ +@OptIn(MiraiInternalApi::class) +public sealed class ShortVideoUploadEvent : BotEvent, BotActiveEvent, AbstractEvent(), VerboseEvent { + public abstract val target: Contact + public abstract val thumbnailSource: ExternalResource + public abstract val videoSource: ExternalResource + public override val bot: Bot + get() = target.bot + + public data class Succeed @MiraiInternalApi constructor( + override val target: Contact, + override val thumbnailSource: ExternalResource, + override val videoSource: ExternalResource, + val video: ShortVideo + ) : ShortVideoUploadEvent() { + override fun toString(): String { + return "ShortVideoUploadEvent.Succeed(target=$target, " + + "thumbnailSource=$thumbnailSource, " + + "videoSource=$videoSource, " + + "video=$video)" + } + } + + public data class Failed @MiraiInternalApi constructor( + override val target: Contact, + override val thumbnailSource: ExternalResource, + override val videoSource: ExternalResource, + val errno: Int, + val message: String + ) : ShortVideoUploadEvent() { + override fun toString(): String { + return "ShortVideoUploadEvent.Failed(target=$target, " + + "thumbnailSource=$thumbnailSource, " + + "videoSource=$videoSource, " + + "errno=$errno, message='$message')" + } + } +} diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index d9f6f5cd14..131b730669 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -12,6 +12,10 @@ package net.mamoe.mirai.internal.contact import io.ktor.utils.io.core.* import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* +import net.mamoe.mirai.event.broadcast +import net.mamoe.mirai.event.events.BeforeShortVideoUploadEvent +import net.mamoe.mirai.event.events.EventCancelledException +import net.mamoe.mirai.event.events.ShortVideoUploadEvent import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl import net.mamoe.mirai.internal.network.highway.Highway @@ -34,7 +38,9 @@ internal abstract class AbstractContact( final override val coroutineContext: CoroutineContext = parentCoroutineContext.childScopeContext() override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { - // TODO: add interrupt uploading video event, just like upload image + if (BeforeShortVideoUploadEvent(this, thumbnail, video).broadcast().isCancelled) { + throw EventCancelledException("cancelled by BeforeShortVideoUploadEvent") + } // TODO: check mp4 file // TODO: check contact is group or friend @@ -49,6 +55,7 @@ internal abstract class AbstractContact( ) ) + // fast path if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { return OfflineShortVideoImpl( uploadResp.fileId, @@ -59,7 +66,9 @@ internal abstract class AbstractContact( thumbnail.size, 1280, // TODO: read thumbnail image width 720 // TODO: read thumbnail image height - ) + ).also { + ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() + } } val resource = buildPacket { @@ -67,7 +76,7 @@ internal abstract class AbstractContact( video.input().use { it.copyTo(this) } }.readBytes().toExternalResource("mp4") - val highwayResp = Highway.uploadResourceBdh( + val highwayRespExt = Highway.uploadResourceBdh( bot = bot, resource = resource, kind = ResourceKind.SHORT_VIDEO, @@ -86,15 +95,22 @@ internal abstract class AbstractContact( ) }.readBytes(), encrypt = true - ) + ).extendInfo resource.close() - if (highwayResp.extendInfo == null) { + if (highwayRespExt == null) { + ShortVideoUploadEvent.Failed( + this, + thumbnail, + video, + -1, + "highway upload short video failed, extendInfo is null." + ) error("highway upload short video failed, extendInfo is null.") } - val highwayUploadResp = highwayResp.extendInfo!!.loadAs(PttShortVideo.PttShortVideoUploadResp.serializer()) + val highwayUploadResp = highwayRespExt.loadAs(PttShortVideo.PttShortVideoUploadResp.serializer()) return OfflineShortVideoImpl( highwayUploadResp.fileid, @@ -105,7 +121,9 @@ internal abstract class AbstractContact( thumbnail.size, 1280, // TODO: read thumbnail image width 720 // TODO: read thumbnail image height - ) + ).also { + ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() + } } } From 1e0f190b348ee4c73cd0fcf93d5867e7bb9363b7 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 09:02:00 +0800 Subject: [PATCH 05/43] [core] doc --- .../src/commonMain/kotlin/contact/Contact.kt | 10 ++++++++-- .../src/commonMain/kotlin/utils/ExternalResource.kt | 7 +++---- .../src/jvmBaseMain/kotlin/contact/Contact.kt | 13 ++++++++++++- .../jvmBaseMain/kotlin/utils/ExternalResource.kt | 4 +++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index ed5d88989d..fa93bcecea 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -87,10 +87,16 @@ public expect interface Contact : ContactOrBot, CoroutineScope { public suspend fun uploadImage(resource: ExternalResource): Image /** - * TODO: doc + * 上传 [资源][ExternalResource] 作为短视频发送。 + * + * 同时需要上传缩略图作为视频消息显示的封面。 + * + * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * + * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. * * @param thumbnail 短视频封面图,为图片资源 - * @param video 视频文件 + * @param video 视频资源,目前仅支持上传 mp4 和 mkv 格式的视频。 */ public suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo diff --git a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt index fad49b70b2..08040b7178 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt @@ -26,9 +26,6 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage import kotlin.contracts.InvocationKind import kotlin.contracts.contract -import kotlin.jvm.JvmName -import kotlin.jvm.JvmOverloads -import kotlin.jvm.JvmStatic /** @@ -150,7 +147,9 @@ public expect interface ExternalResource : net.mamoe.mirai.utils.Closeable { * 文件格式,如 "png", "amr". 当无法自动识别格式时为 [DEFAULT_FORMAT_NAME]. * * 默认会从文件头识别, 支持的文件类型: - * png, jpg, gif, tif, bmp, amr, silk + * * 图片类型: png, jpg, gif, tif, bmp + * * 语音类型: amr, silk + * * 视频类类型: mp4, mkv * * @see net.mamoe.mirai.utils.getFileType * @see net.mamoe.mirai.utils.FILE_TYPES diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt index ea187e55f8..25e5e610cf 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -87,7 +87,18 @@ public actual interface Contact : ContactOrBot, CoroutineScope { */ public actual suspend fun uploadImage(resource: ExternalResource): Image - // TODO: doc + /** + * 上传 [资源][ExternalResource] 作为短视频发送。 + * + * 同时需要上传缩略图作为视频消息显示的封面。 + * + * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * + * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. + * + * @param thumbnail 短视频封面图,为图片资源 + * @param video 视频资源,目前仅支持上传 mp4 和 mkv 格式的视频。 + */ public actual suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo public actual companion object { diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt index 0021745e4d..c2e2c2a555 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt @@ -159,7 +159,9 @@ public actual interface ExternalResource : Closeable { * 文件格式,如 "png", "amr". 当无法自动识别格式时为 [DEFAULT_FORMAT_NAME]. * * 默认会从文件头识别, 支持的文件类型: - * png, jpg, gif, tif, bmp, amr, silk + * * 图片类型: png, jpg, gif, tif, bmp + * * 语音类型: amr, silk + * * 视频类类型: mp4, mkv * * @see net.mamoe.mirai.utils.getFileType * @see net.mamoe.mirai.utils.FILE_TYPES From 85f43a144798f22dd28fec7dc4b531ef4b3569c8 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 10:19:22 +0800 Subject: [PATCH 06/43] [core] protocol --- .../src/commonMain/kotlin/contact/Contact.kt | 2 +- .../kotlin/message/data/ShortVideo.kt | 4 +- .../src/jvmBaseMain/kotlin/contact/Contact.kt | 2 +- .../src/commonMain/kotlin/Files.kt | 36 ++++--- .../kotlin/contact/AbstractContact.kt | 93 +++++++++++-------- .../kotlin/message/data/shortVideo.kt | 13 ++- .../protocol/impl/ShortVideoProtocol.kt | 10 +- .../packet/chat/shortvideo/PttCenterSvr.kt | 16 ++-- 8 files changed, 106 insertions(+), 70 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index fa93bcecea..1df539cdf4 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -96,7 +96,7 @@ public expect interface Contact : ContactOrBot, CoroutineScope { * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. * * @param thumbnail 短视频封面图,为图片资源 - * @param video 视频资源,目前仅支持上传 mp4 和 mkv 格式的视频。 + * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 */ public suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 603667a2be..3867bb96f3 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -46,7 +46,9 @@ public interface ShortVideo : MessageContent, ConstrainSingle { public companion object Key : - AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) + AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) { + + } } @NotStableForInheritance diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt index 25e5e610cf..37d7fff4fb 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -97,7 +97,7 @@ public actual interface Contact : ContactOrBot, CoroutineScope { * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. * * @param thumbnail 短视频封面图,为图片资源 - * @param video 视频资源,目前仅支持上传 mp4 和 mkv 格式的视频。 + * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 */ public actual suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo diff --git a/mirai-core-utils/src/commonMain/kotlin/Files.kt b/mirai-core-utils/src/commonMain/kotlin/Files.kt index 29ffd2ecec..9f8990ce33 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Files.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Files.kt @@ -12,30 +12,36 @@ package net.mamoe.mirai.utils -import kotlin.jvm.JvmMultifileClass -import kotlin.jvm.JvmName +private class FileType( + signature: String, + val requiredHeaderSize: Int, + val formatName: String +) { + val signatureRegex = Regex(signature, RegexOption.IGNORE_CASE) +} /** * 文件头和文件类型列表 */ -public val FILE_TYPES: MutableMap = mutableMapOf( - "FFD8FF" to "jpg", - "89504E47" to "png", - "47494638" to "gif", +private val FILE_TYPES: List = listOf( + FileType("^FFD8FF", 3, "jpg"), + FileType("^89504E47", 4, "png"), + FileType("^47494638", 4, "gif"), + FileType("^424D", 3, "bmp"), + FileType("^2321414D52", 5, "amr"), + FileType("^02232153494C4B5F5633", 10, "silk"), + FileType("^FFD8FF", 3, "jpg"), + FileType("^([a-zA-Z]{8})66747970", 8, "jpg"), + //"49492A00" to "tif", // client doesn't support - "424D" to "bmp", //"52494646" to "webp", // pc client doesn't support - // "57415645" to "wav", // server doesn't support - - "2321414D52" to "amr", - "02232153494C4B5F5633" to "silk", ) /** * 在 [getFileType] 需要的 [ByteArray] 长度 */ -public val COUNT_BYTES_USED_FOR_DETECTING_FILE_TYPE: Int get() = FILE_TYPES.maxOf { it.key.length / 2 } +public val COUNT_BYTES_USED_FOR_DETECTING_FILE_TYPE: Int by lazy { FILE_TYPES.maxOf { it.requiredHeaderSize } } /* @@ -53,9 +59,9 @@ public fun getFileType(fileHeader: ByteArray): String? { "", length = COUNT_BYTES_USED_FOR_DETECTING_FILE_TYPE.coerceAtMost(fileHeader.size) ) - FILE_TYPES.forEach { (k, v) -> - if (hex.startsWith(k)) { - return v + FILE_TYPES.forEach { t -> + if (hex.contains(t.signatureRegex)) { + return t.formatName } } return null diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index 131b730669..16779065da 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.internal.contact import io.ktor.utils.io.core.* +import io.ktor.utils.io.core.use import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.broadcast @@ -18,6 +19,7 @@ import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.ShortVideoUploadEvent import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.internal.message.image.calculateImageInfo import net.mamoe.mirai.internal.network.highway.Highway import net.mamoe.mirai.internal.network.highway.ResourceKind import net.mamoe.mirai.internal.network.protocol.data.proto.PttShortVideo @@ -25,9 +27,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.video.PttCenterSvr import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.message.data.ShortVideo -import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource -import net.mamoe.mirai.utils.childScopeContext import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext @@ -38,11 +39,20 @@ internal abstract class AbstractContact( final override val coroutineContext: CoroutineContext = parentCoroutineContext.childScopeContext() override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { + if (this !is Group && this !is Friend) { + throw UnsupportedOperationException("short video can only upload to friend or group.") + } + + if (video.formatName != "mp4") { + throw UnsupportedOperationException("video format ${video.formatName} is not supported.") + } + if (BeforeShortVideoUploadEvent(this, thumbnail, video).broadcast().isCancelled) { throw EventCancelledException("cancelled by BeforeShortVideoUploadEvent") } - // TODO: check mp4 file - // TODO: check contact is group or friend + + // local uploaded offline short video uses video file md5 as its file name + val videoName = video.md5.toUHexString("") val uploadResp = bot.network.sendAndExpect( PttCenterSvr.GroupShortVideoUpReq( @@ -50,54 +60,63 @@ internal abstract class AbstractContact( contact = this, thumbnailFileMd5 = thumbnail.md5, thumbnailFileSize = thumbnail.size, + videoFileName = videoName, videoFileMd5 = video.md5, - videoFileSize = video.size + videoFileSize = video.size, + videoFileFormat = video.formatName ) ) + // get thumbnail image width and height + val thumbnailInfo = thumbnail.calculateImageInfo() + // fast path if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { return OfflineShortVideoImpl( uploadResp.fileId, video.md5, video.size, - "mp4", // TODO: support more formats to upload + video.formatName, thumbnail.md5, thumbnail.size, - 1280, // TODO: read thumbnail image width - 720 // TODO: read thumbnail image height + thumbnailInfo.width, + thumbnailInfo.height ).also { ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() } } - val resource = buildPacket { - thumbnail.input().use { it.copyTo(this) } - video.input().use { it.copyTo(this) } - }.readBytes().toExternalResource("mp4") - - val highwayRespExt = Highway.uploadResourceBdh( - bot = bot, - resource = resource, - kind = ResourceKind.SHORT_VIDEO, - commandId = 25, - extendInfo = buildPacket { - writeProtoBuf( - PttShortVideo.PttShortVideoUploadReq.serializer(), - PttCenterSvr.GroupShortVideoUpReq.buildShortVideoFileInfo( - client = bot.client, - contact = this@AbstractContact, - thumbnailFileMd5 = thumbnail.md5, - thumbnailFileSize = thumbnail.size, - videoFileMd5 = video.md5, - videoFileSize = video.size + val highwayRespExt = buildPacket { + thumbnail.input().use { + it.copyTo(this) + } + video.input().use { + it.copyTo(this) + } + }.readBytes().toExternalResource().use { resource -> + Highway.uploadResourceBdh( + bot = bot, + resource = resource, + kind = ResourceKind.SHORT_VIDEO, + commandId = 25, + extendInfo = buildPacket { + writeProtoBuf( + PttShortVideo.PttShortVideoUploadReq.serializer(), + PttCenterSvr.GroupShortVideoUpReq.buildShortVideoFileInfo( + client = bot.client, + contact = this@AbstractContact, + thumbnailFileMd5 = thumbnail.md5, + thumbnailFileSize = thumbnail.size, + videoFileName = videoName, + videoFileMd5 = video.md5, + videoFileSize = video.size, + videoFileFormat = video.formatName + ) ) - ) - }.readBytes(), - encrypt = true - ).extendInfo - - resource.close() + }.readBytes(), + encrypt = true + ).extendInfo + } if (highwayRespExt == null) { ShortVideoUploadEvent.Failed( @@ -116,11 +135,11 @@ internal abstract class AbstractContact( highwayUploadResp.fileid, video.md5, video.size, - "mp4", // TODO: support more formats to upload + video.formatName, thumbnail.md5, thumbnail.size, - 1280, // TODO: read thumbnail image width - 720 // TODO: read thumbnail image height + thumbnailInfo.width, + thumbnailInfo.height ).also { ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() } diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 5826a51f1e..9ec22481b5 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -110,8 +110,8 @@ internal class OnlineShortVideoMsgInternal( internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { abstract val thumbMd5: ByteArray abstract val thumbSize: Long - abstract val thumbWidth: Int - abstract val thumbHeight: Int + abstract val thumbWidth: Int? + abstract val thumbHeight: Int? } @Suppress("DuplicatedCode") @@ -131,7 +131,8 @@ internal class OnlineShortVideoImpl( ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { override fun toString(): String { - return "[mirai:svideo:$fileId, name=$fileName, md5=${fileMd5.toUHexString("")}]" + return "[mirai:svideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + + "thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } override fun contentToString(): String { @@ -185,8 +186,12 @@ internal class OfflineShortVideoImpl( override val thumbHeight: Int ) : OfflineShortVideo, AbstractShortVideoWithThumbnail() { + /** + * offline short video uses + */ override fun toString(): String { - return "[mirai:svideo:$fileId, md5=${fileMd5.toUHexString("")}]" + return "[mirai:svideo:${fileMd5.toUHexString("")}.$fileFormat, video=${fileMd5.toUHexString("")}, " + + "videoSize=${fileSize}, thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } override fun contentToString(): String { diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 36fc2a0a32..82072f36d6 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -50,7 +50,7 @@ internal class ShortVideoProtocol : MessageProtocol() { override suspend fun MessageEncoderContext.process(data: AbstractShortVideoWithThumbnail) { markAsConsumed() - collect(ImMsgBody.Elem(text = ImMsgBody.Text("你的QQ暂不支持查看视频短片,请期待后续版本。"))) + collect(ImMsgBody.Elem(text = ImMsgBody.Text("你的 QQ 暂不支持查看视频短片,请期待后续版本。"))) collect( ImMsgBody.Elem( videoFile = ImMsgBody.VideoFile( @@ -60,16 +60,16 @@ internal class ShortVideoProtocol : MessageProtocol() { fileFormat = 3, // mp4, fileTime = 10, fileSize = data.fileSize.toInt(), - thumbWidth = data.thumbWidth, - thumbHeight = data.thumbHeight, + thumbWidth = data.thumbWidth ?: 0, + thumbHeight = data.thumbHeight ?: 0, thumbFileMd5 = data.thumbMd5, thumbFileSize = data.thumbSize.toInt(), busiType = 0, fromChatType = -1, toChatType = -1, boolSupportProgressive = true, - fileWidth = data.thumbWidth, - fileHeight = data.thumbHeight + fileWidth = data.thumbWidth ?: 1280, + fileHeight = data.thumbHeight ?: 720 ) ) ) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt index 8ff176cc60..8c5818d54f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/shortvideo/PttCenterSvr.kt @@ -23,7 +23,6 @@ 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 -import net.mamoe.mirai.utils.toUHexString internal class PttCenterSvr { object GroupShortVideoUpReq : @@ -53,14 +52,15 @@ internal class PttCenterSvr { } } - // TODO: only support mp4 video now. operator fun invoke( client: QQAndroidClient, contact: Contact, thumbnailFileMd5: ByteArray, thumbnailFileSize: Long, + videoFileName: String, videoFileMd5: ByteArray, - videoFileSize: Long + videoFileSize: Long, + videoFileFormat: String ) = buildOutgoingUniPacket(client) { sequenceId -> writeProtoBuf( PttShortVideo.ReqBody.serializer(), @@ -72,8 +72,10 @@ internal class PttCenterSvr { contact, thumbnailFileMd5, thumbnailFileSize, + videoFileName, videoFileMd5, - videoFileSize + videoFileSize, + videoFileFormat ), msgExtensionReq = listOf( PttShortVideo.ExtensionReq( @@ -90,15 +92,17 @@ internal class PttCenterSvr { contact: Contact, thumbnailFileMd5: ByteArray, thumbnailFileSize: Long, + videoFileName: String, videoFileMd5: ByteArray, - videoFileSize: Long + videoFileSize: Long, + videoFileFormat: String ) = PttShortVideo.PttShortVideoUploadReq( fromuin = client.uin, touin = contact.uin, chatType = 1, // guild channel = 4, others = 1 clientType = 2, msgPttShortVideoFileInfo = PttShortVideo.PttShortVideoFileInfo( - fileName = videoFileMd5.toUHexString("") + ".mp4", + fileName = videoFileName + videoFileFormat, fileMd5 = videoFileMd5, fileSize = videoFileSize, fileResLength = 1280, From 1a19b23fd8bf430bffbae910cc232368ef3c856b Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 10:57:22 +0800 Subject: [PATCH 07/43] [core] fix mp4 file check --- mirai-core-utils/src/commonMain/kotlin/Files.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mirai-core-utils/src/commonMain/kotlin/Files.kt b/mirai-core-utils/src/commonMain/kotlin/Files.kt index 9f8990ce33..f51f0992af 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Files.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Files.kt @@ -30,8 +30,7 @@ private val FILE_TYPES: List = listOf( FileType("^424D", 3, "bmp"), FileType("^2321414D52", 5, "amr"), FileType("^02232153494C4B5F5633", 10, "silk"), - FileType("^FFD8FF", 3, "jpg"), - FileType("^([a-zA-Z]{8})66747970", 8, "jpg"), + FileType("^([a-zA-Z0-9]{8})66747970", 8, "mp4"), //"49492A00" to "tif", // client doesn't support //"52494646" to "webp", // pc client doesn't support From 53c29468c969f6d78a5d4f855363d4fe7ceb68b0 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 11:47:05 +0800 Subject: [PATCH 08/43] [core] extract fileName from `OnlineShortVideo` to `ShortVideo` --- .../src/commonMain/kotlin/contact/Contact.kt | 6 +++++- .../kotlin/message/data/ShortVideo.kt | 18 +++++++++--------- .../src/jvmBaseMain/kotlin/contact/Contact.kt | 7 ++++++- .../internal/contact/AbstractMockContact.kt | 6 +++++- .../kotlin/contact/AbstractContact.kt | 12 +++++++++--- .../kotlin/message/data/shortVideo.kt | 5 ++++- .../protocol/impl/ShortVideoProtocol.kt | 3 +-- 7 files changed, 39 insertions(+), 18 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index 1df539cdf4..8ed0b5db48 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -99,7 +99,11 @@ public expect interface Contact : ContactOrBot, CoroutineScope { * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 */ - public suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo + public suspend fun uploadShortVideo( + thumbnail: ExternalResource, + video: ExternalResource, + fileName: String? = null + ): ShortVideo @JvmBlockingBridge public companion object { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 3867bb96f3..232c5373fd 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -16,25 +16,30 @@ import net.mamoe.mirai.utils.safeCast public interface ShortVideo : MessageContent, ConstrainSingle { /** - * 文件 ID. + * 视频 ID. */ public val fileId: String /** - * 文件 MD5. 16 bytes. + * 视频文件 MD5. 16 bytes. */ public val fileMd5: ByteArray /* - * 文件大小 + * 视频大小 */ public val fileSize: Long /** - * 文件类型 + * 视频文件类型(拓展名) */ public val fileFormat: String + /* + * 视频文件名,不包括拓展名 + */ + public val fileName: String + @MiraiInternalApi override fun accept(visitor: MessageVisitor, data: D): R { @@ -53,11 +58,6 @@ public interface ShortVideo : MessageContent, ConstrainSingle { @NotStableForInheritance public interface OnlineShortVideo : ShortVideo { - /* - * 文件名 - */ - public val fileName: String - /** * 下载链接 */ diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt index 37d7fff4fb..de96956fcf 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -98,8 +98,13 @@ public actual interface Contact : ContactOrBot, CoroutineScope { * * @param thumbnail 短视频封面图,为图片资源 * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 + * @param fileName 期望上传的文件名,若为 `null` 则默认为视频资源的 [ExternalResource.md5]。 */ - public actual suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo + public actual suspend fun uploadShortVideo( + thumbnail: ExternalResource, + video: ExternalResource, + fileName: String? + ): ShortVideo public actual companion object { /** diff --git a/mirai-core-mock/src/internal/contact/AbstractMockContact.kt b/mirai-core-mock/src/internal/contact/AbstractMockContact.kt index ef1b40dc7e..8be6eff2a1 100644 --- a/mirai-core-mock/src/internal/contact/AbstractMockContact.kt +++ b/mirai-core-mock/src/internal/contact/AbstractMockContact.kt @@ -58,7 +58,11 @@ internal abstract class AbstractMockContact( return bot.uploadMockImage(resource) } - override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { + override suspend fun uploadShortVideo( + thumbnail: ExternalResource, + video: ExternalResource, + fileName: String? + ): ShortVideo { TODO("mock upload short video") } diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index 16779065da..cfc18ac1d1 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -38,7 +38,11 @@ internal abstract class AbstractContact( ) : Contact { final override val coroutineContext: CoroutineContext = parentCoroutineContext.childScopeContext() - override suspend fun uploadShortVideo(thumbnail: ExternalResource, video: ExternalResource): ShortVideo { + override suspend fun uploadShortVideo( + thumbnail: ExternalResource, + video: ExternalResource, + fileName: String? + ): ShortVideo { if (this !is Group && this !is Friend) { throw UnsupportedOperationException("short video can only upload to friend or group.") } @@ -51,8 +55,8 @@ internal abstract class AbstractContact( throw EventCancelledException("cancelled by BeforeShortVideoUploadEvent") } - // local uploaded offline short video uses video file md5 as its file name - val videoName = video.md5.toUHexString("") + // local uploaded offline short video uses video file md5 as its file name by default + val videoName = fileName ?: video.md5.toUHexString("") val uploadResp = bot.network.sendAndExpect( PttCenterSvr.GroupShortVideoUpReq( @@ -74,6 +78,7 @@ internal abstract class AbstractContact( if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { return OfflineShortVideoImpl( uploadResp.fileId, + videoName, video.md5, video.size, video.formatName, @@ -133,6 +138,7 @@ internal abstract class AbstractContact( return OfflineShortVideoImpl( highwayUploadResp.fileid, + videoName, video.md5, video.size, video.formatName, diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 9ec22481b5..99177bee54 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -177,6 +177,7 @@ internal class OnlineShortVideoImpl( @Serializable internal class OfflineShortVideoImpl( override val fileId: String, + override val fileName: String, override val fileMd5: ByteArray, override val fileSize: Long, override val fileFormat: String, @@ -190,7 +191,7 @@ internal class OfflineShortVideoImpl( * offline short video uses */ override fun toString(): String { - return "[mirai:svideo:${fileMd5.toUHexString("")}.$fileFormat, video=${fileMd5.toUHexString("")}, " + + return "[mirai:svideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, " + "videoSize=${fileSize}, thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } @@ -205,6 +206,7 @@ internal class OfflineShortVideoImpl( other as OfflineShortVideoImpl if (fileId != other.fileId) return false + if (fileName != other.fileName) return false if (!fileMd5.contentEquals(other.fileMd5)) return false if (fileSize != other.fileSize) return false if (fileFormat != other.fileFormat) return false @@ -218,6 +220,7 @@ internal class OfflineShortVideoImpl( override fun hashCode(): Int { var result = fileId.hashCode() + result = 31 * result + fileName.hashCode() result = 31 * result + fileMd5.contentHashCode() result = 31 * result + fileSize.hashCode() result = 31 * result + fileFormat.hashCode() diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 82072f36d6..9219259146 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -24,7 +24,6 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.message.data.MessageContent import net.mamoe.mirai.message.data.ShortVideo import net.mamoe.mirai.message.data.SingleMessage -import net.mamoe.mirai.utils.toUHexString internal class ShortVideoProtocol : MessageProtocol() { override fun ProcessorCollector.collectProcessorsImpl() { @@ -56,7 +55,7 @@ internal class ShortVideoProtocol : MessageProtocol() { videoFile = ImMsgBody.VideoFile( fileUuid = data.fileId.encodeToByteArray(), fileMd5 = data.fileMd5, - fileName = (data.fileMd5.toUHexString("") + ".mp4").encodeToByteArray(), + fileName = data.fileName.encodeToByteArray(), fileFormat = 3, // mp4, fileTime = 10, fileSize = data.fileSize.toInt(), From f21eb65a567a2d85c10fa5c6dfe700ee359f9d74 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 13:25:27 +0800 Subject: [PATCH 09/43] [core] ShortVideo.Builder --- .../src/commonMain/kotlin/contact/Contact.kt | 1 + .../event/events/ShortVideoUploadEvent.kt | 3 +- .../kotlin/message/data/ShortVideo.kt | 136 +++++++++++++++++- .../kotlin/message/data/shortVideo.kt | 21 ++- .../image/InternalShortVideoProtocolImpl.kt | 38 +++++ .../protocol/impl/ShortVideoProtocol.kt | 16 ++- ...ai.message.data.InternalShortVideoProtocol | 10 ++ 7 files changed, 206 insertions(+), 19 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt create mode 100644 mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.message.data.InternalShortVideoProtocol diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index 8ed0b5db48..a0ad071f70 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -97,6 +97,7 @@ public expect interface Contact : ContactOrBot, CoroutineScope { * * @param thumbnail 短视频封面图,为图片资源 * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 + * @param fileName 期望上传的文件名,若为 `null` 则默认为视频资源的 [ExternalResource.md5]. */ public suspend fun uploadShortVideo( diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt index 8786bdfb01..d3224af772 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt @@ -14,7 +14,6 @@ package net.mamoe.mirai.event.events import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact -import net.mamoe.mirai.contact.Contact.Companion.uploadImage import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.event.CancellableEvent import net.mamoe.mirai.event.events.ShortVideoUploadEvent.Failed @@ -49,7 +48,7 @@ public data class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( * 此事件总是在 [BeforeImageUploadEvent] 之后广播. * 若 [BeforeImageUploadEvent] 被取消, 此事件不会广播. * - * @see Contact.uploadImage 上传图片. 为广播这个事件的唯一途径 + * @see Contact.uploadShortVideo 上传短视频. 为广播这个事件的唯一途径 * * @see Succeed * @see Failed diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 232c5373fd..9bf7d85a1b 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -9,16 +9,48 @@ package net.mamoe.mirai.message.data +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.message.code.CodableMessage +import net.mamoe.mirai.message.data.ShortVideo.Builder 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 +import net.mamoe.mirai.utils.* -public interface ShortVideo : MessageContent, ConstrainSingle { +/** + * 短视频消息,指的是可在聊天界面在线播放的视频消息,而非在群文件上传的视频文件. + * + * 短视频消息分为 [OnlineShortVideo] 与 [OfflineShortVideo]. 在本地上传的短视频为 [OfflineShortVideo]. 从服务器接收的短视频为 [OnlineShortVideo]. + * + * 最推荐存储的方式是下载视频文件,每次都通过上传该文件获取视频消息. + * 在上传视频时服务器会根据缓存情况选择回复已有视频 ID 或要求客户端上传. + * + * # 获取短视频消息示例 + * + * ## 上传短视频 + * 使用 [Contact.uploadShortVideo],将视频缩略图和视频[资源][ExternalResource] 上传以得到 [OfflineShortVideo]. + * + * ## 使用 [Builder] 构建短视频 + * [ShortVideo] 提供 [Builder] 构建方式,必须指定 [videoId], [fileName], [fileMd5], [fileSize] 和 [fileFormat] 参数. + * 可选指定 [Builder.thumbnailMd5] 和 [Builder.thumbnailSize]. + * + * ## 从服务器接收 + * 通过监听消息接收的短视频消息可直接转换为 [OnlineShortVideo]. + * + * # 下载视频 + * 通过 [OnlineShortVideo.urlForDownload] 获取下载链接. + * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [fileName] 作为文件名,[fileFormat] 作为文件拓展名. + * + * # 其他信息 + * + * ## mirai 码支持 + * 格式: [mirai:svideo:[videoId],[fileName].[fileFormat],[fileMd5],[fileSize],`thumbnailMd5`,`thumbnailSize`] + * + * `thumbnailMd5` 和 `thumbnailSize` 是可选项. 若不提供,可能会影响服务器判断缓存. + */ +public interface ShortVideo : MessageContent, ConstrainSingle, CodableMessage { /** * 视频 ID. */ - public val fileId: String + public val videoId: String /** * 视频文件 MD5. 16 bytes. @@ -54,6 +86,73 @@ public interface ShortVideo : MessageContent, ConstrainSingle { AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) { } + + public class Builder internal constructor( + public var videoId: String + ) { + public constructor() : this("") + + /** + * @see ShortVideo.fileMd5 + */ + public var fileMd5: ByteArray = EMPTY_BYTE_ARRAY + + /** + * @see ShortVideo.fileSize + */ + public var fileSize: Long = 0 + + /** + * @see ShortVideo.fileFormat + */ + public var fileFormat: String = "" + + /** + * @see ShortVideo.fileName + */ + public var fileName: String = "" + + /** + * 缩略图文件 MD5,仅通过 [Contact.uploadShortVideo] 获取的 [OfflineShortVideo] 或 [OnlineShortVideo] 的 mirai 码获取. + */ + public var thumbnailMd5: ByteArray = EMPTY_BYTE_ARRAY + + /** + * 缩略图文件大小,仅通过 [Contact.uploadShortVideo] 获取的 [OfflineShortVideo] 或 [OnlineShortVideo] 的 mirai 码获取. + */ + public var thumbnailSize: Long = 0 + + public fun build(): ShortVideo { + if (videoId.isEmpty()) { + throw IllegalArgumentException("videoId is empty.") + } + if (fileMd5.contentEquals(EMPTY_BYTE_ARRAY)) { + throw IllegalArgumentException("fileMd5 is empty.") + } + if (fileSize == 0L) { + throw IllegalArgumentException("fileSize is zero.") + } + if (fileFormat.isEmpty()) { + throw IllegalArgumentException("fileFormat is empty.") + } + if (fileName.isEmpty()) { + throw IllegalArgumentException("fileName is empty.") + } + + @OptIn(MiraiInternalApi::class) + return InternalShortVideoProtocol.instance.createOfflineShortVideo( + videoId, fileMd5, fileSize, fileFormat, fileName, thumbnailMd5, thumbnailSize + ) + } + + public companion object { + /** + * 创建一个 [Builder] + */ + @JvmStatic + public fun newBuilder(videoId: String): Builder = Builder(videoId) + } + } } @NotStableForInheritance @@ -76,4 +175,31 @@ public interface OfflineShortVideo : ShortVideo { AbstractPolymorphicMessageKey(ShortVideo, { it.safeCast() }) { public const val SERIAL_NAME: String = "OfflineShortVideo" } +} + +/** + * 内部短视频协议实现 + * @since 2.16.0 + */ +@MiraiInternalApi +public interface InternalShortVideoProtocol { + public fun createOfflineShortVideo( + videoId: String, + fileMd5: ByteArray, + fileSize: Long, + fileFormat: String, + fileName: String, + thumbnailMd5: ByteArray, + thumbnailSize: Long + ): OfflineShortVideo + + @MiraiInternalApi + public companion object { + public val instance: InternalShortVideoProtocol by lazy { + loadService( + InternalShortVideoProtocol::class, + "net.mamoe.mirai.internal.message.InternalShortVideoProtocolImpl" + ) + } + } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 99177bee54..9c9de8a605 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -118,7 +118,7 @@ internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { @SerialName(OnlineShortVideo.SERIAL_NAME) @Serializable internal class OnlineShortVideoImpl( - override val fileId: String, + override val videoId: String, override val fileMd5: ByteArray, override val fileName: String, override val fileSize: Long, @@ -139,13 +139,17 @@ internal class OnlineShortVideoImpl( return "[视频]" } + override fun appendMiraiCodeTo(builder: StringBuilder) { + TODO() + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as OnlineShortVideoImpl - if (fileId != other.fileId) return false + if (videoId != other.videoId) return false if (!fileMd5.contentEquals(other.fileMd5)) return false if (fileName != other.fileName) return false if (fileSize != other.fileSize) return false @@ -160,7 +164,7 @@ internal class OnlineShortVideoImpl( } override fun hashCode(): Int { - var result = fileId.hashCode() + var result = videoId.hashCode() result = 31 * result + fileMd5.contentHashCode() result = 31 * result + fileName.hashCode() result = 31 * result + fileSize.hashCode() @@ -176,7 +180,7 @@ internal class OnlineShortVideoImpl( @Serializable internal class OfflineShortVideoImpl( - override val fileId: String, + override val videoId: String, override val fileName: String, override val fileMd5: ByteArray, override val fileSize: Long, @@ -199,13 +203,18 @@ internal class OfflineShortVideoImpl( return "[视频]" } + @Suppress("DuplicatedCode") + override fun appendMiraiCodeTo(builder: StringBuilder) { + TODO() + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as OfflineShortVideoImpl - if (fileId != other.fileId) return false + if (videoId != other.videoId) return false if (fileName != other.fileName) return false if (!fileMd5.contentEquals(other.fileMd5)) return false if (fileSize != other.fileSize) return false @@ -219,7 +228,7 @@ internal class OfflineShortVideoImpl( } override fun hashCode(): Int { - var result = fileId.hashCode() + var result = videoId.hashCode() result = 31 * result + fileName.hashCode() result = 31 * result + fileMd5.contentHashCode() result = 31 * result + fileSize.hashCode() diff --git a/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt b/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt new file mode 100644 index 0000000000..9c1cf886c6 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt @@ -0,0 +1,38 @@ +/* + * 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.image + +import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.message.data.InternalShortVideoProtocol +import net.mamoe.mirai.message.data.OfflineShortVideo + +internal class InternalShortVideoProtocolImpl : InternalShortVideoProtocol { + override fun createOfflineShortVideo( + videoId: String, + fileMd5: ByteArray, + fileSize: Long, + fileFormat: String, + fileName: String, + thumbnailMd5: ByteArray, + thumbnailSize: Long + ): OfflineShortVideo { + return OfflineShortVideoImpl( + videoId, + fileName, + fileMd5, + fileSize, + fileFormat, + thumbnailMd5, + thumbnailSize, + 0, + 0 + ) + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 9219259146..a538c0157a 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -50,25 +50,29 @@ internal class ShortVideoProtocol : MessageProtocol() { markAsConsumed() collect(ImMsgBody.Elem(text = ImMsgBody.Text("你的 QQ 暂不支持查看视频短片,请期待后续版本。"))) + + val thumbWidth = if (data.thumbWidth == null || data.thumbWidth == 0) 1280 else data.thumbWidth!! + val thumbHeight = if (data.thumbHeight == null || data.thumbHeight == 0) 720 else data.thumbHeight!! + collect( ImMsgBody.Elem( videoFile = ImMsgBody.VideoFile( - fileUuid = data.fileId.encodeToByteArray(), + fileUuid = data.videoId.encodeToByteArray(), fileMd5 = data.fileMd5, fileName = data.fileName.encodeToByteArray(), - fileFormat = 3, // mp4, + fileFormat = 3, // TODO: only support mp4 now fileTime = 10, fileSize = data.fileSize.toInt(), - thumbWidth = data.thumbWidth ?: 0, - thumbHeight = data.thumbHeight ?: 0, + thumbWidth = thumbWidth, + thumbHeight = thumbHeight, thumbFileMd5 = data.thumbMd5, thumbFileSize = data.thumbSize.toInt(), busiType = 0, fromChatType = -1, toChatType = -1, boolSupportProgressive = true, - fileWidth = data.thumbWidth ?: 1280, - fileHeight = data.thumbHeight ?: 720 + fileWidth = thumbWidth, + fileHeight = thumbHeight ) ) ) diff --git a/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.message.data.InternalShortVideoProtocol b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.message.data.InternalShortVideoProtocol new file mode 100644 index 0000000000..82ea60f231 --- /dev/null +++ b/mirai-core/src/commonMain/resources/META-INF/services/net.mamoe.mirai.message.data.InternalShortVideoProtocol @@ -0,0 +1,10 @@ +# +# Copyright 2019-2022 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 +# + +net.mamoe.mirai.internal.message.image.InternalShortVideoProtocolImpl \ No newline at end of file From a26e1f1a67061b4cf09ab76b5f76cb72ee9e7d4e Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 13:52:15 +0800 Subject: [PATCH 10/43] [core] mirai code support for `ShortVideo` --- .../kotlin/message/code/internal/impl.kt | 19 +++++++++++ .../kotlin/message/data/shortVideo.kt | 32 +++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt index 862d5348b6..df13111533 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt @@ -15,6 +15,7 @@ import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.hexToBytes internal fun String.parseMiraiCodeImpl(contact: Contact?): MessageChain = buildMessageChain { @@ -53,6 +54,7 @@ private fun String.forEachMiraiCode(block: (origin: String, name: String?, args: '\\' -> { pos += 2 } + '[' -> { if (get(pos + 1) == 'm' && get(pos + 2) == 'i' && get(pos + 3) == 'r' && get(pos + 4) == 'a' && @@ -83,6 +85,7 @@ private fun String.forEachMiraiCode(block: (origin: String, name: String?, args: } } else pos++ } + else -> { pos++ } @@ -139,6 +142,20 @@ private object MiraiCodeParsers : AbstractMap(), Map FileMessage(id, internalId.toInt(), name, size.toLong()) }, + "svideo" to MiraiCodeParser( + Regex("""(.*),(.+\..{2,4}),([a-zA-Z0-9]{32}),(\d+)(,([a-zA-Z0-9]{32}),(\d+))?""") + ) { (videoId, fullFileName, fileMd5, fileSize, _, thumbnailMd5, thumbnailSize) -> + val fileFormat = fullFileName.substringAfterLast('.') + val fileName = fullFileName.substringBeforeLast(".${fileFormat}") + ShortVideo.Builder.newBuilder(videoId).apply { + this.fileMd5 = fileMd5.hexToBytes() + this.fileName = fileName + this.fileFormat = fileFormat + this.fileSize = fileSize.toLong() + if (thumbnailMd5.isNotEmpty()) this.thumbnailMd5 = thumbnailMd5.hexToBytes() + if (thumbnailSize.isNotEmpty()) this.thumbnailSize = thumbnailSize.toLong() + }.build() + }, ) @@ -178,6 +195,7 @@ internal sealed class MiraiCodeParser { pos++ begin = pos } + else -> pos++ } } @@ -210,6 +228,7 @@ internal fun StringBuilder.appendStringAsMiraiCode(value: String): StringBuilder ':', ',', '\\', -> append("\\").append(char) + '\n' -> append("\\n") '\r' -> append("\\r") else -> append(char) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 9c9de8a605..b1a03b4aa7 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -140,7 +140,21 @@ internal class OnlineShortVideoImpl( } override fun appendMiraiCodeTo(builder: StringBuilder) { - TODO() + builder.append("[mirai:svideo:") + builder.append(videoId) + builder.append(",") + builder.append(fileName) + builder.append(".") + builder.append(fileFormat) + builder.append(",") + builder.append(fileMd5.toUHexString("")) + builder.append(",") + builder.append(fileSize) + builder.append(",") + builder.append(thumbMd5.toUHexString("")) + builder.append(",") + builder.append(thumbSize) + builder.append("]") } override fun equals(other: Any?): Boolean { @@ -205,7 +219,21 @@ internal class OfflineShortVideoImpl( @Suppress("DuplicatedCode") override fun appendMiraiCodeTo(builder: StringBuilder) { - TODO() + builder.append("[mirai:svideo:") + builder.append(videoId) + builder.append(",") + builder.append(fileName) + builder.append(".") + builder.append(fileFormat) + builder.append(",") + builder.append(fileMd5.toUHexString("")) + builder.append(",") + builder.append(fileSize) + builder.append(",") + builder.append(thumbMd5.toUHexString("")) + builder.append(",") + builder.append(thumbSize) + builder.append("]") } override fun equals(other: Any?): Boolean { From 3629e7f8b6c0efc58062486485c7ca310148410b Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 17:38:59 +0800 Subject: [PATCH 11/43] [core] add doc for OnlineShortVideo and OfflineShortVideo --- .../src/commonMain/kotlin/contact/Contact.kt | 2 + .../kotlin/message/data/ShortVideo.kt | 46 +++++++++++++++++++ .../kotlin/utils/ExternalResource.kt | 3 ++ .../src/commonMain/kotlin/Files.kt | 3 ++ .../kotlin/message/data/shortVideo.kt | 9 ++-- .../kotlin/network/highway/Highway.kt | 1 + 6 files changed, 60 insertions(+), 4 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index a0ad071f70..25d09f2bc1 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -25,6 +25,8 @@ import net.mamoe.mirai.recallMessage import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage import kotlin.coroutines.cancellation.CancellationException +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 9bf7d85a1b..314330095a 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -9,8 +9,12 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.KSerializer +import net.mamoe.mirai.contact.AudioSupported import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.code.CodableMessage +import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString import net.mamoe.mirai.message.data.ShortVideo.Builder import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* @@ -45,6 +49,8 @@ import net.mamoe.mirai.utils.* * 格式: [mirai:svideo:[videoId],[fileName].[fileFormat],[fileMd5],[fileSize],`thumbnailMd5`,`thumbnailSize`] * * `thumbnailMd5` 和 `thumbnailSize` 是可选项. 若不提供,可能会影响服务器判断缓存. + * + * @since 2.16 */ public interface ShortVideo : MessageContent, ConstrainSingle, CodableMessage { /** @@ -155,6 +161,20 @@ public interface ShortVideo : MessageContent, ConstrainSingle, CodableMessage { } } +/** + * 在线短视频消息,即从消息事件中接收到的视频消息. + * + * [OnlineShortVideo] 仅可以从事件中的[消息链][MessageChain]接收, 不可手动构造. 若需要手动构造, 请使用 [ShortVideo.Builder] 构造 [离线短视频][OfflineShortVideo]. + * + * ### 序列化支持 + * + * [OnlineShortVideo] 支持序列化. 可使用 [MessageChain.serializeToJsonString] 以及 [MessageChain.deserializeFromJsonString]. + * 也可以在 [MessageSerializers.serializersModule] 获取到 [OnlineShortVideo] 的 [KSerializer]. + * + * 要获取更多有关序列化的信息, 参阅 [MessageSerializers]. + * + * @since 2.16 + */ @NotStableForInheritance public interface OnlineShortVideo : ShortVideo { /** @@ -168,6 +188,32 @@ public interface OnlineShortVideo : ShortVideo { } } +/** + * 离线短视频消息. + * + * [OfflineShortVideo] 拥有协议上必要的五个属性: + * - 视频 ID [videoId] + * - 视频文件名 [fileName] + * - 视频 MD5 [fileMd5] + * - 视频大小 [fileSize] + * - 视频格式 [fileFormat] + * + * 和非必要属性: + * - 缩略图 MD5 `thumbnailMd5` + * - 缩略图大小 `thumbnailSize` + * + * [OfflineShortVideo] 可由本地 [ExternalResource] 经过 [AudioSupported.uploadShortVideo] 上传到服务器得到, 故无[下载链接][OnlineShortVideo.urlForDownload]. + * + * [OfflineShortVideo] 支持使用 [ShortVideo.Builder] 可通过上述七个参数获得 [OfflineShortVideo] 实例. + * + * ### 序列化支持 + * + * [OfflineShortVideo] 支持序列化. 可使用 [MessageChain.serializeToJsonString] 以及 [MessageChain.deserializeFromJsonString]. + * 也可以在 [MessageSerializers.serializersModule] 获取到 [OfflineShortVideo] 的 [KSerializer]. + * + * 要获取更多有关序列化的信息, 参阅 [MessageSerializers]. + * @since 2.16 + */ @NotStableForInheritance public interface OfflineShortVideo : ShortVideo { diff --git a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt index 08040b7178..0dc309d13e 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt @@ -26,6 +26,9 @@ import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic /** diff --git a/mirai-core-utils/src/commonMain/kotlin/Files.kt b/mirai-core-utils/src/commonMain/kotlin/Files.kt index f51f0992af..59cc1f1072 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Files.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Files.kt @@ -12,6 +12,9 @@ package net.mamoe.mirai.utils +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName + private class FileType( signature: String, val requiredHeaderSize: Int, diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index b1a03b4aa7..b72acaaec1 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.message.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.User @@ -126,8 +127,8 @@ internal class OnlineShortVideoImpl( override val urlForDownload: String, override val thumbMd5: ByteArray, override val thumbSize: Long, - override val thumbWidth: Int, - override val thumbHeight: Int + @Transient override val thumbWidth: Int = 0, + @Transient override val thumbHeight: Int = 0 ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { override fun toString(): String { @@ -201,8 +202,8 @@ internal class OfflineShortVideoImpl( override val fileFormat: String, override val thumbMd5: ByteArray, override val thumbSize: Long, - override val thumbWidth: Int, - override val thumbHeight: Int + @Transient override val thumbWidth: Int = 0, + @Transient override val thumbHeight: Int = 0 ) : OfflineShortVideo, AbstractShortVideoWithThumbnail() { /** diff --git a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt index 93d52fd157..44dc622162 100644 --- a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt +++ b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt @@ -32,6 +32,7 @@ import net.mamoe.mirai.internal.utils.sizeToString import net.mamoe.mirai.utils.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.Volatile import kotlin.math.roundToInt internal object Highway { From 338585521722f7a0751edf017a60b6b7f980d577 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 17:44:06 +0800 Subject: [PATCH 12/43] [core] fix text --- .../kotlin/message/protocol/MessageProtocolFacadeTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt index 42ca9dad18..48efd0aad9 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt @@ -32,7 +32,7 @@ internal class MessageProtocolFacadeTest : AbstractTest() { PokeMessageProtocol PttMessageProtocol RichMessageProtocol - ShortVideoMessageProtocol + ShortVideoProtocol TextProtocol VipFaceProtocol ForwardMessageProtocol From c8a6b36ab5346d2c989f496d1e71d41977054370 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 2 Aug 2023 21:37:17 +0800 Subject: [PATCH 13/43] dump api --- .../android/api/android.api | 116 ++++++++++++++++++ .../compatibility-validation/jvm/api/jvm.api | 100 ++++++++++++++- 2 files changed, 214 insertions(+), 2 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 790e1c3386..421774aeef 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -366,6 +366,9 @@ public abstract interface class net/mamoe/mirai/contact/Contact : kotlinx/corout public static fun uploadImage (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image; public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo; + public abstract fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class net/mamoe/mirai/contact/Contact$Companion { @@ -1888,6 +1891,21 @@ public final class net/mamoe/mirai/event/events/BeforeImageUploadEvent : net/mam public fun toString ()Ljava/lang/String; } +public final class net/mamoe/mirai/event/events/BeforeShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/CancellableEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; + public fun equals (Ljava/lang/Object;)Z + public fun getBot ()Lnet/mamoe/mirai/Bot; + public final fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public final fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public abstract interface class net/mamoe/mirai/event/events/BotActiveEvent : net/mamoe/mirai/event/events/BotEvent { } @@ -2948,6 +2966,47 @@ public final class net/mamoe/mirai/event/events/OtherClientOnlineEvent : net/mam public fun toString ()Ljava/lang/String; } +public abstract class net/mamoe/mirai/event/events/ShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { + public fun getBot ()Lnet/mamoe/mirai/Bot; + public abstract fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public abstract fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public abstract fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; +} + +public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component4 ()I + public final fun component5 ()Ljava/lang/String; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; + public fun equals (Ljava/lang/Object;)Z + public final fun getErrno ()I + public final fun getMessage ()Ljava/lang/String; + public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component4 ()Lnet/mamoe/mirai/message/data/ShortVideo; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; + public fun equals (Ljava/lang/Object;)Z + public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun getVideo ()Lnet/mamoe/mirai/message/data/ShortVideo; + public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class net/mamoe/mirai/event/events/SignEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet { public fun getBot ()Lnet/mamoe/mirai/Bot; public final fun getRank ()Ljava/lang/Integer; @@ -4830,6 +4889,15 @@ public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/ma public final class net/mamoe/mirai/message/data/OfflineMessageSource$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/OfflineShortVideo : net/mamoe/mirai/message/data/ShortVideo { + public static final field Key Lnet/mamoe/mirai/message/data/OfflineShortVideo$Key; + public static final field SERIAL_NAME Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { + public static final field SERIAL_NAME Ljava/lang/String; +} + public abstract interface class net/mamoe/mirai/message/data/OnlineAudio : net/mamoe/mirai/message/data/Audio { public static final field Key Lnet/mamoe/mirai/message/data/OnlineAudio$Key; public static final field SERIAL_NAME Ljava/lang/String; @@ -4978,6 +5046,16 @@ public abstract class net/mamoe/mirai/message/data/OnlineMessageSource$Outgoing$ public final class net/mamoe/mirai/message/data/OnlineMessageSource$Outgoing$ToTemp$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/OnlineShortVideo : net/mamoe/mirai/message/data/ShortVideo { + public static final field Key Lnet/mamoe/mirai/message/data/OnlineShortVideo$Key; + public static final field SERIAL_NAME Ljava/lang/String; + public abstract fun getUrlForDownload ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/OnlineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { + public static final field SERIAL_NAME Ljava/lang/String; +} + public final class net/mamoe/mirai/message/data/OrNullDelegate { public static final synthetic fun box-impl (Ljava/lang/Object;)Lnet/mamoe/mirai/message/data/OrNullDelegate; public static fun constructor-impl (Ljava/lang/Object;)Ljava/lang/Object; @@ -5223,6 +5301,44 @@ public abstract interface class net/mamoe/mirai/message/data/ServiceMessage : ne public final class net/mamoe/mirai/message/data/ServiceMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { + public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; + public abstract fun getFileFormat ()Ljava/lang/String; + public abstract fun getFileMd5 ()[B + public abstract fun getFileName ()Ljava/lang/String; + public abstract fun getFileSize ()J + public fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; + public abstract fun getVideoId ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Builder { + public static final field Companion Lnet/mamoe/mirai/message/data/ShortVideo$Builder$Companion; + public fun ()V + public final fun build ()Lnet/mamoe/mirai/message/data/ShortVideo; + public final fun getFileFormat ()Ljava/lang/String; + public final fun getFileMd5 ()[B + public final fun getFileName ()Ljava/lang/String; + public final fun getFileSize ()J + public final fun getThumbnailMd5 ()[B + public final fun getThumbnailSize ()J + public final fun getVideoId ()Ljava/lang/String; + public static final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; + public final fun setFileFormat (Ljava/lang/String;)V + public final fun setFileMd5 ([B)V + public final fun setFileName (Ljava/lang/String;)V + public final fun setFileSize (J)V + public final fun setThumbnailMd5 ([B)V + public final fun setThumbnailSize (J)V + public final fun setVideoId (Ljava/lang/String;)V +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Builder$Companion { + public final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { +} + public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { public static final field INSTANCE Lnet/mamoe/mirai/message/data/ShowImageFlag; public static final field SERIAL_NAME Ljava/lang/String; diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 2e69105e46..8c3414bf25 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -366,6 +366,9 @@ public abstract interface class net/mamoe/mirai/contact/Contact : kotlinx/corout public static fun uploadImage (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/message/data/Image; public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo; + public abstract fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class net/mamoe/mirai/contact/Contact$Companion { @@ -1888,6 +1891,21 @@ public final class net/mamoe/mirai/event/events/BeforeImageUploadEvent : net/mam public fun toString ()Ljava/lang/String; } +public final class net/mamoe/mirai/event/events/BeforeShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/CancellableEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; + public fun equals (Ljava/lang/Object;)Z + public fun getBot ()Lnet/mamoe/mirai/Bot; + public final fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public final fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public abstract interface class net/mamoe/mirai/event/events/BotActiveEvent : net/mamoe/mirai/event/events/BotEvent { } @@ -2948,6 +2966,47 @@ public final class net/mamoe/mirai/event/events/OtherClientOnlineEvent : net/mam public fun toString ()Ljava/lang/String; } +public abstract class net/mamoe/mirai/event/events/ShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { + public fun getBot ()Lnet/mamoe/mirai/Bot; + public abstract fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public abstract fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public abstract fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; +} + +public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component4 ()I + public final fun component5 ()Ljava/lang/String; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; + public fun equals (Ljava/lang/Object;)Z + public final fun getErrno ()I + public final fun getMessage ()Ljava/lang/String; + public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { + public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; + public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun component4 ()Lnet/mamoe/mirai/message/data/ShortVideo; + public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; + public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; + public fun equals (Ljava/lang/Object;)Z + public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; + public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public final fun getVideo ()Lnet/mamoe/mirai/message/data/ShortVideo; + public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + public final class net/mamoe/mirai/event/events/SignEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet { public fun getBot ()Lnet/mamoe/mirai/Bot; public final fun getRank ()Ljava/lang/Integer; @@ -4830,6 +4889,15 @@ public abstract class net/mamoe/mirai/message/data/OfflineMessageSource : net/ma public final class net/mamoe/mirai/message/data/OfflineMessageSource$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } +public abstract interface class net/mamoe/mirai/message/data/OfflineShortVideo : net/mamoe/mirai/message/data/ShortVideo { + public static final field Key Lnet/mamoe/mirai/message/data/OfflineShortVideo$Key; + public static final field SERIAL_NAME Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { + public static final field SERIAL_NAME Ljava/lang/String; +} + public abstract interface class net/mamoe/mirai/message/data/OnlineAudio : net/mamoe/mirai/message/data/Audio { public static final field Key Lnet/mamoe/mirai/message/data/OnlineAudio$Key; public static final field SERIAL_NAME Ljava/lang/String; @@ -5233,11 +5301,39 @@ public abstract interface class net/mamoe/mirai/message/data/ServiceMessage : ne public final class net/mamoe/mirai/message/data/ServiceMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } -public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { +public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; - public abstract fun getFileId ()Ljava/lang/String; + public abstract fun getFileFormat ()Ljava/lang/String; public abstract fun getFileMd5 ()[B + public abstract fun getFileName ()Ljava/lang/String; + public abstract fun getFileSize ()J public fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; + public abstract fun getVideoId ()Ljava/lang/String; +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Builder { + public static final field Companion Lnet/mamoe/mirai/message/data/ShortVideo$Builder$Companion; + public fun ()V + public final fun build ()Lnet/mamoe/mirai/message/data/ShortVideo; + public final fun getFileFormat ()Ljava/lang/String; + public final fun getFileMd5 ()[B + public final fun getFileName ()Ljava/lang/String; + public final fun getFileSize ()J + public final fun getThumbnailMd5 ()[B + public final fun getThumbnailSize ()J + public final fun getVideoId ()Ljava/lang/String; + public static final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; + public final fun setFileFormat (Ljava/lang/String;)V + public final fun setFileMd5 ([B)V + public final fun setFileName (Ljava/lang/String;)V + public final fun setFileSize (J)V + public final fun setThumbnailMd5 ([B)V + public final fun setThumbnailSize (J)V + public final fun setVideoId (Ljava/lang/String;)V +} + +public final class net/mamoe/mirai/message/data/ShortVideo$Builder$Companion { + public final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; } public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { From c38f5ad7c1582a64e0ea64758d8cdcbb8c4f031f Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Thu, 3 Aug 2023 22:22:41 +0800 Subject: [PATCH 14/43] =?UTF-8?q?update=20`Contact.uploadShortVideo`=C2=B7?= =?UTF-8?q?`=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/commonMain/kotlin/contact/Contact.kt | 19 ++++++++----------- .../src/jvmBaseMain/kotlin/contact/Contact.kt | 19 +++++++++---------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index 25d09f2bc1..92e3286325 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -72,8 +72,6 @@ public expect interface Contact : ContactOrBot, CoroutineScope { /** * 上传一个 [资源][ExternalResource] 作为图片以备发送. * - * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** - * * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. * * @see Image 查看有关图片的更多信息, 如上传图片 @@ -89,19 +87,18 @@ public expect interface Contact : ContactOrBot, CoroutineScope { public suspend fun uploadImage(resource: ExternalResource): Image /** - * 上传 [资源][ExternalResource] 作为短视频发送。 - * - * 同时需要上传缩略图作为视频消息显示的封面。 + * 上传 [资源][ExternalResource] 作为短视频发送. + * 同时需要上传缩略图作为视频消息显示的封面. * - * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * @see ShortVideo 查看有关短视频的更多信息 * - * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. + * @see BeforeShortVideoUploadEvent 短视频发送前事件,可通过中断来拦截视频上传. + * @see ShortVideoUploadEvent 短视频上传完成事件,不可拦截. * - * @param thumbnail 短视频封面图,为图片资源 - * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 - * @param fileName 期望上传的文件名,若为 `null` 则默认为视频资源的 [ExternalResource.md5]. + * @param thumbnail 短视频封面图,为图片资源. + * @param video 视频资源,目前仅支持上传 mp4 格式的视频. + * @param fileName 文件名,如为 `null` 这根据 [video] 自动生成. */ - public suspend fun uploadShortVideo( thumbnail: ExternalResource, video: ExternalResource, diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt index de96956fcf..9088ac6b1c 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -25,6 +25,7 @@ import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.OverFileSizeMaxException import java.io.File import java.io.InputStream +import net.mamoe.mirai.event.Event /** * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. @@ -71,8 +72,6 @@ public actual interface Contact : ContactOrBot, CoroutineScope { /** * 上传一个 [资源][ExternalResource] 作为图片以备发送. * - * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** - * * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. * * @see Image 查看有关图片的更多信息, 如上传图片 @@ -88,17 +87,17 @@ public actual interface Contact : ContactOrBot, CoroutineScope { public actual suspend fun uploadImage(resource: ExternalResource): Image /** - * 上传 [资源][ExternalResource] 作为短视频发送。 - * - * 同时需要上传缩略图作为视频消息显示的封面。 + * 上传 [资源][ExternalResource] 作为短视频发送. + * 同时需要上传缩略图作为视频消息显示的封面. * - * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * @see ShortVideo 查看有关短视频的更多信息 * - * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. + * @see BeforeShortVideoUploadEvent 短视频发送前事件,可通过中断来拦截视频上传. + * @see ShortVideoUploadEvent 短视频上传完成事件,不可拦截. * - * @param thumbnail 短视频封面图,为图片资源 - * @param video 视频资源,目前仅支持上传 mp4 格式的视频。 - * @param fileName 期望上传的文件名,若为 `null` 则默认为视频资源的 [ExternalResource.md5]。 + * @param thumbnail 短视频封面图,为图片资源. + * @param video 视频资源,目前仅支持上传 mp4 格式的视频. + * @param fileName 文件名,如为 `null` 这根据 [video] 自动生成。 */ public actual suspend fun uploadShortVideo( thumbnail: ExternalResource, From 263dd9881a052b42c62ee77465d17f951fcfa759 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Thu, 3 Aug 2023 22:27:37 +0800 Subject: [PATCH 15/43] [core] remove mirai code support for ShortVideo --- .../kotlin/message/code/internal/impl.kt | 16 +------- .../kotlin/message/data/ShortVideo.kt | 8 ++-- .../kotlin/message/data/shortVideo.kt | 37 ------------------- 3 files changed, 4 insertions(+), 57 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt index df13111533..099b27df90 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt @@ -141,21 +141,7 @@ private object MiraiCodeParsers : AbstractMap(), Map FileMessage(id, internalId.toInt(), name, size.toLong()) - }, - "svideo" to MiraiCodeParser( - Regex("""(.*),(.+\..{2,4}),([a-zA-Z0-9]{32}),(\d+)(,([a-zA-Z0-9]{32}),(\d+))?""") - ) { (videoId, fullFileName, fileMd5, fileSize, _, thumbnailMd5, thumbnailSize) -> - val fileFormat = fullFileName.substringAfterLast('.') - val fileName = fullFileName.substringBeforeLast(".${fileFormat}") - ShortVideo.Builder.newBuilder(videoId).apply { - this.fileMd5 = fileMd5.hexToBytes() - this.fileName = fileName - this.fileFormat = fileFormat - this.fileSize = fileSize.toLong() - if (thumbnailMd5.isNotEmpty()) this.thumbnailMd5 = thumbnailMd5.hexToBytes() - if (thumbnailSize.isNotEmpty()) this.thumbnailSize = thumbnailSize.toLong() - }.build() - }, + } ) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 314330095a..e8f26528e7 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -34,7 +34,7 @@ import net.mamoe.mirai.utils.* * * ## 使用 [Builder] 构建短视频 * [ShortVideo] 提供 [Builder] 构建方式,必须指定 [videoId], [fileName], [fileMd5], [fileSize] 和 [fileFormat] 参数. - * 可选指定 [Builder.thumbnailMd5] 和 [Builder.thumbnailSize]. + * 可选指定 [Builder.thumbnailMd5] 和 [Builder.thumbnailSize]. 若不提供,可能会影响服务器判断缓存. * * ## 从服务器接收 * 通过监听消息接收的短视频消息可直接转换为 [OnlineShortVideo]. @@ -46,13 +46,11 @@ import net.mamoe.mirai.utils.* * # 其他信息 * * ## mirai 码支持 - * 格式: [mirai:svideo:[videoId],[fileName].[fileFormat],[fileMd5],[fileSize],`thumbnailMd5`,`thumbnailSize`] - * - * `thumbnailMd5` 和 `thumbnailSize` 是可选项. 若不提供,可能会影响服务器判断缓存. + * [ShortVideo] 不支持 mirai 码,意味着如果通过 mirai 码持久化消息,短视频消息将不会被存储. * * @since 2.16 */ -public interface ShortVideo : MessageContent, ConstrainSingle, CodableMessage { +public interface ShortVideo : MessageContent, ConstrainSingle { /** * 视频 ID. */ diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index b72acaaec1..358e5fbc56 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -140,24 +140,6 @@ internal class OnlineShortVideoImpl( return "[视频]" } - override fun appendMiraiCodeTo(builder: StringBuilder) { - builder.append("[mirai:svideo:") - builder.append(videoId) - builder.append(",") - builder.append(fileName) - builder.append(".") - builder.append(fileFormat) - builder.append(",") - builder.append(fileMd5.toUHexString("")) - builder.append(",") - builder.append(fileSize) - builder.append(",") - builder.append(thumbMd5.toUHexString("")) - builder.append(",") - builder.append(thumbSize) - builder.append("]") - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -218,25 +200,6 @@ internal class OfflineShortVideoImpl( return "[视频]" } - @Suppress("DuplicatedCode") - override fun appendMiraiCodeTo(builder: StringBuilder) { - builder.append("[mirai:svideo:") - builder.append(videoId) - builder.append(",") - builder.append(fileName) - builder.append(".") - builder.append(fileFormat) - builder.append(",") - builder.append(fileMd5.toUHexString("")) - builder.append(",") - builder.append(fileSize) - builder.append(",") - builder.append(thumbMd5.toUHexString("")) - builder.append(",") - builder.append(thumbSize) - builder.append("]") - } - override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false From 47bfc67ee18f8bb78f71c6dd16721a910359a0e8 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Thu, 3 Aug 2023 22:29:03 +0800 Subject: [PATCH 16/43] [core] ensure Mirai service is loaded before load other services --- mirai-core-api/src/commonMain/kotlin/message/data/Image.kt | 1 + mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt index 75163ff7cd..5c02027109 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Image.kt @@ -570,6 +570,7 @@ public interface InternalImageProtocol { // naming it Internal* to assign it a l @MiraiInternalApi public companion object { public val instance: InternalImageProtocol by lazy { + Mirai // initialize MiraiImpl first loadService( InternalImageProtocol::class, "net.mamoe.mirai.internal.message.InternalImageProtocolImpl" diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index e8f26528e7..c3bdbdf9f0 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.message.data import kotlinx.serialization.KSerializer +import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.AudioSupported import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.MessageSerializers @@ -240,6 +241,7 @@ public interface InternalShortVideoProtocol { @MiraiInternalApi public companion object { public val instance: InternalShortVideoProtocol by lazy { + Mirai // initialize MiraiImpl first loadService( InternalShortVideoProtocol::class, "net.mamoe.mirai.internal.message.InternalShortVideoProtocolImpl" From 0ae255331a0596aa0eee5d6e541fd99348ae62fa Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 11:42:51 +0800 Subject: [PATCH 17/43] [core] introduce `CombinedExternalResource` to reference multiple external resources for combined calculation. --- .../kotlin/contact/AbstractContact.kt | 10 +--- .../kotlin/utils/ExternalResourceImpl.kt | 15 +++++ .../kotlin/utils/ExternalResourceImpl.kt | 59 +++++++++++++++++++ .../utils/CombinedExternalResourceTest.kt | 45 ++++++++++++++ 4 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/utils/ExternalResourceImpl.kt create mode 100644 mirai-core/src/jvmBaseMain/kotlin/utils/ExternalResourceImpl.kt create mode 100644 mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index cfc18ac1d1..cba4597ef9 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -27,6 +27,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.video.PttCenterSvr import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.message.data.ShortVideo +import net.mamoe.mirai.internal.utils.CombinedExternalResource import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import kotlin.contracts.contract @@ -91,14 +92,7 @@ internal abstract class AbstractContact( } } - val highwayRespExt = buildPacket { - thumbnail.input().use { - it.copyTo(this) - } - video.input().use { - it.copyTo(this) - } - }.readBytes().toExternalResource().use { resource -> + val highwayRespExt = CombinedExternalResource(thumbnail, video).use { resource -> Highway.uploadResourceBdh( bot = bot, resource = resource, diff --git a/mirai-core/src/commonMain/kotlin/utils/ExternalResourceImpl.kt b/mirai-core/src/commonMain/kotlin/utils/ExternalResourceImpl.kt new file mode 100644 index 0000000000..046ed22007 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/utils/ExternalResourceImpl.kt @@ -0,0 +1,15 @@ +/* + * 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.utils + +import net.mamoe.mirai.utils.ExternalResource + +@Suppress("FunctionName") +internal expect fun CombinedExternalResource(vararg resources: ExternalResource): ExternalResource \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/utils/ExternalResourceImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/utils/ExternalResourceImpl.kt new file mode 100644 index 0000000000..505689456b --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/utils/ExternalResourceImpl.kt @@ -0,0 +1,59 @@ +/* + * 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.utils + +import io.ktor.utils.io.core.* +import io.ktor.utils.io.streams.* +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.md5 +import net.mamoe.mirai.utils.sha1 +import java.io.InputStream +import java.io.SequenceInputStream +import java.util.Collections + +@Suppress("FunctionName") +internal actual fun CombinedExternalResource(vararg resources: ExternalResource): ExternalResource { + return CombinedExternalResource(resources.toList()) +} + +/** + * it is caller's responsibility to guarantee the immutability of the stream. + */ +internal class CombinedExternalResource( + private val inputs: Collection +) : ExternalResource { + override val isAutoClose: Boolean = true + + override val size: Long = inputs.sumOf { it.size } + override val md5: ByteArray by lazy { combine().md5() } + override val sha1: ByteArray by lazy { combine().sha1() } + + override val formatName: String = "" + + private val _closed = CompletableDeferred() + override val closed: Deferred + get() = _closed + + override fun close() { + _closed.complete(Unit) + } + + override fun inputStream(): InputStream = combine() + + @MiraiInternalApi + override fun input(): Input = inputStream().asInput() + + private fun combine(): InputStream { + return SequenceInputStream(Collections.enumeration(inputs.map { it.inputStream() })) + } +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt b/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt new file mode 100644 index 0000000000..3a7cc35b4b --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt @@ -0,0 +1,45 @@ +/* + * 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.utils + +import io.ktor.utils.io.core.* +import net.mamoe.mirai.internal.test.AbstractTest +import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.text.toByteArray + +class CombinedExternalResourceTest : AbstractTest() { + @Test + fun `work`() { + val res1 = STRING_1.toByteArray().toExternalResource() + val res2 = STRING_1.toByteArray().toExternalResource() + + val combined1 = buildPacket { + res1.input().use { it.copyTo(this) } + res2.input().use { it.copyTo(this) } + }.readBytes().toExternalResource() + + val combined2 = CombinedExternalResource(res1, res2) + + assertEquals(combined1.size, combined2.size) + assertTrue { combined1.md5.contentEquals(combined2.md5) } + assertTrue { combined1.sha1.contentEquals(combined2.sha1) } + } + + + private val STRING_1 = """ + b4FNDvv49gMInP29t82fPJuWQ4ArG1k1YVeCN3UReWXplm4H2S4Rp7zTpt8WXRQEtTL7VemlTIytPbwUkus7qgPVsyUCFreRR1vB3QhRznXqcT06fDkXJQJKyyBGEdwddNWZAkqZcdrOk679sG14kKK5GexaQUmdfTivT5VPO8w1yoWPcUHPfpjB0shCEzjkHI84LJbWNRCVjoZhy0jZAKZxLrsi1sGhl30QcXCFnHpPhWbED8Er9c8gVbjYsG8ejaUlbeNNdKW3GoOpgjFLbwZoQI4QZZgvP5jhBWUPiMG3MCcPlYRSgTf70JpDVTE0YOLhXdJJxz87S8MR4M7rU0WO7ZRkoFOQpFHdmfMmJxbiATHHkOyHVhu1mvA0L72MNtDQP5GcKlDbDcdJL7om4FmekAVVnh7R + """.trimIndent() + private val STRING_2 = """ + FdDoAZt2hJkKAfEWBNWO44R0tJRmApqIwHDD05oW0jyLVVPOdcPaFjY1muYM1qa6jbhZppWYm1oOmgbpFgdPZRYDgzznR0kSapdqXeSSevV4ww4E1U71ELDMsq4f0a1Y8K6UxIOpQl1n20eoe80fHuXKkfN6kbhROBXcwGbiFRpPg5k8G5hCerQQunQyNoeEZrbKacq2OYkOEJV57LuSbBTF4FMZYxCEp1a8omnK1EUHC1Go5pGy0dovz78KpCshPr7MHNMnRu0FiuJ1WYT8ri8iXWsTx3AMxHRjCYfJgrtqc86L3HW0V6Wr8FqFMJLtFl4PgXj5etfRSaaqRJFIZ3nWiRqW48JMRqdGRvLTUWs1Zoa8H11bych18MVypUQJOyxghLLJw0ZP4CvSNUeJOEMitxFxyzjC + """.trimIndent() +} \ No newline at end of file From 553243ae57766257b34dbf7381957ec13b2d28bd Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 11:47:06 +0800 Subject: [PATCH 18/43] [core] move refine context key defined in `OnlineShortVideoMsgInternal` to `RefineContext` --- .../contact/roaming/RoamingMessagesImplGroup.kt | 8 ++++---- .../kotlin/message/ReceiveMessageHandler.kt | 5 ++--- .../commonMain/kotlin/message/RefinableMessage.kt | 6 ++++++ .../commonMain/kotlin/message/data/shortVideo.kt | 12 +++--------- .../network/notice/group/GroupMessageProcessor.kt | 14 +++++++------- .../network/notice/priv/PrivateMessageProcessor.kt | 8 ++++---- 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt index 29dc3f9a75..88ac2251f1 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt @@ -12,8 +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.RefineContextKey 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 @@ -74,9 +74,9 @@ internal class RoamingMessagesImplGroup( MessageSourceKind.GROUP, SimpleRefineContext( mutableMapOf( - OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP, - OnlineShortVideoMsgInternal.FromId to it.msgHead.fromUin, - OnlineShortVideoMsgInternal.GroupIdOrZero to contact.uin, + RefineContextKey.MessageSourceKind to MessageSourceKind.GROUP, + RefineContextKey.FromId to it.msgHead.fromUin, + RefineContextKey.GroupIdOrZero to contact.uin, ) ) ) diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index ebed777826..b8e7e2ba33 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -17,7 +17,6 @@ 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 @@ -83,8 +82,8 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline( } val mutableRefineContextApplier: MutableRefineContext.() -> Unit = { - set(OnlineShortVideoMsgInternal.MessageSourceKind, kind) - set(OnlineShortVideoMsgInternal.GroupIdOrZero, groupId) + set(RefineContextKey.MessageSourceKind, kind) + set(RefineContextKey.GroupIdOrZero, groupId) } return listOf(this).toMessageChainOnline( diff --git a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt index 41db4c07ce..d7c9b627fe 100644 --- a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt +++ b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt @@ -99,6 +99,12 @@ internal class RefineContextKey( append(')') } } + + internal companion object { + val MessageSourceKind = RefineContextKey("MessageSourceKind") + val FromId = RefineContextKey("FromId") + val GroupIdOrZero = RefineContextKey("GroupIdOrZero") + } } /** diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 358e5fbc56..7f57560e2d 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -39,9 +39,9 @@ internal class OnlineShortVideoMsgInternal( 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 sourceKind = refineContext[RefineContextKey.MessageSourceKind] ?: return null + val fromId = refineContext[RefineContextKey.FromId] ?: return null + val groupId = refineContext[RefineContextKey.GroupIdOrZero] ?: return null val contact = when (sourceKind) { net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId) @@ -100,12 +100,6 @@ internal class OnlineShortVideoMsgInternal( override fun contentToString(): String { TODO("Not yet implemented") } - - companion object { - val MessageSourceKind = RefineContextKey("MessageSourceKind") - val FromId = RefineContextKey("FromId") - val GroupIdOrZero = RefineContextKey("GroupIdOrZero") - } } internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt index 8999e073c4..cedaba8030 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupMessageProcessor.kt @@ -20,8 +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.RefineContextKey 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 @@ -167,9 +167,9 @@ internal class GroupMessageProcessor( MessageSourceKind.GROUP, SimpleRefineContext( mutableMapOf( - OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP, - OnlineShortVideoMsgInternal.FromId to sender.uin, - OnlineShortVideoMsgInternal.GroupIdOrZero to group.uin, + RefineContextKey.MessageSourceKind to MessageSourceKind.GROUP, + RefineContextKey.FromId to sender.uin, + RefineContextKey.GroupIdOrZero to group.uin, ) ) ), @@ -193,9 +193,9 @@ internal class GroupMessageProcessor( MessageSourceKind.GROUP, SimpleRefineContext( mutableMapOf( - OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.GROUP, - OnlineShortVideoMsgInternal.FromId to sender.uin, - OnlineShortVideoMsgInternal.GroupIdOrZero to group.uin, + RefineContextKey.MessageSourceKind to MessageSourceKind.GROUP, + RefineContextKey.FromId to sender.uin, + RefineContextKey.GroupIdOrZero to group.uin, ) ) ), 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 f9f10c25af..ca55935492 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt @@ -15,8 +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.RefineContextKey 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 @@ -139,9 +139,9 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor(type user.correspondingMessageSourceKind, SimpleRefineContext( mutableMapOf( - OnlineShortVideoMsgInternal.MessageSourceKind to MessageSourceKind.FRIEND, - OnlineShortVideoMsgInternal.FromId to user.uin, - OnlineShortVideoMsgInternal.GroupIdOrZero to 0L, + RefineContextKey.MessageSourceKind to MessageSourceKind.FRIEND, + RefineContextKey.FromId to user.uin, + RefineContextKey.GroupIdOrZero to 0L, ) ) ) From 6a5b9f3857a0935333fbb88c22296cabaac18605 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 11:48:22 +0800 Subject: [PATCH 19/43] [core] remove data class --- .../kotlin/event/events/ShortVideoUploadEvent.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt index d3224af772..03f33d0a54 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt @@ -33,7 +33,7 @@ import net.mamoe.mirai.utils.MiraiInternalApi * @see Contact.uploadShortVideo 上传短视频. 为广播这个事件的唯一途径 */ @OptIn(MiraiInternalApi::class) -public data class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( +public class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( public val target: Contact, public val thumbnailSource: ExternalResource, public val videoSource: ExternalResource @@ -61,11 +61,11 @@ public sealed class ShortVideoUploadEvent : BotEvent, BotActiveEvent, AbstractEv public override val bot: Bot get() = target.bot - public data class Succeed @MiraiInternalApi constructor( + public class Succeed @MiraiInternalApi constructor( override val target: Contact, override val thumbnailSource: ExternalResource, override val videoSource: ExternalResource, - val video: ShortVideo + public val video: ShortVideo ) : ShortVideoUploadEvent() { override fun toString(): String { return "ShortVideoUploadEvent.Succeed(target=$target, " + @@ -75,12 +75,12 @@ public sealed class ShortVideoUploadEvent : BotEvent, BotActiveEvent, AbstractEv } } - public data class Failed @MiraiInternalApi constructor( + public class Failed @MiraiInternalApi constructor( override val target: Contact, override val thumbnailSource: ExternalResource, override val videoSource: ExternalResource, - val errno: Int, - val message: String + public val errno: Int, + public val message: String ) : ShortVideoUploadEvent() { override fun toString(): String { return "ShortVideoUploadEvent.Failed(target=$target, " + From 6fe4b633c1dc243879448091e985ba707af136db Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 11:55:08 +0800 Subject: [PATCH 20/43] [core] broadcast `ShortVideoUploadEvent.Failed` event --- .../src/commonMain/kotlin/message/data/ShortVideo.kt | 1 + mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt | 2 +- mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index c3bdbdf9f0..ea4e1b1143 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -51,6 +51,7 @@ import net.mamoe.mirai.utils.* * * @since 2.16 */ +@NotStableForInheritance public interface ShortVideo : MessageContent, ConstrainSingle { /** * 视频 ID. diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index cba4597ef9..abb6e412ad 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -124,7 +124,7 @@ internal abstract class AbstractContact( video, -1, "highway upload short video failed, extendInfo is null." - ) + ).broadcast() error("highway upload short video failed, extendInfo is null.") } diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 7f57560e2d..4fdb4cf551 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -126,7 +126,7 @@ internal class OnlineShortVideoImpl( ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { override fun toString(): String { - return "[mirai:svideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + + return "[mirai:shortvideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + "thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } @@ -186,7 +186,7 @@ internal class OfflineShortVideoImpl( * offline short video uses */ override fun toString(): String { - return "[mirai:svideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, " + + return "[mirai:shortvideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, " + "videoSize=${fileSize}, thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } From d203935c887baad782404dbd0a65e8ae61f0dceb Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 11:55:23 +0800 Subject: [PATCH 21/43] [core] warn when cannot determine fromId --- .../kotlin/message/ReceiveMessageHandler.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index b8e7e2ba33..0765ba620f 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -27,6 +27,7 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.castOrNull import net.mamoe.mirai.utils.structureToString import net.mamoe.mirai.utils.toLongUnsigned +import net.mamoe.mirai.utils.warning /** * 只在手动构造 [OfflineMessageSource] 时调用 @@ -150,12 +151,20 @@ private fun List.toMessageChainImpl( } else null if (source != null) builder.add(source) + val fromId = source?.fromId ?: firstOrNull()?.msgHead?.fromUin + if (fromId == null) { + bot.logger.warning { + "Cannot determine fromId from message source and msg elements, " + + "source: $source, elements: ${this.joinToString(", ")}" + } + } + messageList.forEach { msg -> facade.decode( msg.msgBody.richText.elems, groupIdOrZero, messageSourceKind, - source?.fromId ?: first().msgHead.fromUin, + fromId ?: 0, bot, builder, msg From 2fea0c8bc1d38cd540c29c242456e1588a959a6e Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:00:54 +0800 Subject: [PATCH 22/43] [core] add `contentToString` and `toString` for `OnlineShortVideoMsgInternal` --- .../src/commonMain/kotlin/message/data/shortVideo.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 4fdb4cf551..d9f2591b6a 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -47,7 +47,7 @@ internal class OnlineShortVideoMsgInternal( net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId) net.mamoe.mirai.message.data.MessageSourceKind.GROUP -> bot.getGroup(groupId) else -> return null // TODO: ignore processing stranger's video message - }.cast() + } as Contact val sender = when (sourceKind) { net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId) net.mamoe.mirai.message.data.MessageSourceKind.GROUP -> { @@ -56,7 +56,7 @@ internal class OnlineShortVideoMsgInternal( } else -> return null // TODO: ignore processing stranger's video message - }.cast() + } as User val shortVideoDownloadReq = bot.network.sendAndExpect( PttCenterSvr.ShortVideoDownReq( @@ -94,11 +94,11 @@ internal class OnlineShortVideoMsgInternal( override fun toString(): String { - TODO("Not yet implemented") + return "OnlineShortVideoMsgInternal(videoElem=$videoFile)" } override fun contentToString(): String { - TODO("Not yet implemented") + return "[视频元数据]" } } From c004f6e560d241d52316c65b08bc24403c16c80f Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:02:18 +0800 Subject: [PATCH 23/43] [core] optimize imports --- mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt | 1 - mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt | 2 -- mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt | 1 - .../jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt | 2 +- 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index ea4e1b1143..6177081aa7 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -14,7 +14,6 @@ import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.AudioSupported import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.MessageSerializers -import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString import net.mamoe.mirai.message.data.ShortVideo.Builder import net.mamoe.mirai.message.data.visitor.MessageVisitor diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index abb6e412ad..44ea6bb959 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -10,7 +10,6 @@ package net.mamoe.mirai.internal.contact import io.ktor.utils.io.core.* -import io.ktor.utils.io.core.use import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.broadcast @@ -29,7 +28,6 @@ import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.message.data.ShortVideo import net.mamoe.mirai.internal.utils.CombinedExternalResource import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index d9f2591b6a..fde855c306 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -22,7 +22,6 @@ 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.* -import net.mamoe.mirai.utils.cast import net.mamoe.mirai.utils.toUHexString /** diff --git a/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt b/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt index 3a7cc35b4b..129a0c48c7 100644 --- a/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/utils/CombinedExternalResourceTest.kt @@ -21,7 +21,7 @@ class CombinedExternalResourceTest : AbstractTest() { @Test fun `work`() { val res1 = STRING_1.toByteArray().toExternalResource() - val res2 = STRING_1.toByteArray().toExternalResource() + val res2 = STRING_2.toByteArray().toExternalResource() val combined1 = buildPacket { res1.input().use { it.copyTo(this) } From de059c102e86a97c49203086cdf1817187efba68 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:16:15 +0800 Subject: [PATCH 24/43] [core] import --- .../kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt index fcc593822b..b4f1f88e60 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt @@ -16,8 +16,8 @@ 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.RefineContextKey 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 @@ -44,7 +44,7 @@ internal sealed class TimeBasedRoamingMessagesImpl : AbstractRoamingMessages() { contact.bot, refineContext = SimpleRefineContext( mutableListOf( - OnlineShortVideoMsgInternal.FromId to it.msgHead.fromUin + RefineContextKey.FromId to it.msgHead.fromUin ).cast() ) ) @@ -58,7 +58,7 @@ internal sealed class TimeBasedRoamingMessagesImpl : AbstractRoamingMessages() { contact.bot, refineContext = SimpleRefineContext( mutableListOf( - OnlineShortVideoMsgInternal.FromId to message.msgHead.fromUin + RefineContextKey.FromId to message.msgHead.fromUin ).cast() ) ) From 428e2e54d86ebba88de42ecd09751f0fe6913cf7 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:40:47 +0800 Subject: [PATCH 25/43] [core] revert --- .../src/commonMain/kotlin/message/code/internal/impl.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt index 099b27df90..862d5348b6 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt @@ -15,7 +15,6 @@ import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi -import net.mamoe.mirai.utils.hexToBytes internal fun String.parseMiraiCodeImpl(contact: Contact?): MessageChain = buildMessageChain { @@ -54,7 +53,6 @@ private fun String.forEachMiraiCode(block: (origin: String, name: String?, args: '\\' -> { pos += 2 } - '[' -> { if (get(pos + 1) == 'm' && get(pos + 2) == 'i' && get(pos + 3) == 'r' && get(pos + 4) == 'a' && @@ -85,7 +83,6 @@ private fun String.forEachMiraiCode(block: (origin: String, name: String?, args: } } else pos++ } - else -> { pos++ } @@ -141,7 +138,7 @@ private object MiraiCodeParsers : AbstractMap(), Map FileMessage(id, internalId.toInt(), name, size.toLong()) - } + }, ) @@ -181,7 +178,6 @@ internal sealed class MiraiCodeParser { pos++ begin = pos } - else -> pos++ } } @@ -214,7 +210,6 @@ internal fun StringBuilder.appendStringAsMiraiCode(value: String): StringBuilder ':', ',', '\\', -> append("\\").append(char) - '\n' -> append("\\n") '\r' -> append("\\r") else -> append(char) From 461f61211cff9b6dcca17860fe62a1eabdac4b7d Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:43:04 +0800 Subject: [PATCH 26/43] [core] doc --- mirai-core-api/src/commonMain/kotlin/contact/Contact.kt | 2 +- .../src/commonMain/kotlin/message/data/ShortVideo.kt | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index 92e3286325..29ff2d490a 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -97,7 +97,7 @@ public expect interface Contact : ContactOrBot, CoroutineScope { * * @param thumbnail 短视频封面图,为图片资源. * @param video 视频资源,目前仅支持上传 mp4 格式的视频. - * @param fileName 文件名,如为 `null` 这根据 [video] 自动生成. + * @param fileName 文件名,若为 `null` 则根据 [video] 自动生成. */ public suspend fun uploadShortVideo( thumbnail: ExternalResource, diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 6177081aa7..2d23c7a886 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -43,11 +43,6 @@ import net.mamoe.mirai.utils.* * 通过 [OnlineShortVideo.urlForDownload] 获取下载链接. * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [fileName] 作为文件名,[fileFormat] 作为文件拓展名. * - * # 其他信息 - * - * ## mirai 码支持 - * [ShortVideo] 不支持 mirai 码,意味着如果通过 mirai 码持久化消息,短视频消息将不会被存储. - * * @since 2.16 */ @NotStableForInheritance @@ -163,7 +158,7 @@ public interface ShortVideo : MessageContent, ConstrainSingle { /** * 在线短视频消息,即从消息事件中接收到的视频消息. * - * [OnlineShortVideo] 仅可以从事件中的[消息链][MessageChain]接收, 不可手动构造. 若需要手动构造, 请使用 [ShortVideo.Builder] 构造 [离线短视频][OfflineShortVideo]. + * [OnlineShortVideo] 仅可以从事件中的[消息链][MessageChain]接收, 不可手动构造. * * ### 序列化支持 * From 342b4598963df6f71bd861ce9a4de4c35f006e37 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Fri, 4 Aug 2023 12:47:14 +0800 Subject: [PATCH 27/43] [core] auto close resource --- .../kotlin/contact/AbstractContact.kt | 166 +++++++++--------- 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index 44ea6bb959..6f53c221f1 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -41,42 +41,96 @@ internal abstract class AbstractContact( thumbnail: ExternalResource, video: ExternalResource, fileName: String? - ): ShortVideo { - if (this !is Group && this !is Friend) { - throw UnsupportedOperationException("short video can only upload to friend or group.") - } + ): ShortVideo = thumbnail.withAutoClose { + video.withAutoClose { + if (this !is Group && this !is Friend) { + throw UnsupportedOperationException("short video can only upload to friend or group.") + } - if (video.formatName != "mp4") { - throw UnsupportedOperationException("video format ${video.formatName} is not supported.") - } + if (video.formatName != "mp4") { + throw UnsupportedOperationException("video format ${video.formatName} is not supported.") + } - if (BeforeShortVideoUploadEvent(this, thumbnail, video).broadcast().isCancelled) { - throw EventCancelledException("cancelled by BeforeShortVideoUploadEvent") - } + if (BeforeShortVideoUploadEvent(this, thumbnail, video).broadcast().isCancelled) { + throw EventCancelledException("cancelled by BeforeShortVideoUploadEvent") + } - // local uploaded offline short video uses video file md5 as its file name by default - val videoName = fileName ?: video.md5.toUHexString("") - - val uploadResp = bot.network.sendAndExpect( - PttCenterSvr.GroupShortVideoUpReq( - client = bot.client, - contact = this, - thumbnailFileMd5 = thumbnail.md5, - thumbnailFileSize = thumbnail.size, - videoFileName = videoName, - videoFileMd5 = video.md5, - videoFileSize = video.size, - videoFileFormat = video.formatName + // local uploaded offline short video uses video file md5 as its file name by default + val videoName = fileName ?: video.md5.toUHexString("") + + val uploadResp = bot.network.sendAndExpect( + PttCenterSvr.GroupShortVideoUpReq( + client = bot.client, + contact = this, + thumbnailFileMd5 = thumbnail.md5, + thumbnailFileSize = thumbnail.size, + videoFileName = videoName, + videoFileMd5 = video.md5, + videoFileSize = video.size, + videoFileFormat = video.formatName + ) ) - ) - // get thumbnail image width and height - val thumbnailInfo = thumbnail.calculateImageInfo() + // get thumbnail image width and height + val thumbnailInfo = thumbnail.calculateImageInfo() + + // fast path + if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { + return OfflineShortVideoImpl( + uploadResp.fileId, + videoName, + video.md5, + video.size, + video.formatName, + thumbnail.md5, + thumbnail.size, + thumbnailInfo.width, + thumbnailInfo.height + ).also { + ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() + } + } + + val highwayRespExt = CombinedExternalResource(thumbnail, video).use { resource -> + Highway.uploadResourceBdh( + bot = bot, + resource = resource, + kind = ResourceKind.SHORT_VIDEO, + commandId = 25, + extendInfo = buildPacket { + writeProtoBuf( + PttShortVideo.PttShortVideoUploadReq.serializer(), + PttCenterSvr.GroupShortVideoUpReq.buildShortVideoFileInfo( + client = bot.client, + contact = this@AbstractContact, + thumbnailFileMd5 = thumbnail.md5, + thumbnailFileSize = thumbnail.size, + videoFileName = videoName, + videoFileMd5 = video.md5, + videoFileSize = video.size, + videoFileFormat = video.formatName + ) + ) + }.readBytes(), + encrypt = true + ).extendInfo + } + + if (highwayRespExt == null) { + ShortVideoUploadEvent.Failed( + this, + thumbnail, + video, + -1, + "highway upload short video failed, extendInfo is null." + ).broadcast() + error("highway upload short video failed, extendInfo is null.") + } + + val highwayUploadResp = highwayRespExt.loadAs(PttShortVideo.PttShortVideoUploadResp.serializer()) - // fast path - if (uploadResp is PttCenterSvr.GroupShortVideoUpReq.Response.FileExists) { - return OfflineShortVideoImpl( - uploadResp.fileId, + OfflineShortVideoImpl( + highwayUploadResp.fileid, videoName, video.md5, video.size, @@ -89,58 +143,6 @@ internal abstract class AbstractContact( ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() } } - - val highwayRespExt = CombinedExternalResource(thumbnail, video).use { resource -> - Highway.uploadResourceBdh( - bot = bot, - resource = resource, - kind = ResourceKind.SHORT_VIDEO, - commandId = 25, - extendInfo = buildPacket { - writeProtoBuf( - PttShortVideo.PttShortVideoUploadReq.serializer(), - PttCenterSvr.GroupShortVideoUpReq.buildShortVideoFileInfo( - client = bot.client, - contact = this@AbstractContact, - thumbnailFileMd5 = thumbnail.md5, - thumbnailFileSize = thumbnail.size, - videoFileName = videoName, - videoFileMd5 = video.md5, - videoFileSize = video.size, - videoFileFormat = video.formatName - ) - ) - }.readBytes(), - encrypt = true - ).extendInfo - } - - if (highwayRespExt == null) { - ShortVideoUploadEvent.Failed( - this, - thumbnail, - video, - -1, - "highway upload short video failed, extendInfo is null." - ).broadcast() - error("highway upload short video failed, extendInfo is null.") - } - - val highwayUploadResp = highwayRespExt.loadAs(PttShortVideo.PttShortVideoUploadResp.serializer()) - - return OfflineShortVideoImpl( - highwayUploadResp.fileid, - videoName, - video.md5, - video.size, - video.formatName, - thumbnail.md5, - thumbnail.size, - thumbnailInfo.width, - thumbnailInfo.height - ).also { - ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() - } } } From b50124f057f1678753d7f2febd571730673f49ab Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 6 Aug 2023 10:04:24 +0800 Subject: [PATCH 28/43] dump api --- .../android/api/android.api | 27 +------------------ .../compatibility-validation/jvm/api/jvm.api | 27 +------------------ 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 421774aeef..6f31f01cce 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -1892,18 +1892,10 @@ public final class net/mamoe/mirai/event/events/BeforeImageUploadEvent : net/mam } public final class net/mamoe/mirai/event/events/BeforeShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/CancellableEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; - public fun equals (Ljava/lang/Object;)Z public fun getBot ()Lnet/mamoe/mirai/Bot; public final fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public final fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public final fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; } public abstract interface class net/mamoe/mirai/event/events/BotActiveEvent : net/mamoe/mirai/event/events/BotEvent { @@ -2974,36 +2966,19 @@ public abstract class net/mamoe/mirai/event/events/ShortVideoUploadEvent : net/m } public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component4 ()I - public final fun component5 ()Ljava/lang/String; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; - public fun equals (Ljava/lang/Object;)Z public final fun getErrno ()I public final fun getMessage ()Ljava/lang/String; public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component4 ()Lnet/mamoe/mirai/message/data/ShortVideo; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; - public fun equals (Ljava/lang/Object;)Z public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public final fun getVideo ()Lnet/mamoe/mirai/message/data/ShortVideo; public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -5301,7 +5276,7 @@ public abstract interface class net/mamoe/mirai/message/data/ServiceMessage : ne public final class net/mamoe/mirai/message/data/ServiceMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } -public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { +public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; public abstract fun getFileFormat ()Ljava/lang/String; public abstract fun getFileMd5 ()[B diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 8c3414bf25..93e19db5b7 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -1892,18 +1892,10 @@ public final class net/mamoe/mirai/event/events/BeforeImageUploadEvent : net/mam } public final class net/mamoe/mirai/event/events/BeforeShortVideoUploadEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/CancellableEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/event/VerboseEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BeforeShortVideoUploadEvent; - public fun equals (Ljava/lang/Object;)Z public fun getBot ()Lnet/mamoe/mirai/Bot; public final fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public final fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public final fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; } public abstract interface class net/mamoe/mirai/event/events/BotActiveEvent : net/mamoe/mirai/event/events/BotEvent { @@ -2974,36 +2966,19 @@ public abstract class net/mamoe/mirai/event/events/ShortVideoUploadEvent : net/m } public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component4 ()I - public final fun component5 ()Ljava/lang/String; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;ILjava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Failed; - public fun equals (Ljava/lang/Object;)Z public final fun getErrno ()I public final fun getMessage ()Ljava/lang/String; public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class net/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed : net/mamoe/mirai/event/events/ShortVideoUploadEvent { - public final fun component1 ()Lnet/mamoe/mirai/contact/Contact; - public final fun component2 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component3 ()Lnet/mamoe/mirai/utils/ExternalResource; - public final fun component4 ()Lnet/mamoe/mirai/message/data/ShortVideo; - public final fun copy (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; - public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed;Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/message/data/ShortVideo;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/ShortVideoUploadEvent$Succeed; - public fun equals (Ljava/lang/Object;)Z public fun getTarget ()Lnet/mamoe/mirai/contact/Contact; public fun getThumbnailSource ()Lnet/mamoe/mirai/utils/ExternalResource; public final fun getVideo ()Lnet/mamoe/mirai/message/data/ShortVideo; public fun getVideoSource ()Lnet/mamoe/mirai/utils/ExternalResource; - public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -5301,7 +5276,7 @@ public abstract interface class net/mamoe/mirai/message/data/ServiceMessage : ne public final class net/mamoe/mirai/message/data/ServiceMessage$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } -public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { +public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; public abstract fun getFileFormat ()Ljava/lang/String; public abstract fun getFileMd5 ()[B From 75abe69bcdecdd5b40ce471309b2cc84bae86512 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 16:39:06 +0800 Subject: [PATCH 29/43] keep consistence of param name --- .../commonMain/kotlin/message/data/ShortVideo.kt | 10 +++++----- .../commonMain/kotlin/message/data/shortVideo.kt | 16 ++++++++-------- .../message/protocol/impl/ShortVideoProtocol.kt | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 2d23c7a886..37b8c3c2a1 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -33,7 +33,7 @@ import net.mamoe.mirai.utils.* * 使用 [Contact.uploadShortVideo],将视频缩略图和视频[资源][ExternalResource] 上传以得到 [OfflineShortVideo]. * * ## 使用 [Builder] 构建短视频 - * [ShortVideo] 提供 [Builder] 构建方式,必须指定 [videoId], [fileName], [fileMd5], [fileSize] 和 [fileFormat] 参数. + * [ShortVideo] 提供 [Builder] 构建方式,必须指定 [videoId], [filename], [fileMd5], [fileSize] 和 [fileFormat] 参数. * 可选指定 [Builder.thumbnailMd5] 和 [Builder.thumbnailSize]. 若不提供,可能会影响服务器判断缓存. * * ## 从服务器接收 @@ -41,7 +41,7 @@ import net.mamoe.mirai.utils.* * * # 下载视频 * 通过 [OnlineShortVideo.urlForDownload] 获取下载链接. - * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [fileName] 作为文件名,[fileFormat] 作为文件拓展名. + * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [filename] 作为文件名,[fileFormat] 作为文件拓展名. * * @since 2.16 */ @@ -70,7 +70,7 @@ public interface ShortVideo : MessageContent, ConstrainSingle { /* * 视频文件名,不包括拓展名 */ - public val fileName: String + public val filename: String @MiraiInternalApi @@ -108,7 +108,7 @@ public interface ShortVideo : MessageContent, ConstrainSingle { public var fileFormat: String = "" /** - * @see ShortVideo.fileName + * @see ShortVideo.filename */ public var fileName: String = "" @@ -187,7 +187,7 @@ public interface OnlineShortVideo : ShortVideo { * * [OfflineShortVideo] 拥有协议上必要的五个属性: * - 视频 ID [videoId] - * - 视频文件名 [fileName] + * - 视频文件名 [filename] * - 视频 MD5 [fileMd5] * - 视频大小 [fileSize] * - 视频格式 [fileFormat] diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index fde855c306..d96039739c 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -114,7 +114,7 @@ internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { internal class OnlineShortVideoImpl( override val videoId: String, override val fileMd5: ByteArray, - override val fileName: String, + override val filename: String, override val fileSize: Long, override val fileFormat: String, override val urlForDownload: String, @@ -125,7 +125,7 @@ internal class OnlineShortVideoImpl( ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { override fun toString(): String { - return "[mirai:shortvideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + + return "[mirai:shortvideo:$filename.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + "thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } @@ -141,7 +141,7 @@ internal class OnlineShortVideoImpl( if (videoId != other.videoId) return false if (!fileMd5.contentEquals(other.fileMd5)) return false - if (fileName != other.fileName) return false + if (filename != other.filename) return false if (fileSize != other.fileSize) return false if (fileFormat != other.fileFormat) return false if (urlForDownload != other.urlForDownload) return false @@ -156,7 +156,7 @@ internal class OnlineShortVideoImpl( override fun hashCode(): Int { var result = videoId.hashCode() result = 31 * result + fileMd5.contentHashCode() - result = 31 * result + fileName.hashCode() + result = 31 * result + filename.hashCode() result = 31 * result + fileSize.hashCode() result = 31 * result + fileFormat.hashCode() result = 31 * result + urlForDownload.hashCode() @@ -171,7 +171,7 @@ internal class OnlineShortVideoImpl( @Serializable internal class OfflineShortVideoImpl( override val videoId: String, - override val fileName: String, + override val filename: String, override val fileMd5: ByteArray, override val fileSize: Long, override val fileFormat: String, @@ -185,7 +185,7 @@ internal class OfflineShortVideoImpl( * offline short video uses */ override fun toString(): String { - return "[mirai:shortvideo:$fileName.$fileFormat, video=${fileMd5.toUHexString("")}, " + + return "[mirai:shortvideo:$filename.$fileFormat, video=${fileMd5.toUHexString("")}, " + "videoSize=${fileSize}, thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } @@ -200,7 +200,7 @@ internal class OfflineShortVideoImpl( other as OfflineShortVideoImpl if (videoId != other.videoId) return false - if (fileName != other.fileName) return false + if (filename != other.filename) return false if (!fileMd5.contentEquals(other.fileMd5)) return false if (fileSize != other.fileSize) return false if (fileFormat != other.fileFormat) return false @@ -214,7 +214,7 @@ internal class OfflineShortVideoImpl( override fun hashCode(): Int { var result = videoId.hashCode() - result = 31 * result + fileName.hashCode() + result = 31 * result + filename.hashCode() result = 31 * result + fileMd5.contentHashCode() result = 31 * result + fileSize.hashCode() result = 31 * result + fileFormat.hashCode() diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index a538c0157a..445828a38a 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -59,7 +59,7 @@ internal class ShortVideoProtocol : MessageProtocol() { videoFile = ImMsgBody.VideoFile( fileUuid = data.videoId.encodeToByteArray(), fileMd5 = data.fileMd5, - fileName = data.fileName.encodeToByteArray(), + fileName = data.filename.encodeToByteArray(), fileFormat = 3, // TODO: only support mp4 now fileTime = 10, fileSize = data.fileSize.toInt(), From 0e35672012d9fa9994e636f0497a699054333d06 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 16:53:48 +0800 Subject: [PATCH 30/43] update doc --- .../kotlin/event/events/ShortVideoUploadEvent.kt | 3 ++- .../src/commonMain/kotlin/message/data/ShortVideo.kt | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt index 03f33d0a54..e8575cf6ce 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/ShortVideoUploadEvent.kt @@ -31,6 +31,7 @@ import net.mamoe.mirai.utils.MiraiInternalApi * 若此事件被取消, [ShortVideoUploadEvent] 不会广播. * * @see Contact.uploadShortVideo 上传短视频. 为广播这个事件的唯一途径 + * @since 2.16 */ @OptIn(MiraiInternalApi::class) public class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( @@ -49,9 +50,9 @@ public class BeforeShortVideoUploadEvent @MiraiInternalApi constructor( * 若 [BeforeImageUploadEvent] 被取消, 此事件不会广播. * * @see Contact.uploadShortVideo 上传短视频. 为广播这个事件的唯一途径 - * * @see Succeed * @see Failed + * @since 2.16 */ @OptIn(MiraiInternalApi::class) public sealed class ShortVideoUploadEvent : BotEvent, BotActiveEvent, AbstractEvent(), VerboseEvent { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 37b8c3c2a1..6906a8782e 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -39,6 +39,11 @@ import net.mamoe.mirai.utils.* * ## 从服务器接收 * 通过监听消息接收的短视频消息可直接转换为 [OnlineShortVideo]. * + * kotlin 示例: + * ```kotlin + * val video: OnlineShortVideo = event.message[OnlineShortVideo] + * ``` + * * # 下载视频 * 通过 [OnlineShortVideo.urlForDownload] 获取下载链接. * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [filename] 作为文件名,[fileFormat] 作为文件拓展名. @@ -218,7 +223,7 @@ public interface OfflineShortVideo : ShortVideo { } /** - * 内部短视频协议实现 + * 内部短视频协议实现, 请不要使用此接口 * @since 2.16.0 */ @MiraiInternalApi From 884bc85bcb9d3fbbdddd05946389695e39f798ba Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 21:54:27 +0800 Subject: [PATCH 31/43] move Builder to OfflineShortVideo --- .../kotlin/message/data/ShortVideo.kt | 155 +++++++++--------- 1 file changed, 77 insertions(+), 78 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 6906a8782e..83e12e1684 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -15,26 +15,25 @@ import net.mamoe.mirai.contact.AudioSupported import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString -import net.mamoe.mirai.message.data.ShortVideo.Builder import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* /** - * 短视频消息,指的是可在聊天界面在线播放的视频消息,而非在群文件上传的视频文件. + * 短视频消息, 指的是可在聊天界面在线播放的视频消息, 而非在群文件上传的视频文件. * * 短视频消息分为 [OnlineShortVideo] 与 [OfflineShortVideo]. 在本地上传的短视频为 [OfflineShortVideo]. 从服务器接收的短视频为 [OnlineShortVideo]. * - * 最推荐存储的方式是下载视频文件,每次都通过上传该文件获取视频消息. + * 最推荐存储的方式是下载视频文件, 每次都通过上传该文件获取视频消息. * 在上传视频时服务器会根据缓存情况选择回复已有视频 ID 或要求客户端上传. * * # 获取短视频消息示例 * * ## 上传短视频 - * 使用 [Contact.uploadShortVideo],将视频缩略图和视频[资源][ExternalResource] 上传以得到 [OfflineShortVideo]. + * 使用 [Contact.uploadShortVideo], 将视频缩略图和视频[资源][ExternalResource] 上传以得到 [OfflineShortVideo]. * - * ## 使用 [Builder] 构建短视频 - * [ShortVideo] 提供 [Builder] 构建方式,必须指定 [videoId], [filename], [fileMd5], [fileSize] 和 [fileFormat] 参数. - * 可选指定 [Builder.thumbnailMd5] 和 [Builder.thumbnailSize]. 若不提供,可能会影响服务器判断缓存. + * ## 使用 [OfflineShortVideo.Builder] 构建短视频 + * [OfflineShortVideo] 提供 [Builder][OfflineShortVideo.Builder] 构建方式, 必须指定 [videoId], [filename], [fileMd5], [fileSize] 和 [fileFormat] 参数. + * 可选指定 [thumbnailMd5][OfflineShortVideo.Builder.thumbnailMd5] 和 [thumbnailSize][OfflineShortVideo.Builder.thumbnailSize]. 若不提供, 可能会影响服务器判断缓存. * * ## 从服务器接收 * 通过监听消息接收的短视频消息可直接转换为 [OnlineShortVideo]. @@ -46,7 +45,7 @@ import net.mamoe.mirai.utils.* * * # 下载视频 * 通过 [OnlineShortVideo.urlForDownload] 获取下载链接. - * 该下载链接不包含短视频的文件信息,可以使用 [videoId] 或 [filename] 作为文件名,[fileFormat] 作为文件拓展名. + * 该下载链接不包含短视频的文件信息, 可以使用 [videoId] 或 [filename] 作为文件名, [fileFormat] 作为文件拓展名. * * @since 2.16 */ @@ -73,7 +72,7 @@ public interface ShortVideo : MessageContent, ConstrainSingle { public val fileFormat: String /* - * 视频文件名,不包括拓展名 + * 视频文件名, 不包括拓展名 */ public val filename: String @@ -91,77 +90,10 @@ public interface ShortVideo : MessageContent, ConstrainSingle { AbstractPolymorphicMessageKey(MessageContent, { it.safeCast() }) { } - - public class Builder internal constructor( - public var videoId: String - ) { - public constructor() : this("") - - /** - * @see ShortVideo.fileMd5 - */ - public var fileMd5: ByteArray = EMPTY_BYTE_ARRAY - - /** - * @see ShortVideo.fileSize - */ - public var fileSize: Long = 0 - - /** - * @see ShortVideo.fileFormat - */ - public var fileFormat: String = "" - - /** - * @see ShortVideo.filename - */ - public var fileName: String = "" - - /** - * 缩略图文件 MD5,仅通过 [Contact.uploadShortVideo] 获取的 [OfflineShortVideo] 或 [OnlineShortVideo] 的 mirai 码获取. - */ - public var thumbnailMd5: ByteArray = EMPTY_BYTE_ARRAY - - /** - * 缩略图文件大小,仅通过 [Contact.uploadShortVideo] 获取的 [OfflineShortVideo] 或 [OnlineShortVideo] 的 mirai 码获取. - */ - public var thumbnailSize: Long = 0 - - public fun build(): ShortVideo { - if (videoId.isEmpty()) { - throw IllegalArgumentException("videoId is empty.") - } - if (fileMd5.contentEquals(EMPTY_BYTE_ARRAY)) { - throw IllegalArgumentException("fileMd5 is empty.") - } - if (fileSize == 0L) { - throw IllegalArgumentException("fileSize is zero.") - } - if (fileFormat.isEmpty()) { - throw IllegalArgumentException("fileFormat is empty.") - } - if (fileName.isEmpty()) { - throw IllegalArgumentException("fileName is empty.") - } - - @OptIn(MiraiInternalApi::class) - return InternalShortVideoProtocol.instance.createOfflineShortVideo( - videoId, fileMd5, fileSize, fileFormat, fileName, thumbnailMd5, thumbnailSize - ) - } - - public companion object { - /** - * 创建一个 [Builder] - */ - @JvmStatic - public fun newBuilder(videoId: String): Builder = Builder(videoId) - } - } } /** - * 在线短视频消息,即从消息事件中接收到的视频消息. + * 在线短视频消息, 即从消息事件中接收到的视频消息. * * [OnlineShortVideo] 仅可以从事件中的[消息链][MessageChain]接收, 不可手动构造. * @@ -203,7 +135,7 @@ public interface OnlineShortVideo : ShortVideo { * * [OfflineShortVideo] 可由本地 [ExternalResource] 经过 [AudioSupported.uploadShortVideo] 上传到服务器得到, 故无[下载链接][OnlineShortVideo.urlForDownload]. * - * [OfflineShortVideo] 支持使用 [ShortVideo.Builder] 可通过上述七个参数获得 [OfflineShortVideo] 实例. + * [OfflineShortVideo] 支持使用 [OfflineShortVideo.Builder] 可通过上述七个必要参数和两个非必要参数构造 [OfflineShortVideo] 实例. * * ### 序列化支持 * @@ -220,8 +152,75 @@ public interface OfflineShortVideo : ShortVideo { AbstractPolymorphicMessageKey(ShortVideo, { it.safeCast() }) { public const val SERIAL_NAME: String = "OfflineShortVideo" } + + public class Builder internal constructor( + public var videoId: String, + public var fileMd5: ByteArray, + public var fileSize: Long, + public var fileFormat: String, + public var fileName: String + ) { + /** + * 缩略图文件 MD5 + * + * 传入此处的缩略图 MD5 应该仅有以下来源: + * * *已通过 [Contact.uploadShortVideo] 上传完成的*缩略图[资源][ExternalResource], 可由 [ExternalResource.md5] 获得. + */ + public var thumbnailMd5: ByteArray = EMPTY_BYTE_ARRAY + + /** + * 缩略图文件大小 + * + * 传入此处的缩略图文件大小应该仅有以下来源: + * * *已通过 [Contact.uploadShortVideo] 上传完成的*缩略图[资源][ExternalResource], 可由 [ExternalResource.size] 获得. + */ + public var thumbnailSize: Long = 0 + + public fun build(): OfflineShortVideo { + + @OptIn(MiraiInternalApi::class) + return InternalShortVideoProtocol.instance.createOfflineShortVideo( + videoId, fileMd5, fileSize, fileFormat, fileName, thumbnailMd5, thumbnailSize + ) + } + + public companion object { + /** + * 创建一个 [OfflineShortVideo.Builder] + * + * 在 Kotlin 可以使用类构造器的函数 [OfflineShortVideo]: `OfflineShortVideo(...)` + */ + @JvmStatic + public fun newBuilder( + videoId: String, + fileMd5: ByteArray, + fileSize: Long, + fileFormat: String, + fileName: String + ): Builder = Builder(videoId, fileMd5, fileSize, fileFormat, fileName) + } + } } +/** + * 构造 [OfflineShortVideo]. 有关参数的含义, 参考 [ShortVideo]. + * @since 2.16 + */ +@Suppress("NOTHING_TO_INLINE") +@JvmSynthetic +public inline fun OfflineShortVideo( + videoId: String, + fileMd5: ByteArray, + fileSize: Long, + fileFormat: String, + fileName: String, + thumbnailMd5: ByteArray = byteArrayOf(), + thumbnailSize: Long = 0, +): OfflineShortVideo = OfflineShortVideo.Builder.newBuilder(videoId, fileMd5, fileSize, fileFormat, fileName).apply { + this@apply.thumbnailMd5 = thumbnailMd5 + this@apply.thumbnailSize = thumbnailSize +}.build() + /** * 内部短视频协议实现, 请不要使用此接口 * @since 2.16.0 From 413e97e29f85dbe76e4f0e0cca195651a2352f89 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 22:11:28 +0800 Subject: [PATCH 32/43] optimize RefineContext --- mirai-core/src/commonMain/kotlin/MiraiImpl.kt | 4 +-- .../roaming/RoamingMessagesImplGroup.kt | 8 +++--- .../roaming/TimeBasedRoamingMessagesImpl.kt | 27 ++++--------------- .../kotlin/message/ReceiveMessageHandler.kt | 6 ++--- .../kotlin/message/RefinableMessage.kt | 3 +++ .../message/protocol/MessageProtocolFacade.kt | 11 +++----- .../message/source/offlineSourceImpl.kt | 5 +++- .../notice/priv/PrivateMessageProcessor.kt | 8 +++--- .../kotlin/message/data/MessageRefineTest.kt | 2 +- 9 files changed, 25 insertions(+), 49 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt index 2b1ef2025d..926cfad0fc 100644 --- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt @@ -723,9 +723,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { it.fileName to it.buffer.loadAs(MsgTransmit.PbMultiMsgNew.serializer()) } val main = pbs["MultiMsg"] ?: return this.msg.map { it.toNode(bot, EmptyRefineContext) } - val context = SimpleRefineContext(mutableMapOf()) - context[ForwardMessageInternal.MsgTransmits] = pbs - return main.toForwardMessageNodes(bot, context) + return main.toForwardMessageNodes(bot, SimpleRefineContext(ForwardMessageInternal.MsgTransmits to pbs)) } private suspend fun MsgComm.Msg.toNode(bot: Bot, refineContext: RefineContext): ForwardMessage.Node { diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt index 88ac2251f1..b73fdbac06 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImplGroup.kt @@ -73,11 +73,9 @@ internal class RoamingMessagesImplGroup( contact.id, MessageSourceKind.GROUP, SimpleRefineContext( - mutableMapOf( - RefineContextKey.MessageSourceKind to MessageSourceKind.GROUP, - RefineContextKey.FromId to it.msgHead.fromUin, - RefineContextKey.GroupIdOrZero to contact.uin, - ) + RefineContextKey.MessageSourceKind to MessageSourceKind.GROUP, + RefineContextKey.FromId to it.msgHead.fromUin, + RefineContextKey.GroupIdOrZero to contact.uin, ) ) } diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt index b4f1f88e60..05661b4470 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/TimeBasedRoamingMessagesImpl.kt @@ -21,7 +21,6 @@ import net.mamoe.mirai.internal.message.SimpleRefineContext 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( @@ -38,31 +37,15 @@ internal sealed class TimeBasedRoamingMessagesImpl : AbstractRoamingMessages() { if (filter == null || filter === RoamingMessageFilter.ANY) { // fast path - messages.forEach { - emit( - it.toMessageChainOnline( - contact.bot, - refineContext = SimpleRefineContext( - mutableListOf( - RefineContextKey.FromId to it.msgHead.fromUin - ).cast() - ) - ) - ) + messages.forEach { msg -> + val context = SimpleRefineContext(RefineContextKey.FromId to msg.msgHead.fromUin) + emit(msg.toMessageChainOnline(contact.bot, context)) } } else { for (message in messages) { if (filter.invoke(createRoamingMessage(message, messages))) { - emit( - message.toMessageChainOnline( - contact.bot, - refineContext = SimpleRefineContext( - mutableListOf( - RefineContextKey.FromId to message.msgHead.fromUin - ).cast() - ) - ) - ) + val context = SimpleRefineContext(RefineContextKey.FromId to message.msgHead.fromUin) + emit(message.toMessageChainOnline(contact.bot, context)) } } } diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index 0765ba620f..98da9df5c7 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -36,13 +36,12 @@ 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, fromId, bot, this, null) + facade.decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) }.cleanupRubbishMessageElements().refineLight(bot, refineContext) } @@ -92,7 +91,7 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline( groupId, kind, // TODO: it is better to add `RefineContext.merge(other, override)` to merge with another refine context - (refineContext.castOrNull() ?: SimpleRefineContext(mutableMapOf())) + (refineContext.castOrNull() ?: SimpleRefineContext()) .apply(mutableRefineContextApplier), facade ) @@ -164,7 +163,6 @@ private fun List.toMessageChainImpl( msg.msgBody.richText.elems, groupIdOrZero, messageSourceKind, - fromId ?: 0, bot, builder, msg diff --git a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt index d7c9b627fe..6b236351e9 100644 --- a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt +++ b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt @@ -148,6 +148,9 @@ internal class SimpleRefineContext( } } +internal fun SimpleRefineContext(vararg elements: Pair, Any>): SimpleRefineContext = + SimpleRefineContext(elements.toMap().toMutableMap()) + /** * 执行不需要 `suspend` 的 refine. 用于 [MessageSource.originalMessage]. */ diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt index f2905115ed..5a96dfe220 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt @@ -77,7 +77,6 @@ internal interface MessageProtocolFacade { elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, - fromId: Long, bot: Bot, builder: MessageChainBuilder, containingMsg: MsgComm.Msg? = null, @@ -135,10 +134,9 @@ internal interface MessageProtocolFacade { elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, - fromId: Long, bot: Bot, ): MessageChain = buildMessageChain { - decode(elements, groupIdOrZero, messageSourceKind, fromId, bot, this, null) + decode(elements, groupIdOrZero, messageSourceKind, bot, this, null) } @@ -175,19 +173,17 @@ internal fun MessageProtocolFacade.decodeAndRefineLight( elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, - fromId: Long, bot: Bot, refineContext: RefineContext = EmptyRefineContext -): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, fromId, bot).refineLight(bot, refineContext) +): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineLight(bot, refineContext) internal suspend fun MessageProtocolFacade.decodeAndRefineDeep( elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, - fromId: Long, bot: Bot, refineContext: RefineContext = EmptyRefineContext -): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, fromId, bot).refineDeep(bot, refineContext) +): MessageChain = decode(elements, groupIdOrZero, messageSourceKind, bot).refineDeep(bot, refineContext) private const val errorTips = @@ -294,7 +290,6 @@ internal class MessageProtocolFacadeImpl( elements: List, groupIdOrZero: Long, messageSourceKind: MessageSourceKind, - fromId: Long, bot: Bot, builder: MessageChainBuilder, containingMsg: MsgComm.Msg? diff --git a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt index c06bb954ca..2a4e643ef5 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt @@ -16,6 +16,8 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.internal.message.MessageSourceSerializerImpl +import net.mamoe.mirai.internal.message.RefineContextKey +import net.mamoe.mirai.internal.message.SimpleRefineContext import net.mamoe.mirai.internal.message.toMessageChainNoSource import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm @@ -184,7 +186,8 @@ internal fun OfflineMessageSourceImplData( .origUids?.mapToIntArray { it.toInt() } ?: intArrayOf(), time = delegate.time, originalMessageLazy = lazy { - delegate.toMessageChainNoSource(bot, messageSourceKind, groupIdOrZero, delegate.senderUin) + val context = SimpleRefineContext(RefineContextKey.FromId to delegate.senderUin) + delegate.toMessageChainNoSource(bot, messageSourceKind, groupIdOrZero, context) }, fromId = delegate.senderUin, targetId = when { 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 ca55935492..ba0a27a163 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/priv/PrivateMessageProcessor.kt @@ -138,11 +138,9 @@ internal class PrivateMessageProcessor : SimpleNoticeProcessor(type 0, user.correspondingMessageSourceKind, SimpleRefineContext( - mutableMapOf( - RefineContextKey.MessageSourceKind to MessageSourceKind.FRIEND, - RefineContextKey.FromId to user.uin, - RefineContextKey.GroupIdOrZero to 0L, - ) + RefineContextKey.MessageSourceKind to MessageSourceKind.FRIEND, + RefineContextKey.FromId to user.uin, + RefineContextKey.GroupIdOrZero to 0L, ) ) val time = msgHead.msgTime diff --git a/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt b/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt index 718165d0f5..f735a03ce1 100644 --- a/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/data/MessageRefineTest.kt @@ -316,7 +316,7 @@ private fun sourceStub( private suspend fun testRecursiveRefine(list: List, expected: MessageChain, isLight: Boolean) { val actual = buildMessageChain { - MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, 0L, bot, this, null) + MessageProtocolFacade.decode(list, 0, MessageSourceKind.GROUP, bot, this, null) }.let { c -> if (isLight) { c.refineLight(bot) From e6bafd3513c050bfdf2cc5fd5605fdf1854405e7 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 22:45:35 +0800 Subject: [PATCH 33/43] RefineContext.merge --- .../kotlin/message/ReceiveMessageHandler.kt | 12 ++--- .../kotlin/message/RefinableMessage.kt | 22 +++++++++ .../kotlin/message/RefineContextTest.kt | 49 +++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index 98da9df5c7..4ad63f9393 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -81,18 +81,14 @@ internal suspend fun MsgComm.Msg.toMessageChainOnline( else -> 0 } - val mutableRefineContextApplier: MutableRefineContext.() -> Unit = { - set(RefineContextKey.MessageSourceKind, kind) - set(RefineContextKey.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() ?: SimpleRefineContext()) - .apply(mutableRefineContextApplier), + refineContext.merge(SimpleRefineContext( + RefineContextKey.MessageSourceKind to kind, + RefineContextKey.GroupIdOrZero to groupId + ), false), facade ) } diff --git a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt index 6b236351e9..0f557bb665 100644 --- a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt +++ b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt @@ -16,6 +16,7 @@ import net.mamoe.mirai.internal.message.LightMessageRefiner.refineMessageSource import net.mamoe.mirai.internal.message.flags.InternalFlagOnlyMessage import net.mamoe.mirai.internal.message.source.IncomingMessageSourceInternal import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.cast import net.mamoe.mirai.utils.safeCast /** @@ -114,6 +115,8 @@ internal interface RefineContext { operator fun contains(key: RefineContextKey<*>): Boolean operator fun get(key: RefineContextKey): T? fun getNotNull(key: RefineContextKey): T = get(key) ?: error("No such value of `$key`") + fun merge(other: RefineContext, override: Boolean): RefineContext + fun entries(): Set, Any>> } internal interface MutableRefineContext : RefineContext { @@ -124,6 +127,12 @@ internal interface MutableRefineContext : RefineContext { internal object EmptyRefineContext : RefineContext { override fun contains(key: RefineContextKey<*>): Boolean = false override fun get(key: RefineContextKey): T? = null + override fun merge(other: RefineContext, override: Boolean): RefineContext { + return other + } + override fun entries(): Set, Any>> { + return emptySet() + } override fun toString(): String { return "EmptyRefineContext" } @@ -146,6 +155,19 @@ internal class SimpleRefineContext( override fun remove(key: RefineContextKey<*>) { delegate.remove(key) } + + override fun entries(): Set, Any>> { + return delegate.entries.map { (k, v) -> k to v }.toSet() + } + + override fun merge(other: RefineContext, override: Boolean): RefineContext { + other.entries().forEach { (key, value) -> + if (get(key) == null || override) { + set(key as RefineContextKey, value) + } + } + return this + } } internal fun SimpleRefineContext(vararg elements: Pair, Any>): SimpleRefineContext = diff --git a/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt b/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt new file mode 100644 index 0000000000..7f62040ed4 --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt @@ -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.internal.message + +import net.mamoe.mirai.internal.test.AbstractTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +internal class RefineContextTest : AbstractTest() { + @Test + fun `merge test`() { + val Key1 = RefineContextKey("KeyInt") + val Key2 = RefineContextKey("KeyDouble") + val Key3 = RefineContextKey("KeyString") + val Key4 = RefineContextKey("KeyBytes") + + val context1 = SimpleRefineContext( + Key1 to 114514, + Key2 to 1919.810, + Key3 to "sodayo" + ) + + val context2 = SimpleRefineContext( + Key2 to 1919.811, + Key3 to "yarimasune", + Key4 to byteArrayOf(11, 45, 14) + ) + + val combinedOverride = context1.merge(context2, override = true) + val combinedNotOverride = context1.merge(context2, override = false) + + assertEquals(context1.merge(context2, override = true), context2.merge(context1, override = true)) + assertFalse(context1.merge(context2, override = false) != context2.merge(context1, override = false)) + + assertTrue(combinedOverride.entries().size == 5) + assertTrue(combinedOverride[Key2] == 1919.811) + assertTrue(combinedNotOverride[Key2] == 1919.810) + assertTrue(byteArrayOf(11, 45, 14).contentEquals(combinedNotOverride[Key4])) + } +} \ No newline at end of file From fafb134c82aa73d05bbc09f8d8f875a38dadb268 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 22:46:02 +0800 Subject: [PATCH 34/43] dump api --- .../android/api/android.api | 54 ++++++++++--------- .../compatibility-validation/jvm/api/jvm.api | 54 ++++++++++--------- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 6f31f01cce..f1b01b8f45 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -4869,6 +4869,30 @@ public abstract interface class net/mamoe/mirai/message/data/OfflineShortVideo : public static final field SERIAL_NAME Ljava/lang/String; } +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { + public static final field Companion Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion; + public final fun build ()Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public final fun getFileFormat ()Ljava/lang/String; + public final fun getFileMd5 ()[B + public final fun getFileName ()Ljava/lang/String; + public final fun getFileSize ()J + public final fun getThumbnailMd5 ()[B + public final fun getThumbnailSize ()J + public final fun getVideoId ()Ljava/lang/String; + public static final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public final fun setFileFormat (Ljava/lang/String;)V + public final fun setFileMd5 ([B)V + public final fun setFileName (Ljava/lang/String;)V + public final fun setFileSize (J)V + public final fun setThumbnailMd5 ([B)V + public final fun setThumbnailSize (J)V + public final fun setVideoId (Ljava/lang/String;)V +} + +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion { + public final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; +} + public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { public static final field SERIAL_NAME Ljava/lang/String; } @@ -5280,38 +5304,18 @@ public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/ma public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; public abstract fun getFileFormat ()Ljava/lang/String; public abstract fun getFileMd5 ()[B - public abstract fun getFileName ()Ljava/lang/String; public abstract fun getFileSize ()J + public abstract fun getFilename ()Ljava/lang/String; public fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; public abstract fun getVideoId ()Ljava/lang/String; } -public final class net/mamoe/mirai/message/data/ShortVideo$Builder { - public static final field Companion Lnet/mamoe/mirai/message/data/ShortVideo$Builder$Companion; - public fun ()V - public final fun build ()Lnet/mamoe/mirai/message/data/ShortVideo; - public final fun getFileFormat ()Ljava/lang/String; - public final fun getFileMd5 ()[B - public final fun getFileName ()Ljava/lang/String; - public final fun getFileSize ()J - public final fun getThumbnailMd5 ()[B - public final fun getThumbnailSize ()J - public final fun getVideoId ()Ljava/lang/String; - public static final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; - public final fun setFileFormat (Ljava/lang/String;)V - public final fun setFileMd5 ([B)V - public final fun setFileName (Ljava/lang/String;)V - public final fun setFileSize (J)V - public final fun setThumbnailMd5 ([B)V - public final fun setThumbnailSize (J)V - public final fun setVideoId (Ljava/lang/String;)V -} - -public final class net/mamoe/mirai/message/data/ShortVideo$Builder$Companion { - public final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; +public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } -public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { +public final class net/mamoe/mirai/message/data/ShortVideoKt { + public static final synthetic fun OfflineShortVideo (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; } public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 93e19db5b7..227bdc2e95 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -4869,6 +4869,30 @@ public abstract interface class net/mamoe/mirai/message/data/OfflineShortVideo : public static final field SERIAL_NAME Ljava/lang/String; } +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { + public static final field Companion Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion; + public final fun build ()Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public final fun getFileFormat ()Ljava/lang/String; + public final fun getFileMd5 ()[B + public final fun getFileName ()Ljava/lang/String; + public final fun getFileSize ()J + public final fun getThumbnailMd5 ()[B + public final fun getThumbnailSize ()J + public final fun getVideoId ()Ljava/lang/String; + public static final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public final fun setFileFormat (Ljava/lang/String;)V + public final fun setFileMd5 ([B)V + public final fun setFileName (Ljava/lang/String;)V + public final fun setFileSize (J)V + public final fun setThumbnailMd5 ([B)V + public final fun setThumbnailSize (J)V + public final fun setVideoId (Ljava/lang/String;)V +} + +public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion { + public final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; +} + public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { public static final field SERIAL_NAME Ljava/lang/String; } @@ -5280,38 +5304,18 @@ public abstract interface class net/mamoe/mirai/message/data/ShortVideo : net/ma public static final field Key Lnet/mamoe/mirai/message/data/ShortVideo$Key; public abstract fun getFileFormat ()Ljava/lang/String; public abstract fun getFileMd5 ()[B - public abstract fun getFileName ()Ljava/lang/String; public abstract fun getFileSize ()J + public abstract fun getFilename ()Ljava/lang/String; public fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey; public abstract fun getVideoId ()Ljava/lang/String; } -public final class net/mamoe/mirai/message/data/ShortVideo$Builder { - public static final field Companion Lnet/mamoe/mirai/message/data/ShortVideo$Builder$Companion; - public fun ()V - public final fun build ()Lnet/mamoe/mirai/message/data/ShortVideo; - public final fun getFileFormat ()Ljava/lang/String; - public final fun getFileMd5 ()[B - public final fun getFileName ()Ljava/lang/String; - public final fun getFileSize ()J - public final fun getThumbnailMd5 ()[B - public final fun getThumbnailSize ()J - public final fun getVideoId ()Ljava/lang/String; - public static final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; - public final fun setFileFormat (Ljava/lang/String;)V - public final fun setFileMd5 ([B)V - public final fun setFileName (Ljava/lang/String;)V - public final fun setFileSize (J)V - public final fun setThumbnailMd5 ([B)V - public final fun setThumbnailSize (J)V - public final fun setVideoId (Ljava/lang/String;)V -} - -public final class net/mamoe/mirai/message/data/ShortVideo$Builder$Companion { - public final fun newBuilder (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo$Builder; +public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { } -public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { +public final class net/mamoe/mirai/message/data/ShortVideoKt { + public static final synthetic fun OfflineShortVideo (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; } public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { From a560b4199cf4bd1ee4da0cbfa2d6be5ae36548f1 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sat, 12 Aug 2023 23:37:22 +0800 Subject: [PATCH 35/43] fix test --- .../kotlin/message/RefinableMessage.kt | 18 +++++++++++++++--- .../kotlin/message/RefineContextTest.kt | 16 +++++++++++----- .../impl/AbstractMessageProtocolTest.kt | 1 - .../message/protocol/impl/FaceProtocolTest.kt | 1 - 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt index 0f557bb665..05e5fecfdf 100644 --- a/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt +++ b/mirai-core/src/commonMain/kotlin/message/RefinableMessage.kt @@ -136,6 +136,10 @@ internal object EmptyRefineContext : RefineContext { override fun toString(): String { return "EmptyRefineContext" } + + override fun equals(other: Any?): Boolean { + return other === EmptyRefineContext + } } @Suppress("UNCHECKED_CAST") @@ -161,12 +165,20 @@ internal class SimpleRefineContext( } override fun merge(other: RefineContext, override: Boolean): RefineContext { + val new = SimpleRefineContext(*entries().toTypedArray()) other.entries().forEach { (key, value) -> - if (get(key) == null || override) { - set(key as RefineContextKey, value) + if (new[key] == null || override) { + new[key as RefineContextKey] = value } } - return this + return new + } + + override fun equals(other: Any?): Boolean { + if (other !is RefineContext) return false + if (other === this) return true + + return other.entries() == entries() } } diff --git a/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt b/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt index 7f62040ed4..28cca2ff5e 100644 --- a/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/RefineContextTest.kt @@ -38,12 +38,18 @@ internal class RefineContextTest : AbstractTest() { val combinedOverride = context1.merge(context2, override = true) val combinedNotOverride = context1.merge(context2, override = false) - assertEquals(context1.merge(context2, override = true), context2.merge(context1, override = true)) - assertFalse(context1.merge(context2, override = false) != context2.merge(context1, override = false)) + val context3 = SimpleRefineContext( + Key2 to 1919.811, + Key3 to "yarimasune" + ) + + assertEquals(context1, context1.merge(context3, false)) + assertTrue(combinedOverride != combinedNotOverride) - assertTrue(combinedOverride.entries().size == 5) - assertTrue(combinedOverride[Key2] == 1919.811) - assertTrue(combinedNotOverride[Key2] == 1919.810) + assertEquals(4, combinedOverride.entries().size) + assertEquals(1919.811, combinedOverride[Key2]) + assertEquals(1919.810, combinedNotOverride[Key2]) + assertEquals("sodayo", combinedNotOverride[Key3]) assertTrue(byteArrayOf(11, 45, 14).contentEquals(combinedNotOverride[Key4])) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt index 7b0ae24890..5c2733ebde 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt @@ -240,7 +240,6 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler config.elems, config.groupIdOrZero, config.messageSourceKind, - config.target?.id ?: 0L, bot ) } diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt index 245d9c7df1..2980b4ec8a 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt @@ -57,7 +57,6 @@ internal class FaceProtocolTest : AbstractMessageProtocolTest() { ), groupIdOrZero = 0, MessageSourceKind.GROUP, - fromId = 0L, bot, ) } From 76391f36546e1ee0498d23991d7ab93481b8fe7b Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 13 Aug 2023 15:54:10 +0800 Subject: [PATCH 36/43] show more video info --- .../src/commonMain/kotlin/message/data/shortVideo.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index d96039739c..f0d563cc3b 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -125,8 +125,8 @@ internal class OnlineShortVideoImpl( ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { override fun toString(): String { - return "[mirai:shortvideo:$filename.$fileFormat, video=${fileMd5.toUHexString("")}, videoSize=${fileSize}, " + - "thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" + return "[mirai:shortvideo:$videoId, videoName=$filename.$fileFormat, videoMd5=${fileMd5.toUHexString("")}, " + + "videoSize=${fileSize}, thumbnailMd5=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } override fun contentToString(): String { @@ -185,8 +185,8 @@ internal class OfflineShortVideoImpl( * offline short video uses */ override fun toString(): String { - return "[mirai:shortvideo:$filename.$fileFormat, video=${fileMd5.toUHexString("")}, " + - "videoSize=${fileSize}, thumbnail=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" + return "[mirai:shortvideo:$videoId, videoName=$filename.$fileFormat, videoMd5=${fileMd5.toUHexString("")}, " + + "videoSize=${fileSize}, thumbnailMd5=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" } override fun contentToString(): String { From 138a679085a54b4297a4986312dffdf311595216 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 13 Aug 2023 16:00:38 +0800 Subject: [PATCH 37/43] optimize constructor and builder of offline short video --- .../android/api/android.api | 8 ++++---- .../compatibility-validation/jvm/api/jvm.api | 8 ++++---- .../kotlin/message/data/ShortVideo.kt | 12 ++++++------ .../message/protocol/impl/ShortVideoProtocol.kt | 17 ++++++++++++++++- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index f1b01b8f45..5d02f51b30 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -4879,7 +4879,7 @@ public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { public final fun getThumbnailMd5 ()[B public final fun getThumbnailSize ()J public final fun getVideoId ()Ljava/lang/String; - public static final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public static final fun newBuilder (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; public final fun setFileFormat (Ljava/lang/String;)V public final fun setFileMd5 ([B)V public final fun setFileName (Ljava/lang/String;)V @@ -4890,7 +4890,7 @@ public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { } public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion { - public final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public final fun newBuilder (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; } public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { @@ -5314,8 +5314,8 @@ public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai } public final class net/mamoe/mirai/message/data/ShortVideoKt { - public static final synthetic fun OfflineShortVideo (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; - public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static final synthetic fun OfflineShortVideo (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; } public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 227bdc2e95..0a70f4f023 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -4879,7 +4879,7 @@ public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { public final fun getThumbnailMd5 ()[B public final fun getThumbnailSize ()J public final fun getVideoId ()Ljava/lang/String; - public static final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public static final fun newBuilder (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; public final fun setFileFormat (Ljava/lang/String;)V public final fun setFileMd5 ([B)V public final fun setFileName (Ljava/lang/String;)V @@ -4890,7 +4890,7 @@ public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder { } public final class net/mamoe/mirai/message/data/OfflineShortVideo$Builder$Companion { - public final fun newBuilder (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; + public final fun newBuilder (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo$Builder; } public final class net/mamoe/mirai/message/data/OfflineShortVideo$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { @@ -5314,8 +5314,8 @@ public final class net/mamoe/mirai/message/data/ShortVideo$Key : net/mamoe/mirai } public final class net/mamoe/mirai/message/data/ShortVideoKt { - public static final synthetic fun OfflineShortVideo (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; - public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;[BJLjava/lang/String;Ljava/lang/String;[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static final synthetic fun OfflineShortVideo (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ[BJ)Lnet/mamoe/mirai/message/data/OfflineShortVideo; + public static synthetic fun OfflineShortVideo$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[BJ[BJILjava/lang/Object;)Lnet/mamoe/mirai/message/data/OfflineShortVideo; } public final class net/mamoe/mirai/message/data/ShowImageFlag : net/mamoe/mirai/message/data/AbstractMessageKey, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt index 83e12e1684..f6a4bde8fc 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ShortVideo.kt @@ -193,10 +193,10 @@ public interface OfflineShortVideo : ShortVideo { @JvmStatic public fun newBuilder( videoId: String, - fileMd5: ByteArray, - fileSize: Long, + fileName: String, fileFormat: String, - fileName: String + fileMd5: ByteArray, + fileSize: Long ): Builder = Builder(videoId, fileMd5, fileSize, fileFormat, fileName) } } @@ -210,13 +210,13 @@ public interface OfflineShortVideo : ShortVideo { @JvmSynthetic public inline fun OfflineShortVideo( videoId: String, + fileName: String, + fileFormat: String, fileMd5: ByteArray, fileSize: Long, - fileFormat: String, - fileName: String, thumbnailMd5: ByteArray = byteArrayOf(), thumbnailSize: Long = 0, -): OfflineShortVideo = OfflineShortVideo.Builder.newBuilder(videoId, fileMd5, fileSize, fileFormat, fileName).apply { +): OfflineShortVideo = OfflineShortVideo.Builder.newBuilder(videoId, fileName, fileFormat, fileMd5, fileSize).apply { this@apply.thumbnailMd5 = thumbnailMd5 this@apply.thumbnailSize = thumbnailSize }.build() diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 445828a38a..568703a140 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -60,7 +60,7 @@ internal class ShortVideoProtocol : MessageProtocol() { fileUuid = data.videoId.encodeToByteArray(), fileMd5 = data.fileMd5, fileName = data.filename.encodeToByteArray(), - fileFormat = 3, // TODO: only support mp4 now + fileFormat = data.fileFormat.toVideoFormat(), fileTime = 10, fileSize = data.fileSize.toInt(), thumbWidth = thumbWidth, @@ -79,4 +79,19 @@ internal class ShortVideoProtocol : MessageProtocol() { } } +} + +private fun String.toVideoFormat() = when (this) { + "ts" -> 1 + "avi" -> 2 + "mp4" -> 3 + "wmv" -> 4 + "mkv" -> 5 + "rmvb" -> 6 + "rm" -> 7 + "afs" -> 8 + "mov" -> 9 + "mod" -> 10 + "mts" -> 11 + else -> -1 // unknown to default } \ No newline at end of file From 12a40ef8e576e830cac7dd38c1e5398af92fbe75 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 16 Aug 2023 22:41:41 +0800 Subject: [PATCH 38/43] optimize thumbnail --- .../kotlin/message/data/shortVideo.kt | 35 ++++++++++++------- .../protocol/impl/ShortVideoProtocol.kt | 8 ++--- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index f0d563cc3b..45f68b7fc4 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -101,11 +101,16 @@ internal class OnlineShortVideoMsgInternal( } } +@Serializable +internal class ShortVideoThumbnail( + val md5: ByteArray, + val size: Long, + val width: Int?, + val height: Int?, +) + internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { - abstract val thumbMd5: ByteArray - abstract val thumbSize: Long - abstract val thumbWidth: Int? - abstract val thumbHeight: Int? + abstract val thumbnail: ShortVideoThumbnail } @Suppress("DuplicatedCode") @@ -118,11 +123,14 @@ internal class OnlineShortVideoImpl( override val fileSize: Long, override val fileFormat: String, override val urlForDownload: String, - override val thumbMd5: ByteArray, - override val thumbSize: Long, - @Transient override val thumbWidth: Int = 0, - @Transient override val thumbHeight: Int = 0 + val thumbMd5: ByteArray, + val thumbSize: Long, + @Transient val thumbWidth: Int = 0, + @Transient val thumbHeight: Int = 0 ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { + override val thumbnail: ShortVideoThumbnail by lazy { + ShortVideoThumbnail(thumbMd5, thumbSize, thumbWidth, thumbHeight) + } override fun toString(): String { return "[mirai:shortvideo:$videoId, videoName=$filename.$fileFormat, videoMd5=${fileMd5.toUHexString("")}, " + @@ -175,11 +183,14 @@ internal class OfflineShortVideoImpl( override val fileMd5: ByteArray, override val fileSize: Long, override val fileFormat: String, - override val thumbMd5: ByteArray, - override val thumbSize: Long, - @Transient override val thumbWidth: Int = 0, - @Transient override val thumbHeight: Int = 0 + val thumbMd5: ByteArray, + val thumbSize: Long, + @Transient val thumbWidth: Int = 0, + @Transient val thumbHeight: Int = 0 ) : OfflineShortVideo, AbstractShortVideoWithThumbnail() { + override val thumbnail: ShortVideoThumbnail by lazy { + ShortVideoThumbnail(thumbMd5, thumbSize, thumbWidth, thumbHeight) + } /** * offline short video uses diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index 568703a140..e5601a2649 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -51,8 +51,8 @@ internal class ShortVideoProtocol : MessageProtocol() { collect(ImMsgBody.Elem(text = ImMsgBody.Text("你的 QQ 暂不支持查看视频短片,请期待后续版本。"))) - val thumbWidth = if (data.thumbWidth == null || data.thumbWidth == 0) 1280 else data.thumbWidth!! - val thumbHeight = if (data.thumbHeight == null || data.thumbHeight == 0) 720 else data.thumbHeight!! + val thumbWidth = if (data.thumbnail.width == null || data.thumbnail.width == 0) 1280 else data.thumbnail.width!! + val thumbHeight = if (data.thumbnail.height == null || data.thumbnail.height == 0) 720 else data.thumbnail.height!! collect( ImMsgBody.Elem( @@ -65,8 +65,8 @@ internal class ShortVideoProtocol : MessageProtocol() { fileSize = data.fileSize.toInt(), thumbWidth = thumbWidth, thumbHeight = thumbHeight, - thumbFileMd5 = data.thumbMd5, - thumbFileSize = data.thumbSize.toInt(), + thumbFileMd5 = data.thumbnail.md5, + thumbFileSize = data.thumbnail.size.toInt(), busiType = 0, fromChatType = -1, toChatType = -1, From f3c422266aee2ec1a46eb52f7d3d667f40e0b088 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 16 Aug 2023 23:14:30 +0800 Subject: [PATCH 39/43] move thumbnail to main constructor arg --- .../kotlin/contact/AbstractContact.kt | 21 +++-- .../kotlin/message/data/shortVideo.kt | 76 +++++++++---------- .../image/InternalShortVideoProtocolImpl.kt | 11 ++- 3 files changed, 58 insertions(+), 50 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt index 6f53c221f1..dfb58c74da 100644 --- a/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt +++ b/mirai-core/src/commonMain/kotlin/contact/AbstractContact.kt @@ -18,6 +18,7 @@ import net.mamoe.mirai.event.events.EventCancelledException import net.mamoe.mirai.event.events.ShortVideoUploadEvent import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.internal.message.data.ShortVideoThumbnail import net.mamoe.mirai.internal.message.image.calculateImageInfo import net.mamoe.mirai.internal.network.highway.Highway import net.mamoe.mirai.internal.network.highway.ResourceKind @@ -82,10 +83,12 @@ internal abstract class AbstractContact( video.md5, video.size, video.formatName, - thumbnail.md5, - thumbnail.size, - thumbnailInfo.width, - thumbnailInfo.height + ShortVideoThumbnail( + thumbnail.md5, + thumbnail.size, + thumbnailInfo.width, + thumbnailInfo.height + ) ).also { ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() } @@ -135,10 +138,12 @@ internal abstract class AbstractContact( video.md5, video.size, video.formatName, - thumbnail.md5, - thumbnail.size, - thumbnailInfo.width, - thumbnailInfo.height + ShortVideoThumbnail( + thumbnail.md5, + thumbnail.size, + thumbnailInfo.width, + thumbnailInfo.height + ) ).also { ShortVideoUploadEvent.Succeed(this, thumbnail, video, it).broadcast() } diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 45f68b7fc4..3b5bd193fa 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -84,10 +84,12 @@ internal class OnlineShortVideoMsgInternal( videoFile.fileSize.toLong(), videoFile.fileFormat.toVideoFormat(), shortVideoDownloadReq.urlV4, - videoFile.thumbFileMd5, - videoFile.thumbFileSize.toLong(), - videoFile.thumbWidth, - videoFile.thumbHeight + ShortVideoThumbnail( + videoFile.thumbFileMd5, + videoFile.thumbFileSize.toLong(), + videoFile.thumbWidth, + videoFile.thumbHeight + ) ) } @@ -102,12 +104,34 @@ internal class OnlineShortVideoMsgInternal( } @Serializable -internal class ShortVideoThumbnail( +internal data class ShortVideoThumbnail( val md5: ByteArray, val size: Long, val width: Int?, val height: Int?, -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ShortVideoThumbnail + + if (!md5.contentEquals(other.md5)) return false + if (size != other.size) return false + if (width != other.width) return false + if (height != other.height) return false + + return true + } + + override fun hashCode(): Int { + var result = md5.contentHashCode() + result = 31 * result + size.hashCode() + result = 31 * result + (width ?: 0) + result = 31 * result + (height ?: 0) + return result + } +} internal abstract class AbstractShortVideoWithThumbnail : ShortVideo { abstract val thumbnail: ShortVideoThumbnail @@ -123,18 +147,12 @@ internal class OnlineShortVideoImpl( override val fileSize: Long, override val fileFormat: String, override val urlForDownload: String, - val thumbMd5: ByteArray, - val thumbSize: Long, - @Transient val thumbWidth: Int = 0, - @Transient val thumbHeight: Int = 0 + override val thumbnail: ShortVideoThumbnail ) : OnlineShortVideo, AbstractShortVideoWithThumbnail() { - override val thumbnail: ShortVideoThumbnail by lazy { - ShortVideoThumbnail(thumbMd5, thumbSize, thumbWidth, thumbHeight) - } override fun toString(): String { return "[mirai:shortvideo:$videoId, videoName=$filename.$fileFormat, videoMd5=${fileMd5.toUHexString("")}, " + - "videoSize=${fileSize}, thumbnailMd5=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" + "videoSize=${fileSize}, thumbnailMd5=${thumbnail.md5.toUHexString("")}, thumbnailSize=${thumbnail.size}]" } override fun contentToString(): String { @@ -153,10 +171,7 @@ internal class OnlineShortVideoImpl( if (fileSize != other.fileSize) return false if (fileFormat != other.fileFormat) return false if (urlForDownload != other.urlForDownload) return false - if (!thumbMd5.contentEquals(other.thumbMd5)) return false - if (thumbSize != other.thumbSize) return false - if (thumbWidth != other.thumbWidth) return false - if (thumbHeight != other.thumbHeight) return false + if (thumbnail != other.thumbnail) return false return true } @@ -168,10 +183,7 @@ internal class OnlineShortVideoImpl( result = 31 * result + fileSize.hashCode() result = 31 * result + fileFormat.hashCode() result = 31 * result + urlForDownload.hashCode() - result = 31 * result + thumbMd5.contentHashCode() - result = 31 * result + thumbSize.hashCode() - result = 31 * result + thumbWidth - result = 31 * result + thumbHeight + result = 31 * result + thumbnail.hashCode() return result } } @@ -183,21 +195,15 @@ internal class OfflineShortVideoImpl( override val fileMd5: ByteArray, override val fileSize: Long, override val fileFormat: String, - val thumbMd5: ByteArray, - val thumbSize: Long, - @Transient val thumbWidth: Int = 0, - @Transient val thumbHeight: Int = 0 + override val thumbnail: ShortVideoThumbnail ) : OfflineShortVideo, AbstractShortVideoWithThumbnail() { - override val thumbnail: ShortVideoThumbnail by lazy { - ShortVideoThumbnail(thumbMd5, thumbSize, thumbWidth, thumbHeight) - } /** * offline short video uses */ override fun toString(): String { return "[mirai:shortvideo:$videoId, videoName=$filename.$fileFormat, videoMd5=${fileMd5.toUHexString("")}, " + - "videoSize=${fileSize}, thumbnailMd5=${thumbMd5.toUHexString("")}, thumbnailSize=${thumbSize}]" + "videoSize=${fileSize}, thumbnailMd5=${thumbnail.md5.toUHexString("")}, thumbnailSize=${thumbnail.size}]" } override fun contentToString(): String { @@ -215,10 +221,7 @@ internal class OfflineShortVideoImpl( if (!fileMd5.contentEquals(other.fileMd5)) return false if (fileSize != other.fileSize) return false if (fileFormat != other.fileFormat) return false - if (!thumbMd5.contentEquals(other.thumbMd5)) return false - if (thumbSize != other.thumbSize) return false - if (thumbWidth != other.thumbWidth) return false - if (thumbHeight != other.thumbHeight) return false + if (thumbnail != other.thumbnail) return false return true } @@ -229,10 +232,7 @@ internal class OfflineShortVideoImpl( result = 31 * result + fileMd5.contentHashCode() result = 31 * result + fileSize.hashCode() result = 31 * result + fileFormat.hashCode() - result = 31 * result + thumbMd5.contentHashCode() - result = 31 * result + thumbSize.hashCode() - result = 31 * result + thumbWidth - result = 31 * result + thumbHeight + result = 31 * result + thumbnail.hashCode() return result } } diff --git a/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt b/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt index 9c1cf886c6..3849ebdccd 100644 --- a/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/image/InternalShortVideoProtocolImpl.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.internal.message.image import net.mamoe.mirai.internal.message.data.OfflineShortVideoImpl +import net.mamoe.mirai.internal.message.data.ShortVideoThumbnail import net.mamoe.mirai.message.data.InternalShortVideoProtocol import net.mamoe.mirai.message.data.OfflineShortVideo @@ -29,10 +30,12 @@ internal class InternalShortVideoProtocolImpl : InternalShortVideoProtocol { fileMd5, fileSize, fileFormat, - thumbnailMd5, - thumbnailSize, - 0, - 0 + ShortVideoThumbnail( + thumbnailMd5, + thumbnailSize, + 0, + 0 + ) ) } } \ No newline at end of file From 29e799a8417453ebcf3e444bbbcb8ad7e2093f2c Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Wed, 16 Aug 2023 23:17:32 +0800 Subject: [PATCH 40/43] dump api --- mirai-core-api/compatibility-validation/android/api/android.api | 1 + mirai-core-api/compatibility-validation/jvm/api/jvm.api | 1 + 2 files changed, 2 insertions(+) diff --git a/mirai-core-api/compatibility-validation/android/api/android.api b/mirai-core-api/compatibility-validation/android/api/android.api index 31d5983d03..0b72025f50 100644 --- a/mirai-core-api/compatibility-validation/android/api/android.api +++ b/mirai-core-api/compatibility-validation/android/api/android.api @@ -368,6 +368,7 @@ public abstract interface class net/mamoe/mirai/contact/Contact : kotlinx/corout public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo; public abstract fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/message/data/ShortVideo; public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } diff --git a/mirai-core-api/compatibility-validation/jvm/api/jvm.api b/mirai-core-api/compatibility-validation/jvm/api/jvm.api index 65187b2f35..c5311a71a2 100644 --- a/mirai-core-api/compatibility-validation/jvm/api/jvm.api +++ b/mirai-core-api/compatibility-validation/jvm/api/jvm.api @@ -368,6 +368,7 @@ public abstract interface class net/mamoe/mirai/contact/Contact : kotlinx/corout public abstract fun uploadImage (Lnet/mamoe/mirai/utils/ExternalResource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/ShortVideo; public abstract fun uploadShortVideo (Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/message/data/ShortVideo; public static synthetic fun uploadShortVideo$default (Lnet/mamoe/mirai/contact/Contact;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ExternalResource;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } From 858e2fefd88d64a32cbf295fc4d23ea040e8635d Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 20 Aug 2023 17:58:17 +0800 Subject: [PATCH 41/43] avoid null cast exception. --- .../kotlin/message/data/shortVideo.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 3b5bd193fa..5300ee84db 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -1,4 +1,4 @@ -/* + /* * Copyright 2019-2023 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. @@ -15,6 +15,7 @@ import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.User +import net.mamoe.mirai.contact.getMember import net.mamoe.mirai.internal.asQQAndroidBot import net.mamoe.mirai.internal.message.RefinableMessage import net.mamoe.mirai.internal.message.RefineContext @@ -48,14 +49,14 @@ internal class OnlineShortVideoMsgInternal( else -> return null // TODO: ignore processing stranger's video message } as Contact val sender = when (sourceKind) { - net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> bot.getFriend(fromId) + net.mamoe.mirai.message.data.MessageSourceKind.FRIEND -> + bot.getFriend(fromId) ?: error("Cannot find friend $fromId.") net.mamoe.mirai.message.data.MessageSourceKind.GROUP -> { - val group = bot.getGroup(groupId) - checkNotNull(group).members[fromId] + val group = bot.getGroup(groupId) ?: error("Cannot find group $groupId.") + group.getMember(fromId) ?: error("Cannot find member $fromId of group $groupId.") } - else -> return null // TODO: ignore processing stranger's video message - } as User + } val shortVideoDownloadReq = bot.network.sendAndExpect( PttCenterSvr.ShortVideoDownReq( @@ -68,11 +69,11 @@ internal class OnlineShortVideoMsgInternal( ) if (shortVideoDownloadReq !is PttCenterSvr.ShortVideoDownReq.Response.Success) - throw IllegalStateException("failed to query short video download attributes.") + 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. " + + "Queried short video download attributes doesn't match the requests. " + "message provides: ${videoFile.fileMd5.toUHexString("")}, " + "queried result: ${shortVideoDownloadReq.fileMd5.toUHexString("")}" ) From 929f550599ce5c0c7141ae6b93412f364dca9ee6 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 20 Aug 2023 18:04:50 +0800 Subject: [PATCH 42/43] combine format transition --- .../kotlin/message/data/shortVideo.kt | 23 +++++--------- .../protocol/impl/ShortVideoProtocol.kt | 31 ++++++++++--------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 5300ee84db..6b083f153d 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -20,9 +20,11 @@ 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.message.protocol.impl.ShortVideoProtocol 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.* +import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.toUHexString /** @@ -78,12 +80,16 @@ internal class OnlineShortVideoMsgInternal( "queried result: ${shortVideoDownloadReq.fileMd5.toUHexString("")}" ) + val format = ShortVideoProtocol.FORMAT + .firstOrNull { it.second == videoFile.fileFormat }?.first + ?: ExternalResource.DEFAULT_FORMAT_NAME + return OnlineShortVideoImpl( videoFile.fileUuid.decodeToString(), shortVideoDownloadReq.fileMd5, videoFile.fileName.decodeToString(), videoFile.fileSize.toLong(), - videoFile.fileFormat.toVideoFormat(), + format, shortVideoDownloadReq.urlV4, ShortVideoThumbnail( videoFile.thumbFileMd5, @@ -236,19 +242,4 @@ internal class OfflineShortVideoImpl( result = 31 * result + thumbnail.hashCode() return result } -} - -private fun Int.toVideoFormat() = when (this) { - 1 -> "ts" - 2 -> "avi" - 3 -> "mp4" - 4 -> "wmv" - 5 -> "mkv" - 6 -> "rmvb" - 7 -> "rm" - 8 -> "afs" - 9 -> "mov" - 10 -> "mod" - 11 -> "mts" - else -> "mirai" // unknown to default } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt index e5601a2649..82b97da576 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/ShortVideoProtocol.kt @@ -60,7 +60,7 @@ internal class ShortVideoProtocol : MessageProtocol() { fileUuid = data.videoId.encodeToByteArray(), fileMd5 = data.fileMd5, fileName = data.filename.encodeToByteArray(), - fileFormat = data.fileFormat.toVideoFormat(), + fileFormat = FORMAT.firstOrNull { it.first == data.fileFormat }?.second ?: 3, fileTime = 10, fileSize = data.fileSize.toInt(), thumbWidth = thumbWidth, @@ -79,19 +79,20 @@ internal class ShortVideoProtocol : MessageProtocol() { } } -} -private fun String.toVideoFormat() = when (this) { - "ts" -> 1 - "avi" -> 2 - "mp4" -> 3 - "wmv" -> 4 - "mkv" -> 5 - "rmvb" -> 6 - "rm" -> 7 - "afs" -> 8 - "mov" -> 9 - "mod" -> 10 - "mts" -> 11 - else -> -1 // unknown to default + internal companion object { + internal val FORMAT: List> = listOf( + "ts" to 1, + "avi" to 2, + "mp4" to 3, + "wmv" to 4, + "mkv" to 5, + "rmvb" to 6, + "rm" to 7, + "afs" to 8, + "mov" to 9, + "mod" to 10, + "mts" to 11 + ) + } } \ No newline at end of file From b9efcfe2c43f7f7bbfc549406ba971c234174294 Mon Sep 17 00:00:00 2001 From: StageGuard <1355416608@qq.com> Date: Sun, 20 Aug 2023 18:08:45 +0800 Subject: [PATCH 43/43] cleanup --- mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt index 6b083f153d..d54ae99c12 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/shortVideo.kt @@ -1,4 +1,4 @@ - /* +/* * Copyright 2019-2023 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. @@ -11,10 +11,8 @@ package net.mamoe.mirai.internal.message.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact -import net.mamoe.mirai.contact.User import net.mamoe.mirai.contact.getMember import net.mamoe.mirai.internal.asQQAndroidBot import net.mamoe.mirai.internal.message.RefinableMessage