diff --git a/onebot/src/main/kotlin/client/core/Bot.kt b/onebot/src/main/kotlin/client/core/Bot.kt index d3f9a5d..486b878 100644 --- a/onebot/src/main/kotlin/client/core/Bot.kt +++ b/onebot/src/main/kotlin/client/core/Bot.kt @@ -967,14 +967,26 @@ class Bot( * @return [ActionData] of [GroupFilesResp] */ @JvmBlockingBridge - suspend fun getGroupFilesByFolder(groupId: Long, folderId: String): ActionData { + suspend fun getGroupFilesByFolder(groupId: Long, folderId: String, showWarning: Boolean = true): ActionData { val action = ActionPathEnum.GET_GROUP_FILES_BY_FOLDER val params = JsonObject() params.addProperty("group_id", groupId) params.addProperty("folder_id", folderId) - val result = actionHandler.action(this, action, params) + val result = actionHandler.action(this, action, params, showWarning) return result.withToken() } + + suspend fun createGroupFileFolder(groupId: Long, name: String, parentId: String): ActionRaw { + val action = ActionPathEnum.CREATE_GROUP_FILE_FOLDER + val params = JsonObject().apply { + addProperty("group_id", groupId) + addProperty("name", name) + addProperty("parent_id", parentId) + } + val result = actionHandler.action(this, action, params) + return result.withClass() + } + /** * 获取群文件下载链接 * diff --git a/onebot/src/main/kotlin/sdk/enums/ActionPathEnum.kt b/onebot/src/main/kotlin/sdk/enums/ActionPathEnum.kt index 3318bc2..ed6bc39 100644 --- a/onebot/src/main/kotlin/sdk/enums/ActionPathEnum.kt +++ b/onebot/src/main/kotlin/sdk/enums/ActionPathEnum.kt @@ -303,4 +303,9 @@ enum class ActionPathEnum( * 属于 LLOnebot, NapCat 扩展 API */ EXT_GET_FILE("get_file"), + + /** + * 创建群文件夹 + */ + CREATE_GROUP_FILE_FOLDER("create_group_file_folder"), } diff --git a/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/contact/data/RemoteFilesWrapper.kt b/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/contact/data/RemoteFilesWrapper.kt index 3d135fe..ad26c38 100644 --- a/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/contact/data/RemoteFilesWrapper.kt +++ b/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/contact/data/RemoteFilesWrapper.kt @@ -1,6 +1,11 @@ package top.mrxiaom.overflow.internal.contact.data +import cn.evolvefield.onebot.sdk.response.group.GroupFilesResp import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.firstOrNull +import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.contact.file.AbsoluteFile import net.mamoe.mirai.contact.file.AbsoluteFileFolder import net.mamoe.mirai.contact.file.AbsoluteFolder @@ -8,7 +13,9 @@ import net.mamoe.mirai.contact.file.RemoteFiles import net.mamoe.mirai.message.data.FileMessage import net.mamoe.mirai.utils.* import top.mrxiaom.overflow.internal.contact.GroupWrapper +import top.mrxiaom.overflow.internal.contact.data.RemoteFilesWrapper.Companion.update import top.mrxiaom.overflow.internal.message.data.WrappedFileMessage +import top.mrxiaom.overflow.internal.utils.toMiraiFile import top.mrxiaom.overflow.internal.utils.toMiraiFiles import top.mrxiaom.overflow.internal.utils.toMiraiFolders import top.mrxiaom.overflow.spi.FileService @@ -27,13 +34,17 @@ internal class RemoteFilesWrapper( this, null, "/", "/", 0, 0, 0, data?.files?.size ?: 0 ) if (data != null) { - data.folders?.toMiraiFolders(this)?.also { root.folders.addAll(it) } - data.files?.toMiraiFiles(this)?.also { root.files.addAll(it) } + root.update(data) } else { bot.logger.warning("获取群 $id 的文件列表失败,可能是 Onebot 实现不支持,详见网络日志") } return RemoteFilesWrapper(this, root) } + + internal fun FolderWrapper.update(data: GroupFilesResp) { + data.folders?.toMiraiFolders(contact)?.also { folders.addAll(it) } + data.files?.toMiraiFiles(contact)?.also { files.addAll(it) } + } } } @@ -69,80 +80,104 @@ internal class FolderWrapper( } override suspend fun exists(): Boolean { - TODO("Not yet implemented") + return true // TODO: 获取该文件夹当前是否还存在 } override suspend fun files(): Flow { - TODO("Not yet implemented") + return files.asFlow() } override suspend fun refresh(): Boolean { - TODO("Not yet implemented") + return refreshed() != null } override suspend fun renameTo(newName: String): Boolean { - TODO("Not yet implemented") + TODO("暂无重命名文件夹实现") } override suspend fun children(): Flow { - TODO("Not yet implemented") + return folders.plus(files).asFlow() } @JavaFriendlyAPI override suspend fun childrenStream(): Stream { - TODO("Not yet implemented") + return folders.plus(files).stream() } override suspend fun createFolder(name: String): AbsoluteFolder { - TODO("Not yet implemented") + if (name.isEmpty()) throw IllegalArgumentException("子目录名称不能为空") + if (name.matches(Regex(":*?\"<>|"))) throw IllegalArgumentException("不能在非根目录下创建子目录") + contact.bot.impl.createGroupFileFolder(contact.id, name, id) + throw PermissionDeniedException("无法获取文件夹创建回执") } @JavaFriendlyAPI override suspend fun filesStream(): Stream { - TODO("Not yet implemented") + return files.toList().stream() } override suspend fun folders(): Flow { - TODO("Not yet implemented") + return folders.asFlow() } @JavaFriendlyAPI override suspend fun foldersStream(): Stream { - TODO("Not yet implemented") + return folders.toList().stream() + } + + suspend fun refreshFromOnebot(): GroupFilesResp? { + return contact.bot.impl.run { + val id = this@FolderWrapper.id + if (id == "/") { + getGroupRootFiles(contact.id, false).data + } else { + getGroupFilesByFolder(contact.id, id, false).data + } + } } override suspend fun refreshed(): AbsoluteFolder? { - TODO("Not yet implemented") + val data = refreshFromOnebot() + update(data ?: return null) + return this } override suspend fun resolveAll(path: String): Flow { - TODO("Not yet implemented") + return children().filter { it.name == path } } @JavaFriendlyAPI override suspend fun resolveAllStream(path: String): Stream { - TODO("Not yet implemented") + return childrenStream().filter { it.name == path } } override suspend fun resolveFileById(id: String, deep: Boolean): AbsoluteFile? { - TODO("Not yet implemented") + if (deep) { + TODO("暂不支持深入子目录查找文件") + } + return files.firstOrNull { it.id == id } } override suspend fun resolveFiles(path: String): Flow { - TODO("Not yet implemented") + // TODO: 获取子目录内的文件 + return files().filter { it.absolutePath.removePrefix(absolutePath) == path } } @JavaFriendlyAPI override suspend fun resolveFilesStream(path: String): Stream { - TODO("Not yet implemented") + // TODO: 获取子目录内的文件 + return filesStream().filter { it.absolutePath.removePrefix(absolutePath) == path } } override suspend fun resolveFolder(name: String): AbsoluteFolder? { - TODO("Not yet implemented") + if (name.isEmpty()) throw IllegalArgumentException("子目录名称不能为空") + if (name.matches(Regex(":*?\"<>|"))) throw IllegalArgumentException("请求子目录包含不允许的字符") + return folders().firstOrNull { it.name == name } } override suspend fun resolveFolderById(id: String): AbsoluteFolder? { - TODO("Not yet implemented") + if (name.isEmpty()) throw IllegalArgumentException("子目录名称不能为空") + return folders().firstOrNull { it.id == id } } @Suppress("INVISIBLE_MEMBER") @@ -152,7 +187,7 @@ internal class FolderWrapper( callback: ProgressionCallback? ): AbsoluteFile { impl.uploadGroupFile(contact.id, FileService.instance!!.upload(content), filepath, id) - TODO("文件上传回执") + throw PermissionDeniedException("没有任何方法获取文件发送回执") } override fun toString(): String = "AbsoluteFolder(name=$name, absolutePath=$absolutePath, id=$id)" @@ -220,19 +255,20 @@ internal class FileWrapper( } override suspend fun moveTo(folder: AbsoluteFolder): Boolean { - TODO("Not yet implemented") + TODO("暂无移动文件实现") } override suspend fun refresh(): Boolean { - TODO("Not yet implemented") + return refreshed() != null } override suspend fun refreshed(): AbsoluteFile? { - TODO("Not yet implemented") + val data = parent?.refreshFromOnebot() ?: return null + return data.files?.firstOrNull { it.fileId == id }?.toMiraiFile(contact, parent) } override suspend fun renameTo(newName: String): Boolean { - TODO("Not yet implemented") + TODO("暂无重命名文件实现") } override fun toMessage(): FileMessage { diff --git a/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/utils/TransformerUtils.kt b/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/utils/TransformerUtils.kt index ef8e31b..ef28a71 100644 --- a/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/utils/TransformerUtils.kt +++ b/overflow-core/src/main/kotlin/top/mrxiaom/overflow/internal/utils/TransformerUtils.kt @@ -98,18 +98,20 @@ internal fun MsgId?.safeMessageIds(bot: RemoteBot): IntArray { } internal fun List.toMiraiFiles(group: GroupWrapper, parent: FolderWrapper? = null): List { - return map { - val md5 = it.md5?.hexToBytes() ?: ByteArray(16) - val sha1 = it.sha1?.hexToBytes() ?: ByteArray(16) - FileWrapper(group, parent, - it.fileId, it.fileName, md5, sha1, it.fileSize, it.deadTime, it.modifyTime, it.uploadTime, it.uploader, it.busid - ) - } + return map { it.toMiraiFile(group, parent) } +} +internal fun GroupFilesResp.Files.toMiraiFile(group: GroupWrapper, parent: FolderWrapper? = null): FileWrapper { + val md5 = md5?.hexToBytes() ?: ByteArray(16) + val sha1 = sha1?.hexToBytes() ?: ByteArray(16) + return FileWrapper(group, parent, + fileId, fileName, md5, sha1, fileSize, deadTime, modifyTime, uploadTime, uploader, busid + ) } internal fun List.toMiraiFolders(group: GroupWrapper, parent: FolderWrapper? = null): List { - return map { - FolderWrapper(group, parent, - it.folderId, it.folderName, it.createTime, it.createTime, it.creator, it.totalFileCount - ) - } + return map { it.toMiraiFolder(group, parent) } +} +internal fun GroupFilesResp.Folders.toMiraiFolder(group: GroupWrapper, parent: FolderWrapper? = null): FolderWrapper { + return FolderWrapper(group, parent, + folderId, folderName, createTime, createTime, creator, totalFileCount + ) }