1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-23 02:28:45 +00:00

add streaming file downloading feature

This commit is contained in:
InsanusMokrassar 2021-08-09 23:22:08 +06:00
parent ea8db5b851
commit b475976ff1
6 changed files with 96 additions and 2 deletions

View File

@ -42,6 +42,14 @@ inline fun telegramBot(
crossinline builder: KtorRequestsExecutorBuilder.() -> Unit = {} crossinline builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, apiUrl), builder) ): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, apiUrl), builder)
@RiskFeature
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
SimpleRequestCallFactory(),
MultipartRequestCallFactory(),
DownloadFileRequestCallFactory,
DownloadFileChannelRequestCallFactory
)
class KtorRequestsExecutor( class KtorRequestsExecutor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper, telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(), client: HttpClient = HttpClient(),
@ -52,7 +60,7 @@ class KtorRequestsExecutor(
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run { private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) { if (!excludeDefaultFactories) {
this + listOf(SimpleRequestCallFactory(), MultipartRequestCallFactory(), DownloadFileRequestCallFactory) this + createTelegramBotDefaultKtorCallRequestsFactories()
} else { } else {
this this
} }

View File

@ -0,0 +1,38 @@
package dev.inmo.tgbotapi.bot.Ktor.base
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.InputStreamAllocator
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 <T : Any> makeCall(
client: HttpClient,
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>,
jsonFormatter: Json
): T? = (request as? DownloadFileStream) ?.let {
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
return InputStreamAllocator {
val scope = CoroutineScope(coroutineContext)
val outChannel = ByteChannel()
scope.launch {
client.get<HttpStatement>(fullUrl).execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.receive()
channel.copyTo(outChannel)
}
}
outChannel
} as T
}
}

View File

@ -16,7 +16,7 @@ object DownloadFileRequestCallFactory : KtorCallFactory {
request: Request<T>, request: Request<T>,
jsonFormatter: Json jsonFormatter: Json
): T? = (request as? DownloadFile) ?.let { ): T? = (request as? DownloadFile) ?.let {
val fullUrl = "${urlsKeeper.fileBaseUrl}/${it.filePath}" val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
return safely { return safely {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View File

@ -0,0 +1,15 @@
package dev.inmo.tgbotapi.requests
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.InputStreamAllocator
import dev.inmo.tgbotapi.utils.InputStreamAllocatorSerializer
import kotlinx.serialization.DeserializationStrategy
class DownloadFileStream(
val filePath: String
) : Request<InputStreamAllocator> {
override fun method(): String = filePath
override val resultDeserializer: DeserializationStrategy<InputStreamAllocator>
get() = InputStreamAllocatorSerializer
}

View File

@ -0,0 +1,31 @@
package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.common.ByteArrayAllocatorSerializer
import io.ktor.util.toByteArray
import io.ktor.utils.io.ByteReadChannel
import kotlinx.coroutines.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
fun interface InputStreamAllocator {
suspend operator fun invoke(): ByteReadChannel
}
object InputStreamAllocatorSerializer : KSerializer<InputStreamAllocator> {
override val descriptor: SerialDescriptor = ByteArrayAllocatorSerializer.descriptor
override fun serialize(encoder: Encoder, value: InputStreamAllocator) {
TODO("Not yet implemented")
// ByteArrayAllocatorSerializer.serialize(
// encoder
// ) {
// }
}
override fun deserialize(decoder: Decoder): InputStreamAllocator {
val byteArrayAllocator = ByteArrayAllocatorSerializer.deserialize(decoder)
return InputStreamAllocator { ByteReadChannel(byteArrayAllocator()) }
}
}

View File

@ -26,4 +26,6 @@ class TelegramAPIUrlsKeeper(
commonAPIUrl = "$correctedHost/bot$token" commonAPIUrl = "$correctedHost/bot$token"
fileBaseUrl = "$correctedHost/file/bot$token" fileBaseUrl = "$correctedHost/file/bot$token"
} }
fun createFileLinkUrl(filePath: String) = "${fileBaseUrl}/$filePath"
} }