mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-24 20:43:33 +08:00
[core] impl outgoing message sender for friend file message
This commit is contained in:
parent
ecee497ec6
commit
02c7086653
mirai-core/src/commonMain/kotlin
contact
message
network/protocol
@ -130,7 +130,7 @@ internal class FriendImpl(
|
||||
val fileUuid = when (appUpResp) {
|
||||
is OfflineFilleHandleSvr.ApplyUploadV3.Response.FileExists -> appUpResp.fileUuid
|
||||
is OfflineFilleHandleSvr.ApplyUploadV3.Response.RequireUpload -> appUpResp.fileUuid
|
||||
else -> error("unreachable!")
|
||||
else -> assertUnreachable()
|
||||
}
|
||||
val file = AbsoluteFriendFileImpl(
|
||||
this,
|
||||
@ -168,10 +168,9 @@ internal class FriendImpl(
|
||||
clientVer = "d92615c5",
|
||||
unknown = 4,
|
||||
),
|
||||
fileNameInfo = ExcitingFileNameInfo(filename)
|
||||
fileNameInfo = ExcitingFileNameInfo(filename = filename)
|
||||
),
|
||||
u3 = 0,
|
||||
u200 = null
|
||||
u200 = 1
|
||||
)
|
||||
|
||||
Highway.uploadResourceBdh(
|
||||
@ -180,7 +179,6 @@ internal class FriendImpl(
|
||||
kind = ResourceKind.FRIEND_FILE,
|
||||
commandId = 69,
|
||||
extendInfo = ext.toByteArray(FileUploadExt.serializer()),
|
||||
dataFlag = 0,
|
||||
callback = if (callback == null) null else fun(it: Long) {
|
||||
callback.onProgression(file, content, it)
|
||||
}
|
||||
@ -194,8 +192,9 @@ internal class FriendImpl(
|
||||
throw IllegalStateException(upSuccResp.message)
|
||||
}
|
||||
|
||||
sendMessage(AllowSendFileMessage + file.toMessage())
|
||||
return file.toMessage()
|
||||
val fileMessage = file.toMessage()
|
||||
sendMessage(AllowSendFileMessage + fileMessage)
|
||||
return fileMessage
|
||||
}
|
||||
|
||||
override suspend fun delete() {
|
||||
|
@ -232,8 +232,7 @@ internal abstract class CommonAbsoluteFolderImpl(
|
||||
),
|
||||
),
|
||||
),
|
||||
u3 = 0,
|
||||
u200 = 1
|
||||
u3 = 0
|
||||
).toByteArray(FileUploadExt.serializer())
|
||||
|
||||
callback?.onBegin(file, content)
|
||||
|
@ -60,7 +60,7 @@ internal class AbsoluteFriendFileImpl(
|
||||
}
|
||||
|
||||
override fun toMessage(): FileMessage {
|
||||
return FriendFileMessageImpl(id, name, size, false)
|
||||
return FriendFileMessageImpl(id, name, size, md5, true)
|
||||
}
|
||||
|
||||
override suspend fun refreshed(): AbsoluteFile? {
|
||||
|
@ -31,11 +31,6 @@ import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.warning
|
||||
import kotlin.contracts.contract
|
||||
|
||||
internal fun FileMessage.checkIsImpl(): GroupFileMessageImpl {
|
||||
contract { returns() implies (this@checkIsImpl is GroupFileMessageImpl) }
|
||||
return this as? GroupFileMessageImpl ?: error("FileMessage must not be implemented manually.")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Suppress("ANNOTATION_ARGUMENT_MUST_BE_CONST") // bug
|
||||
@SerialName(FileMessage.SERIAL_NAME)
|
||||
@ -93,6 +88,7 @@ internal data class FriendFileMessageImpl(
|
||||
override val id: String,
|
||||
override val name: String,
|
||||
override val size: Long,
|
||||
val md5: ByteArray,
|
||||
@Transient val allowSend: Boolean = false,
|
||||
) : FileMessage {
|
||||
override val internalId: Int
|
@ -12,10 +12,12 @@ package net.mamoe.mirai.internal.message.protocol.impl
|
||||
import io.ktor.utils.io.core.*
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import net.mamoe.mirai.contact.Friend
|
||||
import net.mamoe.mirai.internal.contact.SendMessageStep
|
||||
import net.mamoe.mirai.internal.contact.impl
|
||||
import net.mamoe.mirai.internal.contact.uin
|
||||
import net.mamoe.mirai.internal.message.data.FriendFileMessageImpl
|
||||
import net.mamoe.mirai.internal.message.data.GroupFileMessageImpl
|
||||
import net.mamoe.mirai.internal.message.data.checkIsImpl
|
||||
import net.mamoe.mirai.internal.message.flags.AllowSendFileMessage
|
||||
import net.mamoe.mirai.internal.message.protocol.MessageProtocol
|
||||
import net.mamoe.mirai.internal.message.protocol.ProcessorCollector
|
||||
@ -29,20 +31,32 @@ import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelin
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageSender
|
||||
import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessageTransformer
|
||||
import net.mamoe.mirai.internal.message.protocol.serialization.MessageSerializer
|
||||
import net.mamoe.mirai.internal.message.source.OnlineMessageSourceToFriendImpl
|
||||
import net.mamoe.mirai.internal.message.source.createMessageReceipt
|
||||
import net.mamoe.mirai.internal.message.visitor.MessageVisitorEx
|
||||
import net.mamoe.mirai.internal.network.components.ClockHolder.Companion.clock
|
||||
import net.mamoe.mirai.internal.network.components.SyncController.Companion.syncCookie
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.*
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgSvc
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ObjMsg
|
||||
import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x4
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement
|
||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
|
||||
import net.mamoe.mirai.message.data.FileMessage
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageContent
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor
|
||||
import net.mamoe.mirai.message.data.visitor.acceptChildren
|
||||
import net.mamoe.mirai.utils.cast
|
||||
import net.mamoe.mirai.utils.getRandomUnsignedInt
|
||||
import net.mamoe.mirai.utils.read
|
||||
import net.mamoe.mirai.utils.systemProp
|
||||
|
||||
@ -88,8 +102,11 @@ internal class FileMessageProtocol : MessageProtocol() {
|
||||
override fun visitFileMessage(message: FileMessage, data: Unit) {
|
||||
if (ALLOW_SENDING_FILE_MESSAGE) return
|
||||
// #1715
|
||||
if (message !is GroupFileMessageImpl) error("Customized FileMessage cannot be send")
|
||||
if (!message.allowSend) {
|
||||
if (message !is GroupFileMessageImpl && message !is FriendFileMessageImpl) {
|
||||
error("Customized FileMessage cannot be send")
|
||||
}
|
||||
if ((message is GroupFileMessageImpl && !message.allowSend) ||
|
||||
(message is FriendFileMessageImpl && !message.allowSend)) {
|
||||
hasFileMessage = true
|
||||
}
|
||||
}
|
||||
@ -111,26 +128,91 @@ internal class FileMessageProtocol : MessageProtocol() {
|
||||
private class FileMessageSender : OutgoingMessageSender {
|
||||
override suspend fun OutgoingMessagePipelineContext.process() {
|
||||
val file = currentMessageChain[FileMessage] ?: return
|
||||
markAsConsumed()
|
||||
|
||||
file.checkIsImpl() // TODO: file check impl
|
||||
|
||||
val contact = attributes[CONTACT]
|
||||
val bot = contact.bot
|
||||
|
||||
val strategy = components[MessageProtocolStrategy]
|
||||
|
||||
val source = coroutineScope {
|
||||
val source = async {
|
||||
strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 2021)
|
||||
if (file is FriendFileMessageImpl) {
|
||||
markAsConsumed()
|
||||
|
||||
val msgRand = getRandomUnsignedInt()
|
||||
val msgSeq = bot.client.sendFriendMessageSeq.next()
|
||||
|
||||
val msgSvcPbSendMsgResp = bot.network.sendAndExpect(
|
||||
MessageSvcPbSendMsg.buildOutgoingUniPacket(
|
||||
client = bot.client
|
||||
) {
|
||||
writeProtoBuf(
|
||||
MsgSvc.PbSendMsgReq.serializer(),
|
||||
MsgSvc.PbSendMsgReq(
|
||||
routingHead = MsgSvc.RoutingHead(
|
||||
trans0x211 = MsgSvc.Trans0x211(
|
||||
toUin = contact.uin,
|
||||
ccCmd = 4
|
||||
)
|
||||
),
|
||||
contentHead = MsgComm.ContentHead(
|
||||
pkgNum = 1,
|
||||
pkgIndex = 0,
|
||||
divSeq = 0
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
msgContent = SubMsgType0x4.MsgBody(
|
||||
msgNotOnlineFile = ImMsgBody.NotOnlineFile(
|
||||
fileType = 0,
|
||||
fileUuid = file.id.encodeToByteArray(),
|
||||
fileMd5 = file.md5,
|
||||
fileName = file.name.encodeToByteArray(),
|
||||
fileSize = file.size,
|
||||
subcmd = 1
|
||||
)
|
||||
).toByteArray(SubMsgType0x4.MsgBody.serializer())
|
||||
),
|
||||
msgSeq = msgSeq,
|
||||
msgRand = msgRand,
|
||||
syncCookie = bot.client.syncCookie ?: byteArrayOf()
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (msgSvcPbSendMsgResp is MessageSvcPbSendMsg.Response.SUCCESS) {
|
||||
val source = OnlineMessageSourceToFriendImpl(
|
||||
internalIds = intArrayOf(msgRand),
|
||||
sender = bot,
|
||||
target = contact.cast(),
|
||||
time = bot.clock.server.currentTimeSeconds().toInt(),
|
||||
sequenceIds = intArrayOf(msgSeq),
|
||||
originalMessage = currentMessageChain,
|
||||
)
|
||||
|
||||
collect(source.createMessageReceipt(contact, false))
|
||||
return
|
||||
} else {
|
||||
error("Failed to send FileMessage to contact $contact: MessageSvcPbSendMsg failed. $msgSvcPbSendMsgResp")
|
||||
}
|
||||
|
||||
bot.network.sendAndExpect(FileManagement.Feed(bot.client, contact.id, file.busId, file.id))
|
||||
|
||||
source.await()
|
||||
}
|
||||
|
||||
collect(source.createMessageReceipt(contact, true))
|
||||
if (file is GroupFileMessageImpl) {
|
||||
markAsConsumed()
|
||||
|
||||
val source = coroutineScope {
|
||||
val source = async {
|
||||
strategy.constructSourceForSpecialMessage(attributes[ORIGINAL_MESSAGE_AS_CHAIN], 2021)
|
||||
}
|
||||
|
||||
bot.network.sendAndExpect(FileManagement.Feed(bot.client, contact.id, file.busId, file.id))
|
||||
|
||||
source.await()
|
||||
}
|
||||
|
||||
collect(source.createMessageReceipt(contact, true))
|
||||
return
|
||||
}
|
||||
|
||||
error("FileMessage must not be implemented manually.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +265,8 @@ internal class FileMessageProtocol : MessageProtocol() {
|
||||
FriendFileMessageImpl(
|
||||
sub0x4.msgNotOnlineFile.fileUuid.decodeToString(),
|
||||
sub0x4.msgNotOnlineFile.fileName.decodeToString(),
|
||||
sub0x4.msgNotOnlineFile.fileSize
|
||||
sub0x4.msgNotOnlineFile.fileSize,
|
||||
sub0x4.msgNotOnlineFile.fileMd5
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ import kotlin.jvm.JvmField
|
||||
|
||||
@Serializable
|
||||
internal class FileUploadExt(
|
||||
@JvmField @ProtoNumber(1) val u1: Int,
|
||||
@JvmField @ProtoNumber(2) val u2: Int,
|
||||
@JvmField @ProtoNumber(3) val u3: Int,
|
||||
@JvmField @ProtoNumber(1) val u1: Int? = null,
|
||||
@JvmField @ProtoNumber(2) val u2: Int? = null,
|
||||
@JvmField @ProtoNumber(3) val u3: Int? = null,
|
||||
@JvmField @ProtoNumber(100) val entry: FileUploadEntry,
|
||||
@JvmField @ProtoNumber(200) val u200: Int? = null,
|
||||
) : ProtoBuf
|
||||
|
@ -39,8 +39,6 @@ internal class OfflineFilleHandleSvr {
|
||||
internal object UploadSucc : OutgoingPacketFactory<FileInfo>(
|
||||
"OfflineFilleHandleSvr.pb_ftn_CMD_REQ_UPLOAD_SUCC-800"
|
||||
) {
|
||||
|
||||
|
||||
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): FileInfo {
|
||||
val resp = readProtoBuf(Cmd0x346.RspBody.serializer())
|
||||
|
||||
@ -58,7 +56,7 @@ internal class OfflineFilleHandleSvr {
|
||||
fileInfo.uuid,
|
||||
fileInfo.fileName,
|
||||
fileInfo._3sha,
|
||||
fileInfo.md5,
|
||||
fileInfo._10mMd5,
|
||||
fileInfo.fileSize,
|
||||
fileInfo.expireTime.toLong(),
|
||||
fileInfo.ownerUin,
|
||||
@ -69,12 +67,14 @@ internal class OfflineFilleHandleSvr {
|
||||
client: QQAndroidClient,
|
||||
contact: Contact,
|
||||
fileUuid: ByteArray,
|
||||
) = buildOutgoingUniPacket(client) { seq ->
|
||||
) = buildOutgoingUniPacket(client, sequenceId = 7) { seq ->
|
||||
writeProtoBuf(
|
||||
Cmd0x346.ReqBody.serializer(),
|
||||
Cmd0x346.ReqBody(
|
||||
cmd = 800,
|
||||
seq = seq,
|
||||
businessId = 3,
|
||||
clientType = 104,
|
||||
msgUploadSuccReq = Cmd0x346.UploadSuccReq(
|
||||
senderUin = client.uin,
|
||||
recverUin = contact.uin,
|
||||
@ -219,12 +219,14 @@ internal class OfflineFilleHandleSvr {
|
||||
fileSize: Long,
|
||||
fileMd5: ByteArray,
|
||||
fileSha1: ByteArray,
|
||||
) = buildOutgoingUniPacket(client) { seq ->
|
||||
) = buildOutgoingUniPacket(client, sequenceId = client.sendFriendMessageSeq.next()) { seq ->
|
||||
writeProtoBuf(
|
||||
Cmd0x346.ReqBody.serializer(),
|
||||
Cmd0x346.ReqBody(
|
||||
cmd = 1700,
|
||||
seq = seq,
|
||||
businessId = 3,
|
||||
clientType = 104,
|
||||
msgApplyUploadReqV3 = Cmd0x346.ApplyUploadReqV3(
|
||||
senderUin = client.uin,
|
||||
recverUin = contact.uin,
|
||||
@ -233,15 +235,12 @@ internal class OfflineFilleHandleSvr {
|
||||
_10mMd5 = fileMd5,
|
||||
sha = fileSha1,
|
||||
localFilepath = "/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/$filename",
|
||||
dangerLevel = 0,
|
||||
totalSpace = 0,
|
||||
contenttype = 0,
|
||||
md5 = fileMd5
|
||||
)
|
||||
),
|
||||
flagSupportMediaplatform = 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user