package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlin.coroutines.* import kotlin.math.pow private sealed class RequestEvent private class AddRequest( val continuation: Continuation ) : RequestEvent() private object CompleteRequest : RequestEvent() @Serializable data class PowLimiter( private val minAwaitTime: Long = 0L, private val maxAwaitTime: Long = 10000L, private val powValue: Double = 4.0, private val powK: Double = 0.0016 ) : RequestLimiter { @Transient private val scope = CoroutineScope(Dispatchers.Default) @Transient private val eventsChannel = Channel(Channel.UNLIMITED) @Transient private val awaitTimeRange = minAwaitTime .. maxAwaitTime init { scope.launch { var requestsInWork: Double = 0.0 for (event in eventsChannel) { when (event) { is AddRequest -> { val awaitTime = (((requestsInWork.pow(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) } } }