diff --git a/CHANGELOG.md b/CHANGELOG.md index 4af16614a7..47d9236546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,16 @@ must be regular text * `RequestsExecutor#executeAsync(Request, CoroutineScope)` now will return `Deferred` for cases when you need result * `RequestsExecutor#executeUnsafe` will automatically retry request if it was unsuccessful and retries > 0 +### 0.9.3 + +* `KtorRequestsExecutor` now can use custom `JSON` string formatter (by default - non strict) +* `ResponseParameters` renamed to `Response` +* Add `RequestError` sealed class and described in documentation known errors +* Add `ResponseParametersRaw` which can create error based on input parameters +* Add `parameters` field in `Response` and remove useless fields from `Response` +* Add `leftToRetry` parameter in `RetryAfterError` +* Add handling of `RetryAfterError` in `KtorRequestsExecutor` + ### 0.8.5 * Add extension `String#toMarkdown` diff --git a/build.gradle b/build.gradle index a45b9c6d9f..e08085e6e6 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -project.version = "0.9.2" +project.version = "0.9.3" project.group = "com.github.insanusmokrassar" buildscript { diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt index 7660088d98..a9c7e94aa1 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/Ktor/KtorRequestsExecutor.kt @@ -7,12 +7,14 @@ import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestException 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.ResponseParameters +import com.github.insanusmokrassar.TelegramBotAPI.types.Response +import com.github.insanusmokrassar.TelegramBotAPI.types.RetryAfterError import io.ktor.client.HttpClient import io.ktor.client.call.HttpClientCall import io.ktor.client.engine.HttpClientEngine import io.ktor.client.engine.okhttp.OkHttp import io.ktor.util.cio.toByteArray +import kotlinx.coroutines.delay import kotlinx.io.charsets.Charset import kotlinx.serialization.json.JSON @@ -22,7 +24,8 @@ class KtorRequestsExecutor( hostUrl: String = "https://api.telegram.org", callsFactories: List = emptyList(), excludeDefaultFactories: Boolean = false, - private val requestsLimiter: RequestLimiter = EmptyLimiter + private val requestsLimiter: RequestLimiter = EmptyLimiter, + private val jsonFormatter: JSON = JSON.nonstrict ) : BaseRequestsExecutor(token, hostUrl) { constructor( token: String, @@ -59,11 +62,19 @@ class KtorRequestsExecutor( throw IllegalArgumentException("Can't execute request: $request") } val content = call.response.content.toByteArray().toString(Charset.defaultCharset()) - val responseObject = JSON.parse( - ResponseParameters.serializer(request.resultSerializer()), + val responseObject = jsonFormatter.parse( + Response.serializer(request.resultSerializer()), content ) - responseObject.result ?: call.let { + responseObject.result ?: responseObject.parameters ?.let { + val error = it.error + if (error is RetryAfterError) { + delay(error.leftToRetry) + execute(request) + } else { + null + } + } ?: call.let { throw RequestException( responseObject, "Can't get result object" diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestException.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestException.kt index 5bba979f4b..f58666a347 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestException.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/RequestException.kt @@ -1,10 +1,10 @@ package com.github.insanusmokrassar.TelegramBotAPI.bot -import com.github.insanusmokrassar.TelegramBotAPI.types.ResponseParameters +import com.github.insanusmokrassar.TelegramBotAPI.types.Response import java.io.IOException class RequestException( - val response: ResponseParameters<*>, + val response: Response<*>, message: String? = null, cause: Throwable? = null ) : IOException( diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/abstracts/Request.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/abstracts/Request.kt index ab1df291cf..7ab7129b23 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/abstracts/Request.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/requests/abstracts/Request.kt @@ -1,6 +1,6 @@ package com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts -import com.github.insanusmokrassar.TelegramBotAPI.types.ResponseParameters +import com.github.insanusmokrassar.TelegramBotAPI.types.Response import com.github.insanusmokrassar.TelegramBotAPI.utils.toJsonWithoutNulls import kotlinx.serialization.* import kotlinx.serialization.json.JsonObject @@ -15,6 +15,6 @@ interface Request { fun StringFormat.extractResult( from: String, dataSerializer: KSerializer -): ResponseParameters { - return parse(ResponseParameters.serializer(dataSerializer), from) +): Response { + return parse(Response.serializer(dataSerializer), from) } \ No newline at end of file diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/RequestError.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/RequestError.kt new file mode 100644 index 0000000000..4579ed914f --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/RequestError.kt @@ -0,0 +1,20 @@ +package com.github.insanusmokrassar.TelegramBotAPI.types + +import java.util.concurrent.TimeUnit + +sealed class RequestError + +data class RetryAfterError( + val seconds: Long, + val startCountingMillis: Long +) : RequestError() { + val canContinue = TimeUnit.SECONDS.toMillis(seconds) + startCountingMillis + val leftToRetry: Long + get() = canContinue - System.currentTimeMillis() +} + +data class MigrateChatId( + val newChatId: ChatId +) : RequestError() + + diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Response.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Response.kt new file mode 100644 index 0000000000..a0089723fe --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/Response.kt @@ -0,0 +1,25 @@ +package com.github.insanusmokrassar.TelegramBotAPI.types + +import kotlinx.serialization.* +import org.joda.time.DateTime +import java.util.concurrent.TimeUnit + +@Deprecated( + "Deprecated because incorrect name", + ReplaceWith("Response") +) +typealias ResponseParameters = Response + +@Serializable +data class Response( + val ok: Boolean = false, + @Optional + val description: String? = null, + @SerialName("error_code") + @Optional + val errorCode: Int? = null, + @Optional + val result: T? = null, + @Optional + val parameters: ResponseParametersRaw? = null +) diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParameters.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParameters.kt deleted file mode 100644 index d038f3a323..0000000000 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParameters.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.insanusmokrassar.TelegramBotAPI.types - -import kotlinx.serialization.* -import org.joda.time.DateTime -import java.util.concurrent.TimeUnit - -@Serializable -data class ResponseParameters( - val ok: Boolean = false, - @Optional - val description: String? = null, - @SerialName("migrate_to_chat_id") - @Optional - val migrateToChatId: Identifier? = null, - @SerialName("retry_after") - @Optional - val retryAfter: Int? = null, - @SerialName("error_code") - @Optional - val errorCode: Int? = null, - @Optional - val result: T? = null -) { - @Transient - val waitUntil: DateTime? by lazy { - retryAfter ?.let { - DateTime.now().plus(TimeUnit.SECONDS.toMillis(it.toLong())) - } - } -} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParametersRaw.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParametersRaw.kt new file mode 100644 index 0000000000..335b7df35f --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/types/ResponseParametersRaw.kt @@ -0,0 +1,24 @@ +package com.github.insanusmokrassar.TelegramBotAPI.types + +import kotlinx.serialization.* + +@Serializable +data class ResponseParametersRaw( + @SerialName("migrate_to_chat_id") + @Optional + private val migrateToChatId: ChatId? = null, + @SerialName("retry_after") + @Optional + private val retryAfter: Long? = null +) { + @Transient + private val createTime: Long = System.currentTimeMillis() + @Transient + val error: RequestError? by lazy { + when { + migrateToChatId != null -> MigrateChatId(migrateToChatId); + retryAfter != null -> RetryAfterError(retryAfter, createTime); + else -> null + } + } +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/RequestsExecutor.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/RequestsExecutor.kt index 0264a29788..4c3b8bf9f4 100644 --- a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/RequestsExecutor.kt +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/utils/extensions/RequestsExecutor.kt @@ -4,7 +4,7 @@ import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestException import com.github.insanusmokrassar.TelegramBotAPI.bot.RequestsExecutor import com.github.insanusmokrassar.TelegramBotAPI.requests.* import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.Request -import com.github.insanusmokrassar.TelegramBotAPI.types.ResponseParameters +import com.github.insanusmokrassar.TelegramBotAPI.types.Response import com.github.insanusmokrassar.TelegramBotAPI.types.UpdateIdentifier import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.MediaGroupMessage import com.github.insanusmokrassar.TelegramBotAPI.types.update.* @@ -147,7 +147,7 @@ fun RequestsExecutor.startGettingOfUpdates( fun RequestsExecutor.executeAsync( request: Request, - onFail: (suspend (ResponseParameters<*>) -> Unit)? = null, + onFail: (suspend (Response<*>) -> Unit)? = null, scope: CoroutineScope = GlobalScope, onSuccess: (suspend (T) -> Unit)? = null ): Job {