Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

支持解析和上传好友文件 #2788

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,11 @@ public final class net/mamoe/mirai/contact/ExceptionsKt {
public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamoe/mirai/contact/Contact {
public abstract fun getFiles ()Lnet/mamoe/mirai/contact/file/RemoteFiles;
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
public abstract fun uploadFile (Ljava/lang/String;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ProgressionCallback;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun uploadFile$default (Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ProgressionCallback;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/FileSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getFriendGroup ()Lnet/mamoe/mirai/contact/friendgroup/FriendGroup;
Expand Down
4 changes: 3 additions & 1 deletion mirai-core-api/compatibility-validation/jvm/api/jvm.api
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,11 @@ public final class net/mamoe/mirai/contact/ExceptionsKt {
public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamoe/mirai/contact/Contact {
public abstract fun getFiles ()Lnet/mamoe/mirai/contact/file/RemoteFiles;
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
public abstract fun uploadFile (Ljava/lang/String;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ProgressionCallback;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun uploadFile$default (Lnet/mamoe/mirai/contact/FileSupported;Ljava/lang/String;Lnet/mamoe/mirai/utils/ExternalResource;Lnet/mamoe/mirai/utils/ProgressionCallback;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/FileSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getFriendGroup ()Lnet/mamoe/mirai/contact/friendgroup/FriendGroup;
Expand Down
21 changes: 20 additions & 1 deletion mirai-core-api/src/commonMain/kotlin/contact/FileSupported.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@

package net.mamoe.mirai.contact

import net.mamoe.mirai.contact.file.AbsoluteFile
import net.mamoe.mirai.contact.file.AbsoluteFolder
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.message.data.FileMessage
import net.mamoe.mirai.utils.DeprecatedSinceMirai
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.NotStableForInheritance
import net.mamoe.mirai.utils.ProgressionCallback

/**
* 支持文件操作的 [Contact]. 目前仅 [Group].
* 支持文件操作的 [Contact]. 目前仅 [Group] 和 [Friend].
*
* 获取文件操作相关示例: [RemoteFiles]
*
Expand Down Expand Up @@ -47,4 +52,18 @@ public interface FileSupported : Contact {
* @since 2.8
*/
public val files: RemoteFiles

/**
* 上传一个文件到联系人,并返回文件消息.
* 对于群, 上传到群文件根目录.
*
* @param filename 文件名, 不可包含路径符(slash "/")
* @see AbsoluteFolder.uploadNewFile
* @since 2.17
*/
public suspend fun uploadFile(
filename: String,
content: ExternalResource,
callback: ProgressionCallback<AbsoluteFile, Long>? = null
): FileMessage
}
2 changes: 1 addition & 1 deletion mirai-core-api/src/commonMain/kotlin/contact/Friend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import net.mamoe.mirai.utils.NotStableForInheritance
*/
@Suppress("RedundantSetter")
@NotStableForInheritance
public interface Friend : User, CoroutineScope, AudioSupported, RoamingSupported {
public interface Friend : User, CoroutineScope, AudioSupported, RoamingSupported, FileSupported {

/**
* 该好友所在的好友分组
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public interface AbsoluteFile : AbsoluteFileFolder {
*
* 注意该操作有可能产生同名文件或目录 (当 [folder] 中已经存在一个名称为 [name] 的文件或目录时).
*
* 对于好友文件, 此方法不会产生任何效果.
*
* @throws IOException 当发生网络错误时可能抛出
* @throws IllegalStateException 当发生已知的协议错误时抛出
* @throws PermissionDeniedException 当无管理员权限时抛出 (若群仅允许管理员上传)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ private object MiraiCodeParsers : AbstractMap<String, MiraiCodeParser>(), Map<St
MusicShare(MusicKind.valueOf(kind), title, summary, jumpUrl, pictureUrl, musicUrl, brief)
},
"file" to MiraiCodeParser(Regex("""(.*?),(.*?),(.*?),(.*?)""")) { (id, internalId, name, size) ->
FileMessage(id, internalId.toInt(), name, size.toLong())
FileMessage(id, internalId.toInt(), name, size.toLong()) // TODO: parser for friend file
},
)

Expand Down
31 changes: 26 additions & 5 deletions mirai-core-mock/src/internal/contact/MockFriendImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/

@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
@file:Suppress(
"INVISIBLE_MEMBER",
"INVISIBLE_REFERENCE",
"CANNOT_OVERRIDE_INVISIBLE_MEMBER",
"DEPRECATION_ERROR"
)

package net.mamoe.mirai.mock.internal.contact

import kotlinx.coroutines.cancel
import net.mamoe.mirai.contact.AvatarSpec
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.OtherClient
import net.mamoe.mirai.contact.file.AbsoluteFile
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.friendgroup.FriendGroup
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.OfflineAudio
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.MockFriend
import net.mamoe.mirai.mock.internal.contact.friendfroup.MockFriendGroups
Expand All @@ -34,6 +38,8 @@ import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToFriend
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
import net.mamoe.mirai.mock.utils.broadcastBlocking
import net.mamoe.mirai.utils.ExternalResource
import net.mamoe.mirai.utils.ProgressionCallback
import net.mamoe.mirai.utils.RemoteFile
import net.mamoe.mirai.utils.cast
import java.util.concurrent.CancellationException
import kotlin.coroutines.CoroutineContext
Expand Down Expand Up @@ -92,6 +98,21 @@ internal class MockFriendImpl(
FriendRemarkChangeEvent(this, ov, value).broadcastBlocking()
}

@Deprecated("Please use files instead.", replaceWith = ReplaceWith("files.root"), level = DeprecationLevel.ERROR)
override val filesRoot: RemoteFile
get() = throw UnsupportedOperationException("file system is not supported by MockFriend, please use uploadFile instead.")

override val files: RemoteFiles
get() = throw UnsupportedOperationException("file system is not supported by MockFriend, please use uploadFile instead.")

override suspend fun uploadFile(
filename: String,
content: ExternalResource,
callback: ProgressionCallback<AbsoluteFile, Long>?
): FileMessage {
TODO("Not yet implemented")
}

override fun newMessagePreSend(message: Message): MessagePreSendEvent {
return FriendMessagePreSendEvent(this, message)
}
Expand Down
9 changes: 9 additions & 0 deletions mirai-core-mock/src/internal/contact/MockGroupImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.announcement.OfflineAnnouncement
import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters
import net.mamoe.mirai.contact.file.AbsoluteFile
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.data.GroupHonorType
Expand Down Expand Up @@ -350,6 +351,14 @@ internal class MockGroupImpl(
net.mamoe.mirai.mock.internal.remotefile.absolutefile.MockRemoteFiles(this, txFileSystem)
}

override suspend fun uploadFile(
filename: String,
content: ExternalResource,
callback: ProgressionCallback<AbsoluteFile, Long>?
): FileMessage {
TODO("uploadFile of MockGroupImpl")
}

override suspend fun uploadAudio(resource: ExternalResource): OfflineAudio =
resource.mockUploadAudio(bot)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.firstOrNull
import net.mamoe.mirai.contact.FileSupported
import net.mamoe.mirai.contact.file.AbsoluteFile
import net.mamoe.mirai.contact.file.AbsoluteFolder
import net.mamoe.mirai.internal.message.data.FileMessageImpl
import net.mamoe.mirai.internal.message.data.GroupFileMessageImpl
import net.mamoe.mirai.message.data.FileMessage
import net.mamoe.mirai.mock.internal.remotefile.remotefile.MockRemoteFile
import net.mamoe.mirai.mock.resserver.MockServerRemoteFile
Expand Down Expand Up @@ -55,7 +55,7 @@ internal class MockAbsoluteFile(

override fun toMessage(): FileMessage {
//todo busId
return FileMessageImpl(id, 0, name, size)
return GroupFileMessageImpl(id, 0, name, size)
}

override suspend fun refreshed(): AbsoluteFile? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.FileSupported
import net.mamoe.mirai.contact.PermissionDeniedException
import net.mamoe.mirai.contact.isOperator
import net.mamoe.mirai.internal.message.data.FileMessageImpl
import net.mamoe.mirai.internal.message.data.GroupFileMessageImpl
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.FileMessage
import net.mamoe.mirai.mock.contact.MockGroup
Expand Down Expand Up @@ -284,7 +284,7 @@ internal class MockRemoteFile(

override suspend fun toMessage(): FileMessage? {
val resolved = resolveFile() ?: return null
return FileMessageImpl(
return GroupFileMessageImpl(
name = resolved.name,
id = resolved.id,
size = resolved.size,
Expand All @@ -308,7 +308,7 @@ internal class MockRemoteFile(
val rsp = parent.uploadFile(this.name, resource, contact.bot.id)
callback?.onProgression(this, resource, rsSize)
callback?.onSuccess(this, resource)
return FileMessageImpl(
return GroupFileMessageImpl(
name = rsp.name,
id = rsp.id,
size = rsp.size,
Expand Down
2 changes: 1 addition & 1 deletion mirai-core/src/commonMain/kotlin/MiraiImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
}

override fun createFileMessage(id: String, internalId: Int, name: String, size: Long): FileMessage {
return FileMessageImpl(id, internalId, name, size)
return GroupFileMessageImpl(id, internalId, name, size)
}

override fun createUnsupportedMessage(struct: ByteArray): UnsupportedMessage =
Expand Down
108 changes: 108 additions & 0 deletions mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,42 @@ import io.ktor.utils.io.core.*
import kotlinx.coroutines.launch
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.file.AbsoluteFile
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.friendgroup.FriendGroup
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
import net.mamoe.mirai.event.events.FriendRemarkChangeEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.file.AbsoluteFriendFileImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.roaming.RoamingMessagesImplFriend
import net.mamoe.mirai.internal.message.data.OfflineAudioImpl
import net.mamoe.mirai.internal.message.flags.AllowSendFileMessage
import net.mamoe.mirai.internal.message.protocol.outgoing.FriendMessageProtocolStrategy
import net.mamoe.mirai.internal.message.protocol.outgoing.MessageProtocolStrategy
import net.mamoe.mirai.internal.network.components.HttpClientProvider
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.protocol
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x346
import net.mamoe.mirai.internal.network.protocol.data.proto.ExcitingBusiInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.ExcitingClientInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.ExcitingFileEntry
import net.mamoe.mirai.internal.network.protocol.data.proto.ExcitingFileNameInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.FileUploadEntry
import net.mamoe.mirai.internal.network.protocol.data.proto.FileUploadExt
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
import net.mamoe.mirai.internal.network.protocol.packet.chat.OfflineFilleHandleSvr
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.ChangeFriendRemark
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.FileMessage
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.OfflineAudio
import net.mamoe.mirai.spi.AudioToSilkService
Expand Down Expand Up @@ -89,6 +102,101 @@ internal class FriendImpl(

private val messageProtocolStrategy: MessageProtocolStrategy<FriendImpl> = FriendMessageProtocolStrategy(this)

override val files: RemoteFiles
get() = throw UnsupportedOperationException("file system is not supported by Friend, please use uploadFile instead.")

@Suppress("DEPRECATION_ERROR")
@Deprecated("Please use files instead.", replaceWith = ReplaceWith("files.root"), level = DeprecationLevel.ERROR)
override val filesRoot: RemoteFile
get() = throw UnsupportedOperationException("file system is not supported by Friend, please use uploadFile instead.")

override suspend fun uploadFile(
filename: String,
content: ExternalResource,
callback: ProgressionCallback<AbsoluteFile, Long>?
): FileMessage {
val md5 = content.md5
val sha1 = content.sha1
val size = content.size

val appUpResp = bot.network.sendAndExpect(
OfflineFilleHandleSvr.ApplyUploadV3(bot.client, this, filename, size, md5, sha1)
)

if (appUpResp is OfflineFilleHandleSvr.ApplyUploadV3.Response.Failed) {
throw IllegalStateException(appUpResp.message)
}

val fileUuid = when (appUpResp) {
is OfflineFilleHandleSvr.ApplyUploadV3.Response.FileExists -> appUpResp.fileUuid
is OfflineFilleHandleSvr.ApplyUploadV3.Response.RequireUpload -> appUpResp.fileUuid
else -> assertUnreachable()
}
val file = AbsoluteFriendFileImpl(
this,
fileUuid.decodeToString(),
filename,
bot.id,
0,
content.size,
content.sha1,
content.md5
)

if (appUpResp is OfflineFilleHandleSvr.ApplyUploadV3.Response.RequireUpload) {
val ext = FileUploadExt(
u1 = 100,
u2 = 2,
entry = FileUploadEntry(
business = ExcitingBusiInfo(
busId = 3,
senderUin = bot.uin,
receiverUin = uin,
groupCode = 0,
),
fileEntry = ExcitingFileEntry(
fileSize = content.size,
md5 = content.md5,
sha1 = content.sha1,
fileId = appUpResp.fileUuid,
uploadKey = appUpResp.uploadKey,
),
clientInfo = ExcitingClientInfo(
clientType = 2,
appId = bot.client.protocol.id.toString(),
terminalType = 2,
clientVer = "d92615c5",
unknown = 4,
),
fileNameInfo = ExcitingFileNameInfo(filename = filename)
),
u200 = 1
)

Highway.uploadResourceBdh(
bot = bot,
resource = content,
kind = ResourceKind.FRIEND_FILE,
commandId = 69,
extendInfo = ext.toByteArray(FileUploadExt.serializer()),
callback = if (callback == null) null else fun(it: Long) {
callback.onProgression(file, content, it)
}
)

callback?.onFinished(file, content, Result.success(content.size))
}

val upSuccResp = bot.network.sendAndExpect(OfflineFilleHandleSvr.UploadSucc(bot.client, this, fileUuid))
if (upSuccResp is OfflineFilleHandleSvr.FileInfo.Failed) {
throw IllegalStateException(upSuccResp.message)
}

val fileMessage = file.toMessage()
sendMessage(AllowSendFileMessage + fileMessage)
return fileMessage
}

override suspend fun delete() {
check(bot.friends[id] != null) {
"Friend $id had already been deleted"
Expand Down
Loading