diff --git a/CHANGELOG b/CHANGELOG index 0e9898854b..c8b8b4c1ab 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -49,3 +49,4 @@ `com.github.insanusmokrassar.TelegramBotAPI.bot.Ktor` package * Replace `ProxySettings` data class in `settings` package, deprecate old link * `BaseRequestsExecutor` now have no it's own scope +* Add `RequestLimiter` and base realisations diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/EmptyLimiter.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/EmptyLimiter.kt new file mode 100644 index 0000000000..c0a24f1885 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/EmptyLimiter.kt @@ -0,0 +1,5 @@ +package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters + +object EmptyLimiter : RequestLimiter { + override suspend fun limit(block: suspend () -> T): T = block() +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/PowLimiter.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/PowLimiter.kt new file mode 100644 index 0000000000..d20bbc194d --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/PowLimiter.kt @@ -0,0 +1,73 @@ +package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.serialization.Optional +import kotlinx.serialization.Serializable +import java.util.concurrent.Executors +import kotlin.coroutines.* + +private sealed class RequestEvent +private class AddRequest( + val continuation: Continuation +) : RequestEvent() +private object CompleteRequest : RequestEvent() + +@Serializable +data class PowLimiter( + @Optional + private val minAwaitTime: Long = 0L, + @Optional + private val maxAwaitTime: Long = 10000L, + @Optional + private val powValue: Double = 4.0, + @Optional + private val powK: Double = 0.0016 +) : RequestLimiter { + private val scope = CoroutineScope( + Executors.newFixedThreadPool(3).asCoroutineDispatcher() + ) + private val eventsChannel = Channel(Channel.UNLIMITED) + private val awaitTimeRange = minAwaitTime .. maxAwaitTime + + init { + scope.launch { + var requestsInWork: Double = 0.0 + for (event in eventsChannel) { + when (event) { + is AddRequest -> { + val awaitTime = ((Math.pow(requestsInWork, powValue) * powK) * 1000L).toLong() + requestsInWork++ + + event.continuation.resume( + if (awaitTime in awaitTimeRange) { + awaitTime + } else { + if (awaitTime < minAwaitTime) { + minAwaitTime + } else { + maxAwaitTime + } + } + ) + } + is CompleteRequest -> requestsInWork-- + } + } + } + } + + override suspend fun limit( + block: suspend () -> T + ): T { + val delayMillis = suspendCoroutine { + scope.launch { eventsChannel.send(AddRequest(it)) } + } + delay(delayMillis) + return try { + block() + } finally { + eventsChannel.send(CompleteRequest) + } + } +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/RequestLimiter.kt b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/RequestLimiter.kt new file mode 100644 index 0000000000..8abc707151 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/TelegramBotAPI/bot/settings/limiters/RequestLimiter.kt @@ -0,0 +1,8 @@ +package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters + +interface RequestLimiter { + /** + * Use limit for working of block (like delay between or after, for example) + */ + suspend fun limit(block: suspend () -> T): T +}