mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-22 16:23:48 +00:00
commit
03f2f0e25b
@ -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`:
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
|
@ -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!!
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package dev.inmo.tgbotapi.bot.settings.limiters
|
||||||
|
|
||||||
|
object NoLimitsLimiter : RequestLimiter {
|
||||||
|
override suspend fun <T> limit(block: suspend () -> T): T = block()
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user