package com.github.insanusmokrassar.TelegramBotAPI.bot.settings.limiters import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel private fun now(): Long = System.currentTimeMillis() class CommonLimiter( private val lockCount: Int = 10, private val regenTime: Long = 20 * 1000L // 20 seconds for full regen of opportunity to send message ) : RequestLimiter { private var doLimit: Boolean = false private val counterChannel = Channel(Channel.UNLIMITED) private val scope = CoroutineScope(Dispatchers.Default) private val counterJob = scope.launch { var wasLastSecond = 0 var lastCountTime = now() var limitManagementJob: Job? = null var removeLimitTime: Long = lastCountTime for (counter in counterChannel) { val now = now() if (now - lastCountTime > 1000) { lastCountTime = now wasLastSecond = 1 } else { wasLastSecond++ } if (wasLastSecond >= lockCount) { removeLimitTime = now + regenTime if (limitManagementJob == null) { limitManagementJob = launch { doLimit = true var internalNow = now() while (internalNow < removeLimitTime) { delay(removeLimitTime - internalNow) internalNow = now() } doLimit = false } } } if (now > removeLimitTime) { limitManagementJob = null } } } private val quoterChannel = Channel(Channel.CONFLATED) private val tickerJob = scope.launch { while (isActive) { quoterChannel.send(Unit) delay(1000L) } } override suspend fun limit(block: suspend () -> T): T { counterChannel.send(Unit) return if (!doLimit) { block() } else { quoterChannel.receive() block() } } }