From b5334c2b7265a3326b4fe70a9871dea73e8cc547 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 21 Apr 2022 17:54:15 +0600 Subject: [PATCH] fix of #556 --- CHANGELOG.md | 4 ++ gradle.properties | 2 +- tgbotapi.core/build.gradle | 1 + .../Ktor/base/MultipartRequestCallFactory.kt | 3 +- .../tgbotapi/requests/abstracts/InputFile.kt | 54 ++++++++++++++++--- .../dev/inmo/tgbotapi/utils/StorageFile.kt | 34 +++--------- .../abstracts/MPPFileMultipartFileActual.kt | 2 +- .../abstracts/InputFileFromJavaFile.kt | 10 +--- .../abstracts/MPPFileMultipartFileActual.kt | 11 +++- .../inmo/tgbotapi/utils/StorageFileFactory.kt | 1 + .../types/files/ContentAsMultipartFile.kt | 36 +++++++++++++ .../utils/types/files/ContentAsStorageFile.kt | 5 ++ 12 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsMultipartFile.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b4c1cee6b..557e97c239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ __All the `tgbotapi.extensions.*` packages have been removed__ * Constructor of `UnknownInlineKeyboardButton` is not internal and can be created with any `json` ([#563](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/563)) * All the interfaces from `dev.inmo.tgbotapi.types.files.abstracts` have been replaced to `dev.inmo.tgbotapi.types.files` and converted to sealed ([#550](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/550)) * `PassportFile` has been replaced to `dev.inmo.tgbotapi.types.files` + * `StorageFile` has been deprecated (fix of [#556](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/556)) + * `MultipartFile` do not require `StorageFile` anymore + * `InputFile` companion got functions to simplify creation of `InputFile`s + * New typealias `FileUrl` (represents `FileId` but declare that they are the same) * `WebApps`: * Created 🎉 * `BehaviourBuilder`: diff --git a/gradle.properties b/gradle.properties index 0894285386..94b1904d76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ klock_version=2.7.0 uuid_version=0.4.0 ktor_version=1.6.8 -micro_utils_version=0.9.20 +micro_utils_version=0.9.24 javax_activation_version=1.1.1 diff --git a/tgbotapi.core/build.gradle b/tgbotapi.core/build.gradle index e7d8a06973..cb2273c5f7 100644 --- a/tgbotapi.core/build.gradle +++ b/tgbotapi.core/build.gradle @@ -55,6 +55,7 @@ kotlin { api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version" api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version" api "dev.inmo:micro_utils.serialization.typed_serializer:$micro_utils_version" + api "dev.inmo:micro_utils.ktor.common:$micro_utils_version" api "dev.inmo:micro_utils.language_codes:$micro_utils_version" api "io.ktor:ktor-client-core:$ktor_version" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt index feb0daeb65..0a867ae31f 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/MultipartRequestCallFactory.kt @@ -1,6 +1,5 @@ package dev.inmo.tgbotapi.bot.Ktor.base -import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory import dev.inmo.tgbotapi.requests.abstracts.* import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.mapWithCommonValues @@ -26,7 +25,7 @@ class MultipartRequestCallFactory : AbstractRequestCallFactory() { Headers.build { append(HttpHeaders.ContentDisposition, "filename=${value.filename}") }, - block = value.file::input + block = value::input ) is FileId -> append(key, value.fileId) else -> append(key, value.toString()) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt index 4e5949aa6d..4a9cf1a557 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFile.kt @@ -1,11 +1,12 @@ package dev.inmo.tgbotapi.requests.abstracts +import com.benasher44.uuid.uuid4 import dev.inmo.micro_utils.common.MPPFile import dev.inmo.tgbotapi.utils.* import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.ByteReadPacket import io.ktor.utils.io.core.Input -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializable +import kotlinx.serialization.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -24,6 +25,15 @@ import kotlinx.serialization.encoding.Encoder @Serializable(InputFileSerializer::class) sealed class InputFile { abstract val fileId: String + + companion object { + operator fun invoke(file: MPPFile) = file.asMultipartFile() + + fun fromInput(filename: String, inputSource: () -> Input) = MultipartFile(filename, inputSource) + fun fromFile(file: MPPFile) = invoke(file) + fun fromId(id: String) = FileId(id) + fun fromUrl(url: String) = FileUrl(url) + } } internal inline val InputFile.attachFileId @@ -43,6 +53,8 @@ data class FileId( override val fileId: String ) : InputFile() +typealias FileUrl = FileId + fun String.toInputFile() = FileId(this) @RiskFeature @@ -60,23 +72,49 @@ object InputFileSerializer : KSerializer { */ @Serializable(InputFileSerializer::class) data class MultipartFile ( - val file: StorageFile, - val filename: String = file.fileName + val filename: String, + private val inputSource: () -> Input ) : InputFile() { - override val fileId: String = file.generateCustomName() + @Required + override val fileId: String = "${uuid4()}.${filename.fileExtension}" + val input: Input + get() = inputSource() + + @Deprecated("Storage file now is not necessary") + constructor( + file: StorageFile, + filename: String = file.fileName + ) : this( + filename, + file::input + ) } +@Deprecated("Storage file now is not necessary") @Suppress("NOTHING_TO_INLINE", "unused") -inline fun StorageFile.asMultipartFile() = MultipartFile(this) +inline fun StorageFile.asMultipartFile() = MultipartFile(fileName, ::input) @Suppress("NOTHING_TO_INLINE", "unused") suspend inline fun ByteReadChannel.asMultipartFile( fileName: String -) = MultipartFile(asStorageFile(fileName)) +) = MultipartFile( + fileName, + inputSource = asInput().let { { it } } +) + +@Suppress("NOTHING_TO_INLINE", "unused") +inline fun ByteArray.asMultipartFile( + fileName: String +) = MultipartFile( + fileName, + inputSource = { ByteReadPacket(this) } +) @Suppress("NOTHING_TO_INLINE", "unused") suspend inline fun ByteReadChannelAllocator.asMultipartFile( fileName: String ) = this.invoke().asMultipartFile(fileName) -expect suspend fun MPPFile.asMultipartFile(): MultipartFile +expect fun MPPFile.asMultipartFile(): MultipartFile +@Suppress("NOTHING_TO_INLINE") +inline fun MPPFile.multipartFile() = asMultipartFile() diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/StorageFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/StorageFile.kt index a0dfe6e0ab..69dd7b3d3e 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/StorageFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/StorageFile.kt @@ -8,23 +8,6 @@ import io.ktor.utils.io.core.ByteReadPacket import io.ktor.utils.io.core.Input import kotlinx.serialization.Serializable -/** - * Information about file for [StorageFile] - * - * @param contentType Raw type like "application/json" - * @param fileName This filename will be used in telegram system as name of file - */ -@Serializable -@Deprecated("Will be removed soon") -data class StorageFileInfo( - val fileName: String -) { - /** - * This methods is required for random generation of name for keeping warranties about unique file name - */ - fun generateCustomName() = "${uuid4()}.${fileName.fileExtension}" -} - /** * Contains info about file, which potentially can be sent to telegram system. * @@ -34,31 +17,22 @@ data class StorageFileInfo( * @see StorageFileInfo * @see asStorageFile */ +@Deprecated("Storage file now is not necessary") data class StorageFile( val fileName: String, private val inputSource: () -> Input ) { val input: Input get() = inputSource() - @Deprecated("This field will be removed soon. Use fileName instead of StorageFileInfo") - val storageFileInfo: StorageFileInfo - get() = StorageFileInfo(fileName) /** * This methods is required for random generation of name for keeping warranties about unique file name */ fun generateCustomName() = "${uuid4()}.${fileName.fileExtension}" - @Deprecated("This constructor will be removed soon. Use constructor with fileName instead of StorageFileInfo") - constructor( - storageFileInfo: StorageFileInfo, - inputSource: () -> Input - ) : this( - storageFileInfo.fileName, - inputSource - ) } +@Deprecated("Storage file now is not necessary") @Suppress("NOTHING_TO_INLINE") inline fun StorageFile( fileName: String, @@ -69,6 +43,7 @@ inline fun StorageFile( ByteReadPacket(bytes) } +@Deprecated("StorageFile now is not necessary") @Suppress("NOTHING_TO_INLINE") suspend inline fun StorageFile( fileName: String, @@ -78,16 +53,19 @@ suspend inline fun StorageFile( inputSource = byteReadChannel.asInput().let { { it } } ) +@Deprecated("StorageFile now is not necessary") @Suppress("NOTHING_TO_INLINE", "unused") inline fun ByteArray.asStorageFile( fileName: String ) = StorageFile(fileName, this) +@Deprecated("StorageFile now is not necessary") @Suppress("NOTHING_TO_INLINE", "unused") suspend inline fun ByteReadChannel.asStorageFile( fileName: String ) = StorageFile(fileName, this) +@Deprecated("StorageFile now is not necessary") @Suppress("NOTHING_TO_INLINE", "unused") suspend inline fun ByteReadChannelAllocator.asStorageFile( fileName: String diff --git a/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt index b3cd16f998..89b42348b4 100644 --- a/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt +++ b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt @@ -3,6 +3,6 @@ package dev.inmo.tgbotapi.requests.abstracts import dev.inmo.micro_utils.common.* import io.ktor.utils.io.ByteReadChannel -actual suspend fun MPPFile.asMultipartFile(): MultipartFile = ByteReadChannel(bytes()).asMultipartFile( +actual fun MPPFile.asMultipartFile(): MultipartFile = bytesSync().asMultipartFile( filename.name ) diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFileFromJavaFile.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFileFromJavaFile.kt index 34350831f3..c4f2a3ac47 100644 --- a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFileFromJavaFile.kt +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/InputFileFromJavaFile.kt @@ -1,12 +1,6 @@ package dev.inmo.tgbotapi.requests.abstracts -import dev.inmo.tgbotapi.utils.StorageFile import java.io.File -fun File.toInputFile() = if (exists()) { - MultipartFile( - StorageFile(this) - ) -} else { - error("Specified file $absolutePath does not exists") -} +@Deprecated("Duplacation of asMultipartFile", ReplaceWith("asMultipartFile", "dev.inmo.tgbotapi.requests.abstracts.asMultipartFile")) +fun File.toInputFile() = asMultipartFile() diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt index bc2ab9874e..dd0fffa590 100644 --- a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/requests/abstracts/MPPFileMultipartFileActual.kt @@ -1,5 +1,14 @@ package dev.inmo.tgbotapi.requests.abstracts import dev.inmo.micro_utils.common.MPPFile +import dev.inmo.micro_utils.common.filename +import dev.inmo.micro_utils.ktor.common.input -actual suspend fun MPPFile.asMultipartFile(): MultipartFile = toInputFile() +actual fun MPPFile.asMultipartFile(): MultipartFile = if (exists()) { + MultipartFile( + filename.string, + ::input + ) +} else { + error("Specified file $absolutePath does not exists") +} diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/StorageFileFactory.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/StorageFileFactory.kt index 2ceb51562f..be39de9c66 100644 --- a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/StorageFileFactory.kt +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/StorageFileFactory.kt @@ -4,6 +4,7 @@ import io.ktor.utils.io.streams.asInput import java.io.File import java.nio.file.Files +@Deprecated("StorageFile now is not necessary") fun StorageFile( file: File ) = StorageFile( diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsMultipartFile.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsMultipartFile.kt new file mode 100644 index 0000000000..1187f959aa --- /dev/null +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsMultipartFile.kt @@ -0,0 +1,36 @@ +package dev.inmo.tgbotapi.extensions.utils.types.files + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.requests.DownloadFileStream +import dev.inmo.tgbotapi.requests.abstracts.* +import dev.inmo.tgbotapi.requests.get.GetFile +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import dev.inmo.tgbotapi.utils.* + +suspend fun multipartFile( + downloadStreamAllocator: ByteReadChannelAllocator, + pathedFile: PathedFile +): MultipartFile { + return downloadStreamAllocator.asMultipartFile(pathedFile.fileName) +} + +suspend fun TelegramBot.multipartFile( + pathedFile: PathedFile +): MultipartFile = multipartFile( + execute(DownloadFileStream(pathedFile.filePath)), + pathedFile +) + +suspend fun TelegramBot.multipartFile( + fileId: FileId +): MultipartFile = multipartFile(execute(GetFile(fileId))) + +suspend fun TelegramBot.multipartFile( + file: TelegramMediaFile +): MultipartFile = multipartFile(file.fileId) + +suspend fun TelegramBot.multipartFile( + content: MediaContent +): MultipartFile = multipartFile(content.media) diff --git a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt index 59e2978f90..66ffae6686 100644 --- a/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt +++ b/tgbotapi.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt @@ -9,6 +9,7 @@ import dev.inmo.tgbotapi.types.files.TelegramMediaFile import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.utils.* +@Deprecated("StorageFile now is not necessary") suspend fun convertToStorageFile( downloadStreamAllocator: ByteReadChannelAllocator, pathedFile: PathedFile @@ -18,6 +19,7 @@ suspend fun convertToStorageFile( ) } +@Deprecated("StorageFile now is not necessary") suspend fun TelegramBot.convertToStorageFile( pathedFile: PathedFile ): StorageFile = convertToStorageFile( @@ -25,14 +27,17 @@ suspend fun TelegramBot.convertToStorageFile( pathedFile ) +@Deprecated("StorageFile now is not necessary") suspend fun TelegramBot.convertToStorageFile( fileId: FileId ): StorageFile = convertToStorageFile(execute(GetFile(fileId))) +@Deprecated("StorageFile now is not necessary") suspend fun TelegramBot.convertToStorageFile( file: TelegramMediaFile ): StorageFile = convertToStorageFile(file.fileId) +@Deprecated("StorageFile now is not necessary") suspend fun TelegramBot.convertToStorageFile( content: MediaContent ): StorageFile = convertToStorageFile(content.media)