1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2024-11-26 03:58:44 +00:00

Merge pull request #699 from InsanusMokrassar/4.2.3

4.2.3
This commit is contained in:
InsanusMokrassar 2022-12-28 09:16:00 +06:00 committed by GitHub
commit 03f2f0e25b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 28 additions and 60 deletions

View File

@ -1,5 +1,12 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 4.2.3
* `Versions`:
* `MicroUtils`: `0.16.2` -> `0.16.4`
* `Core`:
* Simplify default `RequestsLimiter` (`ExceptionsOnlyLimiter`) (thanks to [@y9san9](https://github.com/y9san9) for help)
## 4.2.2 ## 4.2.2
* `Versions`: * `Versions`:

View File

@ -6,4 +6,4 @@ kotlin.incremental=true
kotlin.incremental.js=true kotlin.incremental.js=true
library_group=dev.inmo library_group=dev.inmo
library_version=4.2.2 library_version=4.2.3

View File

@ -13,7 +13,7 @@ ktor = "2.2.1"
ksp = "1.7.22-1.0.8" ksp = "1.7.22-1.0.8"
kotlin-poet = "1.12.0" kotlin-poet = "1.12.0"
microutils = "0.16.2" microutils = "0.16.4"
github-release-plugin = "2.4.1" github-release-plugin = "2.4.1"
dokka = "1.7.20" dokka = "1.7.20"

View File

@ -30,7 +30,7 @@ class KtorRequestsExecutor(
client: HttpClient = HttpClient(), client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(), callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false, excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(), private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat, private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) { ) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
@ -51,7 +51,7 @@ class KtorRequestsExecutor(
override suspend fun <T : Any> execute(request: Request<T>): T { override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely { return runCatchingSafely {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories) pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit { requestsLimiter.limit(request) {
var result: T? = null var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) { for (potentialFactory in callsFactories) {
@ -111,7 +111,7 @@ class KtorRequestsExecutorBuilder(
var client: HttpClient = HttpClient() var client: HttpClient = HttpClient()
var callsFactories: List<KtorCallFactory> = emptyList() var callsFactories: List<KtorCallFactory> = emptyList()
var excludeDefaultFactories: Boolean = false var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter() var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat var jsonFormatter: Json = nonstrictJsonFormat
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter) fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)

View File

@ -1,66 +1,18 @@
package dev.inmo.tgbotapi.bot.settings.limiters package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
import dev.inmo.tgbotapi.types.MilliSeconds
import dev.inmo.tgbotapi.types.RetryAfterError
import io.ktor.client.plugins.ClientRequestException
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
/** /**
* This limiter will limit requests only after getting a [RetryAfterError] or [ClientRequestException] with * Simple limiter which will lock any request when [TooMuchRequestsExceptions] is thrown and rerun request after lock time
* [HttpStatusCode.TooManyRequests] status code. Important thing is that in case if some of block has been blocked, all
* the others will wait until it will be possible to be called
*
* @param defaultTooManyRequestsDelay This parameter will be used in case of getting [ClientRequestException] with
* [HttpStatusCode.TooManyRequests] as a parameter for delay like it would be [TooMuchRequestsException]. The reason of
* it is that in [ClientRequestException] there is no information about required delay between requests
*/ */
class ExceptionsOnlyLimiter( object ExceptionsOnlyLimiter : RequestLimiter {
private val defaultTooManyRequestsDelay: MilliSeconds = 1000L
) : RequestLimiter {
private val lockState = MutableStateFlow(false)
private suspend fun lock(timeMillis: MilliSeconds) {
try {
safely {
lockState.emit(true)
delay(timeMillis)
}
} finally {
lockState.emit(false)
}
}
override suspend fun <T> limit(block: suspend () -> T): T { override suspend fun <T> limit(block: suspend () -> T): T {
while (true) { return try {
lockState.first { !it } block()
var throwable: Throwable? = null } catch (e: TooMuchRequestsException) {
val result = safely({ delay(e.retryAfter.leftToRetry)
throwable = when (it) { limit(block)
is TooMuchRequestsException -> {
lock(it.retryAfter.leftToRetry)
it
}
is ClientRequestException -> {
if (it.response.status == HttpStatusCode.TooManyRequests) {
lock(defaultTooManyRequestsDelay)
} else {
throw it
}
it
}
else -> throw it
}
null
}) {
block()
}
if (throwable == null) {
return result!!
}
} }
} }
} }

View File

@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.bot.settings.limiters
object NoLimitsLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T = block()
}

View File

@ -1,8 +1,12 @@
package dev.inmo.tgbotapi.bot.settings.limiters package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.tgbotapi.requests.abstracts.Request
interface RequestLimiter { interface RequestLimiter {
/** /**
* Use limit for working of block (like delay between or after, for example) * Use limit for working of block (like delay between or after, for example)
*/ */
suspend fun <T> limit(block: suspend () -> T): T suspend fun <T> limit(block: suspend () -> T): T
suspend fun <T : Any> limit(request: Request<T>, block: suspend () -> T) = limit(block)
} }