1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-10-31 21:43:48 +00:00

Merge pull request #124 from sleshJdev/feature/download-pathed-file-extension

Extended PathedFile to get it as file/stream
This commit is contained in:
InsanusMokrassar 2020-08-23 21:44:53 +06:00 committed by GitHub
commit 0472e35752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 204 additions and 82 deletions

View File

@ -21,6 +21,21 @@
* All deprecations from previous versions were removed
* `TelegramBotAPI-core`:
* Typealias `TelegramBot` was added
* Fully rebuilt `KtorCallFactory` interface to be able to handle custom answers from telegram bot api system
* New implementation of `KtorCallFactory` was added: `DownloadFileRequestCallFactory`
* `DownloadFile` request was added
* All included `KtorCallFactory` realizations (except of abstract) now are objects:
* `MultipartRequestCallFactory`
* `SimpleRequestCallFactory`
* `TelegramBotAPI-extensions-api`:
* Extensions `TelegramBot#downloadFile` were added
* `TelegramBotAPI-extensions-utils`:
* All extensions for media groups (except of `mediaGroupId`) have changed their context: `List<MediaGroupMessage>`
-> `List<CommonMessage<MediaGroupContent>>`
* `forwardInfo`
* `replyTo`
* `chat`
* `createResend` (several extensions)
## 0.27.0

View File

@ -1,13 +1,15 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient
import io.ktor.client.statement.HttpStatement
import kotlinx.serialization.json.Json
interface KtorCallFactory {
suspend fun <T: Any> prepareCall(
suspend fun <T: Any> makeCall(
client: HttpClient,
baseUrl: String,
request: Request<T>
) : HttpStatement?
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>,
jsonFormatter: Json
): T?
}

View File

@ -1,21 +1,17 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor
import com.github.insanusmokrassar.TelegramBotAPI.bot.BaseRequestsExecutor
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.MultipartRequestCallFactory
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.SimpleRequestCallFactory
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base.*
import com.github.insanusmokrassar.TelegramBotAPI.bot.exceptions.newRequestException
import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters.EmptyLimiter
import com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters.RequestLimiter
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import com.github.insanusmokrassar.TelegramBotAPI.types.Response
import com.github.insanusmokrassar.TelegramBotAPI.types.RetryAfterError
import com.github.insanusmokrassar.TelegramBotAPI.utils.*
import io.ktor.client.HttpClient
import io.ktor.client.call.receive
import io.ktor.client.features.*
import io.ktor.client.statement.HttpStatement
import io.ktor.client.statement.readText
import kotlinx.coroutines.delay
import kotlinx.serialization.json.Json
class KtorRequestsExecutor(
@ -28,7 +24,7 @@ class KtorRequestsExecutor(
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) {
asSequence().plus(SimpleRequestCallFactory()).plus(MultipartRequestCallFactory()).toList()
this + listOf(SimpleRequestCallFactory, MultipartRequestCallFactory, DownloadFileRequestCallFactory)
} else {
this
}
@ -57,39 +53,20 @@ class KtorRequestsExecutor(
}
) {
requestsLimiter.limit {
var statement: HttpStatement? = null
for (factory in callsFactories) {
statement = factory.prepareCall(
var result: T? = null
for (potentialFactory in callsFactories) {
result = potentialFactory.makeCall(
client,
telegramAPIUrlsKeeper.commonAPIUrl,
request
telegramAPIUrlsKeeper,
request,
jsonFormatter
)
if (statement != null) {
if (result != null) {
break
}
}
val response = statement?.execute() ?: throw IllegalArgumentException("Can't execute request: $request")
val content = response.receive<String>()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
(responseObject.result?.let {
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
} ?: responseObject.parameters?.let {
val error = it.error
if (error is RetryAfterError) {
delay(error.leftToRetry)
execute(request)
} else {
null
}
} ?: response.let {
throw newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
})
result ?: error("Can't execute request: $request")
}
}
}

View File

@ -1,55 +1,79 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorCallFactory
import com.github.insanusmokrassar.TelegramBotAPI.bot.exceptions.newRequestException
import com.github.insanusmokrassar.TelegramBotAPI.requests.GetUpdates
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import com.github.insanusmokrassar.TelegramBotAPI.types.Response
import com.github.insanusmokrassar.TelegramBotAPI.types.RetryAfterError
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient
import io.ktor.client.call.receive
import io.ktor.client.features.timeout
import io.ktor.client.request.*
import io.ktor.client.statement.HttpStatement
import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType
import io.ktor.http.HttpMethod
import kotlinx.coroutines.delay
import kotlinx.serialization.json.Json
import kotlin.collections.set
abstract class AbstractRequestCallFactory : KtorCallFactory {
private val methodsCache: MutableMap<String, String> = mutableMapOf()
override suspend fun <T : Any> prepareCall(
override suspend fun <T : Any> makeCall(
client: HttpClient,
baseUrl: String,
request: Request<T>
): HttpStatement? {
val preparedBody = prepareCallBody(client, baseUrl, request) ?: return null
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>,
jsonFormatter: Json
): T? {
val preparedBody = prepareCallBody(client, urlsKeeper, request) ?: return null
return HttpStatement(
HttpRequestBuilder().apply {
url(
methodsCache[request.method()] ?: "$baseUrl/${request.method()}".also {
methodsCache[request.method()] = it
}
)
method = HttpMethod.Post
accept(ContentType.Application.Json)
client.post<HttpResponse> {
url(
methodsCache[request.method()] ?: "${urlsKeeper.commonAPIUrl}/${request.method()}".also {
methodsCache[request.method()] = it
}
)
accept(ContentType.Application.Json)
if (request is GetUpdates) {
request.timeout ?.times(1000L) ?.let { customTimeoutMillis ->
if (customTimeoutMillis > 0) {
timeout {
requestTimeoutMillis = customTimeoutMillis
socketTimeoutMillis = customTimeoutMillis
}
if (request is GetUpdates) {
request.timeout?.times(1000L)?.let { customTimeoutMillis ->
if (customTimeoutMillis > 0) {
timeout {
requestTimeoutMillis = customTimeoutMillis
socketTimeoutMillis = customTimeoutMillis
}
}
}
}
body = preparedBody
},
client
)
body = preparedBody
}.let { response ->
val content = response.receive<String>()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
return (responseObject.result?.let {
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
} ?: responseObject.parameters?.let {
val error = it.error
if (error is RetryAfterError) {
delay(error.leftToRetry)
makeCall(client, urlsKeeper, request, jsonFormatter)
} else {
null
}
} ?: response.let {
throw newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
})
}
}
protected abstract fun <T : Any> prepareCallBody(
client: HttpClient,
baseUrl: String,
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>
): Any?
}

View File

@ -0,0 +1,29 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base
import com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.KtorCallFactory
import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor
import com.github.insanusmokrassar.TelegramBotAPI.requests.DownloadFile
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import com.github.insanusmokrassar.TelegramBotAPI.utils.handleSafely
import io.ktor.client.HttpClient
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.HttpMethod
import kotlinx.serialization.json.Json
object DownloadFileRequestCallFactory : KtorCallFactory {
override suspend fun <T : Any> makeCall(
client: HttpClient,
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>,
jsonFormatter: Json
): T? = (request as? DownloadFile) ?.let {
val fullUrl = "${urlsKeeper.fileBaseUrl}/${it.filePath}"
return handleSafely {
@Suppress("UNCHECKED_CAST")
client.get<ByteArray>(fullUrl) as T // always ByteArray
}
}
}

View File

@ -1,6 +1,7 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import com.github.insanusmokrassar.TelegramBotAPI.utils.mapWithCommonValues
import io.ktor.client.HttpClient
import io.ktor.client.request.forms.MultiPartFormDataContent
@ -8,11 +9,10 @@ import io.ktor.client.request.forms.formData
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
class MultipartRequestCallFactory : AbstractRequestCallFactory() {
object MultipartRequestCallFactory : AbstractRequestCallFactory() {
override fun <T : Any> prepareCallBody(
client: HttpClient,
baseUrl: String,
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>
): Any? = (request as? MultipartRequest) ?.let { castedRequest ->
MultiPartFormDataContent(

View File

@ -1,14 +1,15 @@
package com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor.base
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.http.content.TextContent
class SimpleRequestCallFactory : AbstractRequestCallFactory() {
object SimpleRequestCallFactory : AbstractRequestCallFactory() {
override fun <T : Any> prepareCallBody(
client: HttpClient,
baseUrl: String,
urlsKeeper: TelegramAPIUrlsKeeper,
request: Request<T>
): Any? = (request as? SimpleRequest<T>) ?.let { _ ->
val content = request.json().toString()

View File

@ -0,0 +1,14 @@
package com.github.insanusmokrassar.TelegramBotAPI.requests
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.builtins.ByteArraySerializer
class DownloadFile(
val filePath: String
) : Request<ByteArray> {
override fun method(): String = filePath
override val resultDeserializer: DeserializationStrategy<ByteArray>
get() = ByteArraySerializer()
}

View File

@ -24,13 +24,12 @@ data class InputMediaVideo(
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
@Transient
override val arguments: JsonElement = buildArguments(serializer())
@SerialName(mediaField)
val media: String = when (file) {
is FileId -> file.fileId
is MultipartFile -> file.fileId.toInputMediaFileAttachmentName()
}
@Transient
override val arguments: JsonElement = buildArguments(serializer())
}

View File

@ -0,0 +1,32 @@
package com.github.insanusmokrassar.TelegramBotAPI.extensions.api
import com.github.insanusmokrassar.TelegramBotAPI.bot.TelegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.get.getFileAdditionalInfo
import com.github.insanusmokrassar.TelegramBotAPI.requests.DownloadFile
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.FileId
import com.github.insanusmokrassar.TelegramBotAPI.types.files.PathedFile
import com.github.insanusmokrassar.TelegramBotAPI.types.files.abstracts.TelegramMediaFile
suspend fun TelegramBot.downloadFile(
filePath: String
): ByteArray = execute(
DownloadFile(filePath)
)
suspend fun TelegramBot.downloadFile(
pathedFile: PathedFile
): ByteArray = execute(
DownloadFile(pathedFile.filePath)
)
suspend fun TelegramBot.downloadFile(
fileId: FileId
): ByteArray = downloadFile(
getFileAdditionalInfo(fileId)
)
suspend fun TelegramBot.downloadFile(
file: TelegramMediaFile
): ByteArray = downloadFile(
getFileAdditionalInfo(file)
)

View File

@ -4,15 +4,15 @@ import com.github.insanusmokrassar.TelegramBotAPI.requests.send.media.SendMediaG
import com.github.insanusmokrassar.TelegramBotAPI.types.*
import com.github.insanusmokrassar.TelegramBotAPI.types.chat.abstracts.Chat
import com.github.insanusmokrassar.TelegramBotAPI.types.message.ForwardInfo
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.MediaGroupMessage
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.Message
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.*
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MediaGroupContent
import com.github.insanusmokrassar.TelegramBotAPI.types.update.MediaGroupUpdates.SentMediaGroupUpdate
val List<MediaGroupMessage>.forwardInfo: ForwardInfo?
val List<CommonMessage<MediaGroupContent>>.forwardInfo: ForwardInfo?
get() = firstOrNull() ?.forwardInfo
val List<MediaGroupMessage>.replyTo: Message?
val List<CommonMessage<MediaGroupContent>>.replyTo: Message?
get() = firstOrNull() ?.replyTo
val List<MediaGroupMessage>.chat: Chat?
val List<CommonMessage<MediaGroupContent>>.chat: Chat?
get() = firstOrNull() ?.chat
val List<MediaGroupMessage>.mediaGroupId: MediaGroupIdentifier?
get() = firstOrNull() ?.mediaGroupId
@ -26,7 +26,7 @@ val SentMediaGroupUpdate.chat: Chat
val SentMediaGroupUpdate.mediaGroupId: MediaGroupIdentifier
get() = data.mediaGroupId!!
fun List<MediaGroupMessage>.createResend(
fun List<CommonMessage<MediaGroupContent>>.createResend(
chatId: ChatId,
disableNotification: Boolean = false,
replyTo: MessageIdentifier? = null
@ -37,7 +37,7 @@ fun List<MediaGroupMessage>.createResend(
replyTo
)
fun List<MediaGroupMessage>.createResend(
fun List<CommonMessage<MediaGroupContent>>.createResend(
chat: Chat,
disableNotification: Boolean = false,
replyTo: MessageIdentifier? = null

View File

@ -0,0 +1,29 @@
package com.github.insanusmokrassar.TelegramBotAPI.types.files
import com.github.insanusmokrassar.TelegramBotAPI.utils.TelegramAPIUrlsKeeper
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
import java.net.URL
fun PathedFile.asStream(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
): InputStream = URL(this.fullUrl(telegramAPIUrlsKeeper)).openStream()
fun PathedFile.asFile(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
dest: File = File.createTempFile(this.fileUniqueId, this.filename),
defaultBufferSize: Int = DEFAULT_BUFFER_SIZE
): File {
this.asStream(telegramAPIUrlsKeeper).use { input ->
FileOutputStream(dest).use { out ->
input.copyTo(out, defaultBufferSize)
}
}
return dest
}
fun PathedFile.asBytes(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
): ByteArray = this.asStream(telegramAPIUrlsKeeper)
.use { input -> input.readBytes() }