mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-11 04:04:48 +08:00
Add LongMessageOrigin and extract public api IMirai.downloadLongMessage
This commit is contained in:
parent
de8e6469e0
commit
1e95c43ff6
@ -91,6 +91,8 @@ public abstract interface class net/mamoe/mirai/IMirai : net/mamoe/mirai/LowLeve
|
||||
public abstract fun createImage (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/Image;
|
||||
public synthetic fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)Lkotlin/Unit;
|
||||
public fun deleteGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)V
|
||||
public fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;)Lnet/mamoe/mirai/message/data/MessageChain;
|
||||
public abstract fun downloadLongMessage (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
public abstract fun getBotFactory ()Lnet/mamoe/mirai/BotFactory;
|
||||
public abstract fun getFileCacheStrategy ()Lnet/mamoe/mirai/utils/FileCacheStrategy;
|
||||
public fun getGroupAnnouncement (Lnet/mamoe/mirai/Bot;JLjava/lang/String;)Lnet/mamoe/mirai/data/GroupAnnouncement;
|
||||
@ -4364,6 +4366,23 @@ public final class net/mamoe/mirai/message/data/LightApp$Key : net/mamoe/mirai/m
|
||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/LongMessageOrigin : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageMetadata {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/LongMessageOrigin$Key;
|
||||
public fun <init> (Ljava/lang/String;)V
|
||||
public final fun component1 ()Ljava/lang/String;
|
||||
public final fun copy (Ljava/lang/String;)Lnet/mamoe/mirai/message/data/LongMessageOrigin;
|
||||
public static synthetic fun copy$default (Lnet/mamoe/mirai/message/data/LongMessageOrigin;Ljava/lang/String;ILjava/lang/Object;)Lnet/mamoe/mirai/message/data/LongMessageOrigin;
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public fun getKey ()Lnet/mamoe/mirai/message/data/LongMessageOrigin$Key;
|
||||
public synthetic fun getKey ()Lnet/mamoe/mirai/message/data/MessageKey;
|
||||
public final fun getResourceId ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun toString ()Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class net/mamoe/mirai/message/data/LongMessageOrigin$Key : net/mamoe/mirai/message/data/AbstractMessageKey {
|
||||
}
|
||||
|
||||
public abstract interface class net/mamoe/mirai/message/data/MarketFace : net/mamoe/mirai/message/data/HummerMessage {
|
||||
public static final field Key Lnet/mamoe/mirai/message/data/MarketFace$Key;
|
||||
public static final field SERIAL_NAME Ljava/lang/String;
|
||||
|
@ -170,6 +170,14 @@ public interface IMirai : LowLevelApiAccessor {
|
||||
originalMessage: MessageChain
|
||||
): OfflineMessageSource
|
||||
|
||||
/**
|
||||
* @since 2.3
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun downloadLongMessage(
|
||||
bot: Bot,
|
||||
resourceId: String,
|
||||
): MessageChain
|
||||
|
||||
/**
|
||||
* 通过好友验证
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2020 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/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.IMirai
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalApi
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
/**
|
||||
* 标识一个长消息.
|
||||
*
|
||||
*
|
||||
* 消息过长后会通过特殊的通道上传和下载, 每条消息都会获得一个 resourceId.
|
||||
*
|
||||
* 可以通过 resourceId 下载消息 [IMirai.downloadLongMessage].
|
||||
* 但不保证 resourceId 一直有效.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
@MiraiExperimentalApi
|
||||
public data class LongMessageOrigin(
|
||||
val resourceId: String
|
||||
) : MessageMetadata, ConstrainSingle {
|
||||
override val key: Key get() = Key
|
||||
|
||||
override fun toString(): String = ""
|
||||
|
||||
public companion object Key : AbstractMessageKey<LongMessageOrigin>({ it.safeCast() })
|
||||
}
|
@ -16,6 +16,8 @@ import io.ktor.client.request.*
|
||||
import io.ktor.client.request.forms.*
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readBytes
|
||||
import kotlinx.serialization.json.*
|
||||
import net.mamoe.mirai.*
|
||||
import net.mamoe.mirai.contact.*
|
||||
@ -24,15 +26,18 @@ import net.mamoe.mirai.event.broadcast
|
||||
import net.mamoe.mirai.event.events.*
|
||||
import net.mamoe.mirai.internal.contact.*
|
||||
import net.mamoe.mirai.internal.message.*
|
||||
import net.mamoe.mirai.internal.network.highway.Highway
|
||||
import net.mamoe.mirai.internal.network.highway.ResourceKind
|
||||
import net.mamoe.mirai.internal.network.highway.*
|
||||
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcDevLoginInfo
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.*
|
||||
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.login.StatSvc
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
|
||||
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.message.MessageSerializers
|
||||
import net.mamoe.mirai.message.action.Nudge
|
||||
@ -959,4 +964,59 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
||||
kind, ids, botId, time, fromId, targetId, originalMessage, internalIds
|
||||
)
|
||||
|
||||
override suspend fun downloadLongMessage(bot: Bot, resourceId: String): MessageChain {
|
||||
bot.asQQAndroidBot()
|
||||
when (val resp = MultiMsg.ApplyDown(bot.client, 1, resourceId, 1).sendAndExpect(bot)) {
|
||||
is MultiMsg.ApplyDown.Response.RequireDownload -> {
|
||||
val http = Mirai.Http
|
||||
val origin = resp.origin
|
||||
|
||||
val data = if (origin.msgExternInfo?.channelType == 2) {
|
||||
tryDownload(
|
||||
bot = bot,
|
||||
host = "https://ssl.htdata.qq.com",
|
||||
port = 0,
|
||||
resourceKind = ResourceKind.LONG_MESSAGE,
|
||||
channelKind = ChannelKind.HTTP
|
||||
) { host, port ->
|
||||
http.get<ByteArray>("$host${origin.thumbDownPara}:$port")
|
||||
}
|
||||
} else tryServersDownload(
|
||||
bot = bot,
|
||||
servers = origin.uint32DownIp.zip(origin.uint32DownPort),
|
||||
resourceKind = ResourceKind.LONG_MESSAGE,
|
||||
channelKind = ChannelKind.HTTP
|
||||
) { ip, port ->
|
||||
http.get("http://$ip${origin.thumbDownPara}:$port")
|
||||
}
|
||||
|
||||
val body = data.read {
|
||||
check(readByte() == 40.toByte()) {
|
||||
"bad data while MultiMsg.ApplyDown: ${data.toUHexString()}"
|
||||
}
|
||||
val headLength = readInt()
|
||||
val bodyLength = readInt()
|
||||
discardExact(headLength)
|
||||
readBytes(bodyLength)
|
||||
}
|
||||
|
||||
val decrypted = TEA.decrypt(body, origin.msgKey)
|
||||
val longResp =
|
||||
decrypted.loadAs(LongMsg.RspBody.serializer())
|
||||
|
||||
val down = longResp.msgDownRsp.single()
|
||||
check(down.result == 0) {
|
||||
"Message download failed, result=${down.result}, resId=${down.msgResid}, msgContent=${down.msgContent.toUHexString()}"
|
||||
}
|
||||
|
||||
val content = down.msgContent.ungzip()
|
||||
val transmit = content.loadAs(MsgTransmit.PbMultiMsgTransmit.serializer())
|
||||
|
||||
return transmit.msg.toMessageChainNoSource(bot.id, 0, MessageSourceKind.GROUP)
|
||||
}
|
||||
MultiMsg.ApplyDown.Response.MessageTooLarge -> {
|
||||
error("Message is too large and cannot download")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,25 +9,11 @@
|
||||
|
||||
package net.mamoe.mirai.internal.message
|
||||
|
||||
import io.ktor.client.request.*
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readBytes
|
||||
import net.mamoe.mirai.Mirai
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||
import net.mamoe.mirai.internal.network.highway.ChannelKind
|
||||
import net.mamoe.mirai.internal.network.highway.ResourceKind
|
||||
import net.mamoe.mirai.internal.network.highway.tryDownload
|
||||
import net.mamoe.mirai.internal.network.highway.tryServersDownload
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgTransmit
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.MultiMsg
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
|
||||
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
|
||||
// internal runtime value, not serializable
|
||||
internal data class LongMessageInternal internal constructor(override val content: String, val resId: String) :
|
||||
@ -36,59 +22,9 @@ internal data class LongMessageInternal internal constructor(override val conten
|
||||
|
||||
override suspend fun refine(contact: Contact, context: MessageChain): Message {
|
||||
val bot = contact.bot.asQQAndroidBot()
|
||||
when (val resp = MultiMsg.ApplyDown(bot.client, 1, resId, 1).sendAndExpect(bot)) {
|
||||
is MultiMsg.ApplyDown.Response.RequireDownload -> {
|
||||
val http = Mirai.Http
|
||||
val origin = resp.origin
|
||||
val long = Mirai.downloadLongMessage(bot, resId)
|
||||
|
||||
val data = if (origin.msgExternInfo?.channelType == 2) {
|
||||
tryDownload(
|
||||
bot = bot,
|
||||
host = "https://ssl.htdata.qq.com",
|
||||
port = 0,
|
||||
resourceKind = ResourceKind.LONG_MESSAGE,
|
||||
channelKind = ChannelKind.HTTP
|
||||
) { host, port ->
|
||||
http.get<ByteArray>("$host${origin.thumbDownPara}:$port")
|
||||
}
|
||||
} else tryServersDownload(
|
||||
bot = bot,
|
||||
servers = origin.uint32DownIp.zip(origin.uint32DownPort),
|
||||
resourceKind = ResourceKind.LONG_MESSAGE,
|
||||
channelKind = ChannelKind.HTTP
|
||||
) { ip, port ->
|
||||
http.get("http://$ip${origin.thumbDownPara}:$port")
|
||||
}
|
||||
|
||||
val body = data.read {
|
||||
check(readByte() == 40.toByte()) {
|
||||
"bad data while MultiMsg.ApplyDown: ${data.toUHexString()}"
|
||||
}
|
||||
val headLength = readInt()
|
||||
val bodyLength = readInt()
|
||||
discardExact(headLength)
|
||||
readBytes(bodyLength)
|
||||
}
|
||||
|
||||
val decrypted = TEA.decrypt(body, origin.msgKey)
|
||||
val longResp =
|
||||
decrypted.loadAs(LongMsg.RspBody.serializer())
|
||||
|
||||
val down = longResp.msgDownRsp.single()
|
||||
check(down.result == 0) {
|
||||
"Message download failed, result=${down.result}, resId=${down.msgResid}, msgContent=${down.msgContent.toUHexString()}"
|
||||
}
|
||||
|
||||
val content = down.msgContent.ungzip()
|
||||
val transmit = content.loadAs(MsgTransmit.PbMultiMsgTransmit.serializer())
|
||||
|
||||
val source = context.source
|
||||
return transmit.msg.toMessageChainNoSource(bot.id, contact.castOrNull<Group>()?.id ?: 0, source.kind)
|
||||
}
|
||||
MultiMsg.ApplyDown.Response.MessageTooLarge -> {
|
||||
error("Message is too large and cannot download")
|
||||
}
|
||||
}
|
||||
return LongMessageOrigin(resId) + long
|
||||
}
|
||||
|
||||
companion object Key :
|
||||
|
Loading…
Reference in New Issue
Block a user