diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3fd66ea9..908a9149fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,25 @@ # TelegramBotAPI changelog +## 0.35.5 + +**MIME TYPES FOR REQUESTS HAVE BEEN DEPRECATED DUE TO REDUNDANCY OF MIME TYPES IN FILES SENDING** + +* `Core`: + * Several new extensions `ByteReadChannel#asStorageFile` and `ByteReadChannelAllocator#asStorageFile` + * Several new extensions `ByteArray#asMultipartFile`, `ByteReadChannel#asMultipartFile` and + `ByteReadChannelAllocator#asMultipartFile` + * New extension `StorageFile#asMultipartFile` +* `API`: + * New extensions `TelegramBot#downloadFile` for writing of incoming bytes to the file + * New extensions `TelegramBot#downloadFileStream` and `TelegramBot#downloadFileStreamAllocator` for getting of input + streams instead of whole bytes arrays + * Old extensions `TelegramBot#downloadFile` has been replaced to the new package. Migration: replace in your project + `import dev.inmo.tgbotapi.extensions.api.downloadFile` with `import dev.inmo.tgbotapi.extensions.api.files.downloadFile` + * `PathedFile#filename` extension has been deprecated, and new property `PathedFile#fileName` has been included + directly in `PathedFile` +* `Utils`: + * Add several functions `convertToStorageFile` and extensions `TelegramBot#convertToStorageFile` + ## 0.35.4 Hotfix * `Common`: diff --git a/gradle.properties b/gradle.properties index 907ef3eb80..671a2cfeae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ micro_utils_version=0.5.19 javax_activation_version=1.1.1 library_group=dev.inmo -library_version=0.35.4 +library_version=0.35.5 github_release_plugin_version=2.2.12 diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt index 65a0f62e2f..ebbc38bebb 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/KtorRequestsExecutor.kt @@ -42,6 +42,14 @@ inline fun telegramBot( crossinline builder: KtorRequestsExecutorBuilder.() -> Unit = {} ): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, apiUrl), builder) +@RiskFeature +fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf( + SimpleRequestCallFactory(), + MultipartRequestCallFactory(), + DownloadFileRequestCallFactory, + DownloadFileChannelRequestCallFactory +) + class KtorRequestsExecutor( telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, client: HttpClient = HttpClient(), @@ -52,7 +60,7 @@ class KtorRequestsExecutor( ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { private val callsFactories: List = callsFactories.run { if (!excludeDefaultFactories) { - this + listOf(SimpleRequestCallFactory(), MultipartRequestCallFactory(), DownloadFileRequestCallFactory) + this + createTelegramBotDefaultKtorCallRequestsFactories() } else { this } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileChannelRequestCallFactory.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileChannelRequestCallFactory.kt new file mode 100644 index 0000000000..3532ec5217 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileChannelRequestCallFactory.kt @@ -0,0 +1,39 @@ +package dev.inmo.tgbotapi.bot.Ktor.base + +import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions +import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory +import dev.inmo.tgbotapi.requests.DownloadFileStream +import dev.inmo.tgbotapi.requests.abstracts.Request +import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator +import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper +import io.ktor.client.HttpClient +import io.ktor.client.call.receive +import io.ktor.client.request.get +import io.ktor.client.statement.HttpStatement +import io.ktor.utils.io.* +import kotlinx.coroutines.* +import kotlinx.serialization.json.Json +import kotlin.coroutines.coroutineContext + +object DownloadFileChannelRequestCallFactory : KtorCallFactory { + override suspend fun makeCall( + client: HttpClient, + urlsKeeper: TelegramAPIUrlsKeeper, + request: Request, + jsonFormatter: Json + ): T? = (request as? DownloadFileStream) ?.let { + val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath) + + ByteReadChannelAllocator { + val scope = CoroutineScope(coroutineContext) + val outChannel = ByteChannel() + scope.launchSafelyWithoutExceptions { + client.get(fullUrl).execute { httpResponse -> + val channel: ByteReadChannel = httpResponse.receive() + channel.copyAndClose(outChannel) + } + } + outChannel + } as T + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileRequestCallFactory.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileRequestCallFactory.kt index cbccc36e5c..bf8e3687d9 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileRequestCallFactory.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/bot/Ktor/base/DownloadFileRequestCallFactory.kt @@ -16,9 +16,9 @@ object DownloadFileRequestCallFactory : KtorCallFactory { request: Request, jsonFormatter: Json ): T? = (request as? DownloadFile) ?.let { - val fullUrl = "${urlsKeeper.fileBaseUrl}/${it.filePath}" + val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath) - return safely { + safely { @Suppress("UNCHECKED_CAST") client.get(fullUrl) as T // always ByteArray } 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 ca41189baf..2d42d15ea9 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 @@ -24,7 +24,6 @@ class MultipartRequestCallFactory : AbstractRequestCallFactory() { is MultipartFile -> appendInput( key, Headers.build { - append(HttpHeaders.ContentType, value.mimeType) append(HttpHeaders.ContentDisposition, "filename=${value.fileId}") }, block = value.file::input diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/DownloadFileStream.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/DownloadFileStream.kt new file mode 100644 index 0000000000..c9ba0606bf --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/requests/DownloadFileStream.kt @@ -0,0 +1,15 @@ +package dev.inmo.tgbotapi.requests + +import dev.inmo.tgbotapi.requests.abstracts.Request +import dev.inmo.tgbotapi.utils.ByteReadChannelAllocator +import dev.inmo.tgbotapi.utils.ByteReadChannelAllocatorDeserializationStrategy +import kotlinx.serialization.DeserializationStrategy + +class DownloadFileStream( + val filePath: String +) : Request { + override fun method(): String = filePath + override val resultDeserializer: DeserializationStrategy + get() = ByteReadChannelAllocatorDeserializationStrategy + +} 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 d6111400e6..b306112118 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,7 +1,7 @@ package dev.inmo.tgbotapi.requests.abstracts -import dev.inmo.tgbotapi.utils.RiskFeature -import dev.inmo.tgbotapi.utils.StorageFile +import dev.inmo.tgbotapi.utils.* +import io.ktor.utils.io.ByteReadChannel import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.* @@ -42,12 +42,36 @@ object InputFileSerializer : KSerializer { // TODO:: add checks for files size /** * Contains info about file for sending + * + * @see asMultipartFile */ @Serializable(InputFileSerializer::class) data class MultipartFile ( val file: StorageFile, - val mimeType: String = file.storageFileInfo.contentType, val filename: String = file.storageFileInfo.fileName ) : InputFile() { override val fileId: String = file.storageFileInfo.generateCustomName() + + @Deprecated("This constructor is redundant. Use constructor without mime type") + constructor(file: StorageFile, mimeType: String, filename: String): this(file, filename) } + +@Suppress("NOTHING_TO_INLINE", "unused") +inline fun StorageFile.asMultipartFile() = MultipartFile(this) + +@Deprecated("This method is redundant. Use asMultipartFile without mime type") +@Suppress("NOTHING_TO_INLINE", "unused") +inline fun ByteArray.asMultipartFile( + fileName: String, + mimeType: MimeType +) = MultipartFile(asStorageFile(fileName)) + +@Suppress("NOTHING_TO_INLINE", "unused") +suspend inline fun ByteReadChannel.asMultipartFile( + fileName: String +) = MultipartFile(asStorageFile(fileName)) + +@Suppress("NOTHING_TO_INLINE", "unused") +suspend inline fun ByteReadChannelAllocator.asMultipartFile( + fileName: String +) = this.invoke().asMultipartFile(fileName) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt index 06438895c8..247aa63c12 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt @@ -5,8 +5,7 @@ import dev.inmo.tgbotapi.types.FileUniqueId import dev.inmo.tgbotapi.types.fileUniqueIdField import dev.inmo.tgbotapi.types.files.abstracts.* import dev.inmo.tgbotapi.utils.* -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.* @Serializable data class PathedFile( @@ -18,8 +17,14 @@ data class PathedFile( val filePath: String, @SerialName(fileSizeField) override val fileSize: Long? = null -): TelegramMediaFile +): TelegramMediaFile { + @Transient + val fileName: FileName by lazy(LazyThreadSafetyMode.PUBLICATION) { + filePath.filenameFromUrl + } +} +@Deprecated("Use fileName property instead", ReplaceWith("fileName")) val PathedFile.filename: FileName get() = filePath.filenameFromUrl fun TelegramAPIUrlsKeeper.resolveFileURL(file: PathedFile): String = "$fileBaseUrl/${file.filePath}" diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/BuiltinMimeTypes.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/BuiltinMimeTypes.kt index be0dcb1f2f..beb3d96f6e 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/BuiltinMimeTypes.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/BuiltinMimeTypes.kt @@ -8,4 +8,11 @@ object BuiltinMimeTypes { object Video { val MP4 = buildMimeType("video/mp4") } + object Text { + val Html = buildMimeType("text/html") + } + object Application { + val Zip = buildMimeType("application/zip") + val Pdf = buildMimeType("application/pdf") + } } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelAllocator.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelAllocator.kt new file mode 100644 index 0000000000..7df9343cde --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelAllocator.kt @@ -0,0 +1,22 @@ +package dev.inmo.tgbotapi.utils + +import dev.inmo.micro_utils.common.ByteArrayAllocatorSerializer +import io.ktor.utils.io.ByteReadChannel +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +fun interface ByteReadChannelAllocator { + suspend operator fun invoke(): ByteReadChannel +} + +object ByteReadChannelAllocatorDeserializationStrategy : DeserializationStrategy { + override val descriptor: SerialDescriptor = ByteArrayAllocatorSerializer.descriptor + + override fun deserialize(decoder: Decoder): ByteReadChannelAllocator { + val byteArrayAllocator = ByteArrayAllocatorSerializer.deserialize(decoder) + return ByteReadChannelAllocator { ByteReadChannel(byteArrayAllocator()) } + } +} diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelToInput.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelToInput.kt new file mode 100644 index 0000000000..e12b6bae15 --- /dev/null +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/ByteReadChannelToInput.kt @@ -0,0 +1,6 @@ +package dev.inmo.tgbotapi.utils + +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.Input + +expect suspend fun ByteReadChannel.asInput(): Input 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 6db07683ee..a67bb9f19e 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 @@ -1,6 +1,7 @@ package dev.inmo.tgbotapi.utils import com.benasher44.uuid.uuid4 +import io.ktor.utils.io.* import io.ktor.utils.io.core.ByteReadPacket import io.ktor.utils.io.core.Input import kotlinx.serialization.Serializable @@ -13,9 +14,14 @@ import kotlinx.serialization.Serializable */ @Serializable data class StorageFileInfo( - val contentType: String, val fileName: String ) { + @Deprecated("This constructor is redundant. Use constructor without mime type") + constructor( + contentType: String, + fileName: String + ): this(fileName) + /** * This methods is required for random generation of name for keeping warranties about unique file name */ @@ -39,19 +45,55 @@ data class StorageFile( get() = inputSource() } +@Deprecated("This constructor is redundant. Use constructor without mime type") @Suppress("NOTHING_TO_INLINE") inline fun StorageFile( fileName: String, bytes: ByteArray, mimeType: MimeType ) = StorageFile( - StorageFileInfo(mimeType.raw, fileName) + StorageFileInfo(fileName) ) { ByteReadPacket(bytes) } -/** - * - */ +@Suppress("NOTHING_TO_INLINE") +inline fun StorageFile( + fileName: String, + bytes: ByteArray +) = StorageFile( + StorageFileInfo(fileName) +) { + ByteReadPacket(bytes) +} + +@Suppress("NOTHING_TO_INLINE") +suspend inline fun StorageFile( + fileName: String, + byteReadChannel: ByteReadChannel +) = StorageFile( + StorageFileInfo(fileName), + byteReadChannel.asInput().let { { it } } +) + @Suppress("NOTHING_TO_INLINE", "unused") -inline fun ByteArray.asStorageFile(fileName: String, mimeType: MimeType) = StorageFile(fileName, this, mimeType) +inline fun ByteArray.asStorageFile( + fileName: String +) = StorageFile(fileName, this) + +@Deprecated("This constructor is redundant. Use constructor without mime type") +@Suppress("NOTHING_TO_INLINE", "unused") +inline fun ByteArray.asStorageFile( + fileName: String, + mimeType: MimeType +) = asStorageFile(fileName) + +@Suppress("NOTHING_TO_INLINE", "unused") +suspend inline fun ByteReadChannel.asStorageFile( + fileName: String +) = StorageFile(fileName, this) + +@Suppress("NOTHING_TO_INLINE", "unused") +suspend inline fun ByteReadChannelAllocator.asStorageFile( + fileName: String +) = this.invoke().asStorageFile(fileName) diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt index 100781d435..422634479e 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt @@ -26,4 +26,6 @@ class TelegramAPIUrlsKeeper( commonAPIUrl = "$correctedHost/bot$token" fileBaseUrl = "$correctedHost/file/bot$token" } + + fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath" } diff --git a/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt new file mode 100644 index 0000000000..b64c816a97 --- /dev/null +++ b/tgbotapi.core/src/jsMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt @@ -0,0 +1,8 @@ +package dev.inmo.tgbotapi.utils + +import io.ktor.util.toByteArray +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.ByteReadPacket +import io.ktor.utils.io.core.Input + +actual suspend fun ByteReadChannel.asInput(): Input = ByteReadPacket(toByteArray()) 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 8d7ebe5c53..9d26befaeb 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 @@ -7,10 +7,7 @@ import java.nio.file.Files fun StorageFile( file: File ) = StorageFile( - StorageFileInfo( - Files.probeContentType(file.toPath()), - file.name - ) + StorageFileInfo(file.name) ) { file.inputStream().asInput() } diff --git a/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt new file mode 100644 index 0000000000..a68c1f8639 --- /dev/null +++ b/tgbotapi.core/src/jvmMain/kotlin/dev/inmo/tgbotapi/utils/asInput.kt @@ -0,0 +1,10 @@ +package dev.inmo.tgbotapi.utils + +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.Input +import io.ktor.utils.io.jvm.javaio.toInputStream +import io.ktor.utils.io.streams.asInput +import kotlinx.coroutines.job +import kotlin.coroutines.coroutineContext + +actual suspend fun ByteReadChannel.asInput(): Input = toInputStream(coroutineContext.job).asInput() diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/DownloadFile.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/DownloadFile.kt index 7f653730b6..627f75ec8b 100644 --- a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/DownloadFile.kt +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/DownloadFile.kt @@ -1,39 +1,33 @@ package dev.inmo.tgbotapi.extensions.api import dev.inmo.tgbotapi.bot.TelegramBot -import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo -import dev.inmo.tgbotapi.requests.DownloadFile +import dev.inmo.tgbotapi.extensions.api.files.downloadFile import dev.inmo.tgbotapi.requests.abstracts.FileId import dev.inmo.tgbotapi.types.files.PathedFile import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +@Deprecated("Replaced", ReplaceWith("downloadFile", "dev.inmo.tgbotapi.extensions.api.files.downloadFile")) suspend fun TelegramBot.downloadFile( filePath: String -): ByteArray = execute( - DownloadFile(filePath) -) +): ByteArray = downloadFile(filePath) +@Deprecated("Replaced", ReplaceWith("downloadFile", "dev.inmo.tgbotapi.extensions.api.files.downloadFile")) suspend fun TelegramBot.downloadFile( pathedFile: PathedFile -): ByteArray = execute( - DownloadFile(pathedFile.filePath) -) +): ByteArray = downloadFile(pathedFile) +@Deprecated("Replaced", ReplaceWith("downloadFile", "dev.inmo.tgbotapi.extensions.api.files.downloadFile")) suspend fun TelegramBot.downloadFile( fileId: FileId -): ByteArray = downloadFile( - getFileAdditionalInfo(fileId) -) +): ByteArray = downloadFile(fileId) +@Deprecated("Replaced", ReplaceWith("downloadFile", "dev.inmo.tgbotapi.extensions.api.files.downloadFile")) suspend fun TelegramBot.downloadFile( file: TelegramMediaFile -): ByteArray = downloadFile( - getFileAdditionalInfo(file) -) +): ByteArray = downloadFile(file) +@Deprecated("Replaced", ReplaceWith("downloadFile", "dev.inmo.tgbotapi.extensions.api.files.downloadFile")) suspend fun TelegramBot.downloadFile( file: MediaContent -): ByteArray = downloadFile( - getFileAdditionalInfo(file.media) -) +): ByteArray = downloadFile(file) diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFile.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFile.kt new file mode 100644 index 0000000000..6a69e6b15b --- /dev/null +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFile.kt @@ -0,0 +1,39 @@ +package dev.inmo.tgbotapi.extensions.api.files + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo +import dev.inmo.tgbotapi.requests.DownloadFile +import dev.inmo.tgbotapi.requests.abstracts.FileId +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent + +suspend fun TelegramBot.downloadFile( + filePath: String +): ByteArray = execute( + DownloadFile(filePath) +) + +suspend fun TelegramBot.downloadFile( + pathedFile: PathedFile +): ByteArray = downloadFile( + pathedFile.filePath +) + +suspend fun TelegramBot.downloadFile( + fileId: FileId +): ByteArray = downloadFile( + getFileAdditionalInfo(fileId) +) + +suspend fun TelegramBot.downloadFile( + file: TelegramMediaFile +): ByteArray = downloadFile( + getFileAdditionalInfo(file) +) + +suspend fun TelegramBot.downloadFile( + file: MediaContent +): ByteArray = downloadFile( + getFileAdditionalInfo(file.media) +) diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStream.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStream.kt new file mode 100644 index 0000000000..f601f643c9 --- /dev/null +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStream.kt @@ -0,0 +1,29 @@ +package dev.inmo.tgbotapi.extensions.api.files + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo +import dev.inmo.tgbotapi.requests.DownloadFileStream +import dev.inmo.tgbotapi.requests.abstracts.FileId +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent + +suspend fun TelegramBot.downloadFileStream( + filePath: String +) = downloadFileStreamAllocator(filePath).invoke() + +suspend fun TelegramBot.downloadFileStream( + pathedFile: PathedFile +) = downloadFileStream(pathedFile.filePath) + +suspend fun TelegramBot.downloadFileStream( + fileId: FileId +) = downloadFileStream(getFileAdditionalInfo(fileId)) + +suspend fun TelegramBot.downloadFileStream( + file: TelegramMediaFile +) = downloadFileStream(getFileAdditionalInfo(file)) + +suspend fun TelegramBot.downloadFileStream( + file: MediaContent +) = downloadFileStream(getFileAdditionalInfo(file.media)) diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStreamAllocator.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStreamAllocator.kt new file mode 100644 index 0000000000..8261d4c275 --- /dev/null +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileStreamAllocator.kt @@ -0,0 +1,29 @@ +package dev.inmo.tgbotapi.extensions.api.files + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo +import dev.inmo.tgbotapi.requests.DownloadFileStream +import dev.inmo.tgbotapi.requests.abstracts.FileId +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent + +suspend fun TelegramBot.downloadFileStreamAllocator( + filePath: String +) = execute(DownloadFileStream(filePath)) + +suspend fun TelegramBot.downloadFileStreamAllocator( + pathedFile: PathedFile +) = downloadFileStreamAllocator(pathedFile.filePath) + +suspend fun TelegramBot.downloadFileStreamAllocator( + fileId: FileId +) = downloadFileStreamAllocator(getFileAdditionalInfo(fileId)) + +suspend fun TelegramBot.downloadFileStreamAllocator( + file: TelegramMediaFile +) = downloadFileStreamAllocator(getFileAdditionalInfo(file)) + +suspend fun TelegramBot.downloadFileStreamAllocator( + file: MediaContent +) = downloadFileStreamAllocator(getFileAdditionalInfo(file.media)) diff --git a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/get/GetFile.kt b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/get/GetFile.kt index d2515dd8ce..d3ce16dccc 100644 --- a/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/get/GetFile.kt +++ b/tgbotapi.extensions.api/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/api/get/GetFile.kt @@ -4,6 +4,7 @@ import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.requests.abstracts.FileId import dev.inmo.tgbotapi.requests.get.GetFile import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent suspend fun TelegramBot.getFileAdditionalInfo( fileId: FileId @@ -14,3 +15,7 @@ suspend fun TelegramBot.getFileAdditionalInfo( suspend fun TelegramBot.getFileAdditionalInfo( file: TelegramMediaFile ) = getFileAdditionalInfo(file.fileId) + +suspend fun TelegramBot.getFileAdditionalInfo( + content: MediaContent +) = getFileAdditionalInfo(content.media) diff --git a/tgbotapi.extensions.api/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileToFile.kt b/tgbotapi.extensions.api/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileToFile.kt new file mode 100644 index 0000000000..0e12b4c855 --- /dev/null +++ b/tgbotapi.extensions.api/src/jvmMain/kotlin/dev/inmo/tgbotapi/extensions/api/files/DownloadFileToFile.kt @@ -0,0 +1,64 @@ +package dev.inmo.tgbotapi.extensions.api.files + +import dev.inmo.micro_utils.coroutines.doOutsideOfCoroutine +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo +import dev.inmo.tgbotapi.requests.abstracts.FileId +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import io.ktor.util.cio.use +import io.ktor.util.cio.writeChannel +import io.ktor.utils.io.copyTo +import kotlinx.coroutines.job +import java.io.File +import kotlin.coroutines.coroutineContext + +suspend fun TelegramBot.downloadFile( + filePath: String, + destFile: File +): File { + val readChannel = downloadFileStream(filePath) + + destFile.deleteRecursively() + destFile.parentFile.mkdirs() + doOutsideOfCoroutine { destFile.createNewFile() } + + destFile.writeChannel(coroutineContext.job).use { + readChannel.copyTo(this) + } + + return destFile +} + +suspend fun TelegramBot.downloadFile( + pathedFile: PathedFile, + destFile: File +) = downloadFile( + pathedFile.filePath, + destFile +) + +suspend fun TelegramBot.downloadFile( + fileId: FileId, + destFile: File +) = downloadFile( + getFileAdditionalInfo(fileId), + destFile +) + +suspend fun TelegramBot.downloadFile( + file: TelegramMediaFile, + destFile: File +): File = downloadFile( + getFileAdditionalInfo(file), + destFile +) + +suspend fun TelegramBot.downloadFile( + file: MediaContent, + destFile: File +) = downloadFile( + getFileAdditionalInfo(file.media), + destFile +) diff --git a/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt new file mode 100644 index 0000000000..53b192e74e --- /dev/null +++ b/tgbotapi.extensions.utils/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/utils/types/files/ContentAsStorageFile.kt @@ -0,0 +1,38 @@ +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.FileId +import dev.inmo.tgbotapi.requests.get.GetFile +import dev.inmo.tgbotapi.types.files.PathedFile +import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile +import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent +import dev.inmo.tgbotapi.utils.* + +suspend fun convertToStorageFile( + downloadStreamAllocator: ByteReadChannelAllocator, + pathedFile: PathedFile +): StorageFile { + return downloadStreamAllocator.asStorageFile( + pathedFile.fileName + ) +} + +suspend fun TelegramBot.convertToStorageFile( + pathedFile: PathedFile +): StorageFile = convertToStorageFile( + execute(DownloadFileStream(pathedFile.filePath)), + pathedFile +) + +suspend fun TelegramBot.convertToStorageFile( + fileId: FileId +): StorageFile = convertToStorageFile(execute(GetFile(fileId))) + +suspend fun TelegramBot.convertToStorageFile( + file: TelegramMediaFile +): StorageFile = convertToStorageFile(file.fileId) + +suspend fun TelegramBot.convertToStorageFile( + content: MediaContent +): StorageFile = convertToStorageFile(content.media) diff --git a/tgbotapi.extensions.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt b/tgbotapi.extensions.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt index 507349899d..e0cfe38006 100644 --- a/tgbotapi.extensions.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt +++ b/tgbotapi.extensions.utils/src/jvmMain/kotlin/dev/inmo/tgbotapi/types/files/PathedFile.kt @@ -9,6 +9,7 @@ fun PathedFile.asStream( telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper ): InputStream = URL(this.fullUrl(telegramAPIUrlsKeeper)).openStream() +@Deprecated("This api will be removed soon. Use `downloadFile` instead") fun PathedFile.asFile( telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, dest: File = File.createTempFile(this.fileUniqueId, this.filename), @@ -22,6 +23,7 @@ fun PathedFile.asFile( return dest } +@Deprecated("This api will be removed soon. Use `downloadFile` instead") fun PathedFile.asBytes( telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper ): ByteArray = this.asStream(telegramAPIUrlsKeeper)