mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2024-11-24 02:58:43 +00:00
add ExceptionsThrottlerTelegramBotMiddleware
This commit is contained in:
parent
f5529033ec
commit
b8a6534b6a
@ -2,6 +2,10 @@
|
||||
|
||||
## 18.2.3
|
||||
|
||||
* `Core`:
|
||||
* Add default middleware `ExceptionsThrottlerTelegramBotMiddleware`
|
||||
* Make `TelegramBotMiddlewaresPipelinesHandler` to be default `TelegramBotPipelinesHandler`
|
||||
|
||||
## 18.2.2
|
||||
|
||||
* `Version`:
|
||||
|
@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.bot.ktor
|
||||
|
||||
import dev.inmo.kslog.common.KSLog
|
||||
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
|
||||
import dev.inmo.tgbotapi.bot.ktor.middlewares.TelegramBotMiddlewaresPipelinesHandler
|
||||
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
||||
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
@ -39,7 +40,7 @@ fun KtorRequestsExecutor(
|
||||
excludeDefaultFactories: Boolean = false,
|
||||
requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
|
||||
jsonFormatter: Json = nonstrictJsonFormat,
|
||||
pipelineStepsHolder: TelegramBotPipelinesHandler = TelegramBotPipelinesHandler,
|
||||
pipelineStepsHolder: TelegramBotPipelinesHandler = TelegramBotMiddlewaresPipelinesHandler(),
|
||||
logger: KSLog = DefaultKTgBotAPIKSLog,
|
||||
) = KtorRequestsExecutor(
|
||||
telegramAPIUrlsKeeper = telegramAPIUrlsKeeper,
|
||||
|
@ -27,7 +27,7 @@ class KtorRequestsExecutorBuilder(
|
||||
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
|
||||
var jsonFormatter: Json = nonstrictJsonFormat
|
||||
var logger: KSLog = DefaultKTgBotAPIKSLog
|
||||
var pipelineStepsHolder: TelegramBotPipelinesHandler = TelegramBotPipelinesHandler
|
||||
var pipelineStepsHolder: TelegramBotPipelinesHandler = TelegramBotMiddlewaresPipelinesHandler()
|
||||
|
||||
fun includeMiddlewares(block: TelegramBotMiddlewaresPipelinesHandler.Builder.() -> Unit) {
|
||||
pipelineStepsHolder = TelegramBotMiddlewaresPipelinesHandler.build(block)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dev.inmo.tgbotapi.bot.ktor.middlewares
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.common.Warning
|
||||
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||
import dev.inmo.tgbotapi.bot.ktor.TelegramBotPipelinesHandler
|
||||
@ -22,7 +23,7 @@ import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
* Non-null result of lambda will be used as the result of request handling
|
||||
*/
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
class TelegramBotMiddleware(
|
||||
open class TelegramBotMiddleware(
|
||||
internal val onRequestException: (suspend (request: Request<*>, t: Throwable?) -> Any?)? = null,
|
||||
internal val onBeforeSearchCallFactory: (suspend (request: Request<*>, callsFactories: List<KtorCallFactory>) -> Unit)? = null,
|
||||
internal val onBeforeCallFactoryMakeCall: (suspend (request: Request<*>, potentialFactory: KtorCallFactory) -> Unit)? = null,
|
||||
@ -30,6 +31,7 @@ class TelegramBotMiddleware(
|
||||
internal val onRequestResultPresented: (suspend (result: Any?, request: Request<*>, resultCallFactory: KtorCallFactory, callsFactories: List<KtorCallFactory>) -> Any?)? = null,
|
||||
internal val onRequestResultAbsent: (suspend (request: Request<*>, callsFactories: List<KtorCallFactory>) -> Any?)? = null,
|
||||
internal val onRequestReturnResult: (suspend (result: Result<*>, request: Request<*>, callsFactories: List<KtorCallFactory>) -> Result<Any?>?)? = null,
|
||||
val id: String = uuid4().toString()
|
||||
) : TelegramBotPipelinesHandler {
|
||||
object ResultAbsence : Throwable()
|
||||
override suspend fun <T : Any> onRequestException(request: Request<T>, t: Throwable): T? {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package dev.inmo.tgbotapi.bot.ktor.middlewares
|
||||
|
||||
import com.benasher44.uuid.uuid4
|
||||
import dev.inmo.micro_utils.common.Warning
|
||||
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||
import dev.inmo.tgbotapi.bot.ktor.TelegramBotPipelinesHandler
|
||||
@ -14,6 +15,7 @@ class TelegramBotMiddlewareBuilder {
|
||||
var onRequestResultPresented: (suspend (result: Any?, request: Request<*>, resultCallFactory: KtorCallFactory, callsFactories: List<KtorCallFactory>) -> Any?)? = null
|
||||
var onRequestResultAbsent: (suspend (request: Request<*>, callsFactories: List<KtorCallFactory>) -> Any?)? = null
|
||||
var onRequestReturnResult: (suspend (result: Result<*>, request: Request<*>, callsFactories: List<KtorCallFactory>) -> Result<Any?>?)? = null
|
||||
var id: String = uuid4().toString()
|
||||
|
||||
/**
|
||||
* Useful way to set [onRequestException]
|
||||
@ -67,7 +69,8 @@ class TelegramBotMiddlewareBuilder {
|
||||
onAfterCallFactoryMakeCall = onAfterCallFactoryMakeCall,
|
||||
onRequestResultPresented = onRequestResultPresented,
|
||||
onRequestResultAbsent = onRequestResultAbsent,
|
||||
onRequestReturnResult = onRequestReturnResult
|
||||
onRequestReturnResult = onRequestReturnResult,
|
||||
id = id
|
||||
)
|
||||
}
|
||||
|
||||
@ -82,6 +85,7 @@ class TelegramBotMiddlewareBuilder {
|
||||
onRequestResultPresented = middleware.onRequestResultPresented
|
||||
onRequestResultAbsent = middleware.onRequestResultAbsent
|
||||
onRequestReturnResult = middleware.onRequestReturnResult
|
||||
id = middleware.id
|
||||
additionalSetup()
|
||||
}.build()
|
||||
}
|
||||
|
@ -3,11 +3,12 @@ package dev.inmo.tgbotapi.bot.ktor.middlewares
|
||||
import dev.inmo.micro_utils.common.Warning
|
||||
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
|
||||
import dev.inmo.tgbotapi.bot.ktor.TelegramBotPipelinesHandler
|
||||
import dev.inmo.tgbotapi.bot.ktor.middlewares.builtins.ExceptionsThrottlerTelegramBotMiddleware
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
class TelegramBotMiddlewaresPipelinesHandler(
|
||||
private val middlewares: List<TelegramBotMiddleware>
|
||||
private val middlewares: List<TelegramBotMiddleware> = listOf(ExceptionsThrottlerTelegramBotMiddleware())
|
||||
) : TelegramBotPipelinesHandler {
|
||||
override suspend fun <T : Any> onRequestException(request: Request<T>, t: Throwable): T? {
|
||||
return middlewares.firstNotNullOfOrNull {
|
||||
@ -72,6 +73,7 @@ class TelegramBotMiddlewaresPipelinesHandler(
|
||||
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
class Builder {
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
val middlewares = mutableListOf<TelegramBotMiddleware>()
|
||||
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
|
@ -0,0 +1,59 @@
|
||||
package dev.inmo.tgbotapi.bot.ktor.middlewares.builtins
|
||||
|
||||
import dev.inmo.tgbotapi.bot.ktor.middlewares.TelegramBotMiddleware
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
import korlibs.time.milliseconds
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* @see invoke
|
||||
*/
|
||||
object ExceptionsThrottlerTelegramBotMiddleware {
|
||||
const val id: String = "ExceptionsThrottlerTelegramBotMiddleware"
|
||||
|
||||
/**
|
||||
* Creates [TelegramBotMiddleware] and configures it with next parameters:
|
||||
*
|
||||
* * [TelegramBotMiddleware.onRequestException] will throttle after exception if exception has happened before
|
||||
* * [TelegramBotMiddleware.onRequestReturnResult] will clear state of all exceptions happened with the [Request] if its
|
||||
* handling has been completed successfully
|
||||
*/
|
||||
operator fun invoke(
|
||||
exceptionDurationMultiplier: Float = 2f,
|
||||
initialExceptionDuration: Duration = 125.milliseconds,
|
||||
): TelegramBotMiddleware = TelegramBotMiddleware.build {
|
||||
val exceptionsTimeouts = mutableMapOf<KClass<*>, Duration>()
|
||||
val latestExceptionsRequestsTypes = mutableMapOf<KClass<*>, MutableSet<KClass<*>>>()
|
||||
val mutex = Mutex()
|
||||
onRequestException = onRequestException@{ request, t ->
|
||||
t ?: return@onRequestException null
|
||||
val kclass = t::class
|
||||
val toSleep = mutex.withLock {
|
||||
val latestDuration = exceptionsTimeouts[kclass]
|
||||
exceptionsTimeouts[kclass] = latestDuration ?.times(exceptionDurationMultiplier.toDouble()) ?: initialExceptionDuration
|
||||
latestExceptionsRequestsTypes.getOrPut(request::class) { mutableSetOf() }.add(kclass)
|
||||
latestDuration
|
||||
}
|
||||
toSleep ?.let {
|
||||
delay(it)
|
||||
}
|
||||
null
|
||||
}
|
||||
onRequestReturnResult = onRequestReturnResult@{ result, request, _ ->
|
||||
if (result.isSuccess) {
|
||||
mutex.withLock {
|
||||
val exceptionKClass = latestExceptionsRequestsTypes.remove(request::class) ?: return@withLock
|
||||
exceptionKClass.forEach {
|
||||
exceptionsTimeouts.remove(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
id = ExceptionsThrottlerTelegramBotMiddleware.id
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user