mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2026-01-09 23:09:24 +00:00
small hotfix
This commit is contained in:
@@ -3,9 +3,24 @@ package dev.inmo.tgbotapi.bot.ktor
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
|
||||
interface TelegramBotPipelinesHandler {
|
||||
/**
|
||||
* Will be called when any exception will happen due to the [request] handling inside of limiter block. This method
|
||||
* will be called for each exception happened during call factory call
|
||||
*/
|
||||
suspend fun <T: Any> onRequestExceptionInLimiter(
|
||||
request: Request<T>,
|
||||
t: Throwable
|
||||
): T? = null
|
||||
|
||||
/**
|
||||
* Will be called when any exception will happen due to the [request] handling. If returns value - that value
|
||||
* will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
|
||||
* will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead. In difference with
|
||||
* [onRequestExceptionInLimiter], this method will be called only AFTER
|
||||
* [dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter] will pass result of call factory execution outside of
|
||||
* its [dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter.limit] function
|
||||
*
|
||||
* @see dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor
|
||||
* @see dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
|
||||
*/
|
||||
suspend fun <T: Any> onRequestException(
|
||||
request: Request<T>,
|
||||
|
||||
@@ -53,29 +53,33 @@ class DefaultKtorRequestsExecutor internal constructor(
|
||||
logger.v { "Start request $request" }
|
||||
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
|
||||
requestsLimiter.limit(request) {
|
||||
var result: T? = null
|
||||
lateinit var factoryHandledRequest: KtorCallFactory
|
||||
for (potentialFactory in callsFactories) {
|
||||
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
|
||||
logger.v { "Trying factory $potentialFactory for $request" }
|
||||
val resultFromFactory = potentialFactory.makeCall(
|
||||
client,
|
||||
telegramAPIUrlsKeeper,
|
||||
request,
|
||||
jsonFormatter
|
||||
)
|
||||
logger.v { "Result of factory $potentialFactory handling $request: $resultFromFactory" }
|
||||
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(resultFromFactory, request, potentialFactory)
|
||||
logger.v { "Result of pipeline $pipelineStepsHolder handling $resultFromFactory: $result" }
|
||||
if (result != null) {
|
||||
factoryHandledRequest = potentialFactory
|
||||
break
|
||||
runCatching {
|
||||
var result: T? = null
|
||||
lateinit var factoryHandledRequest: KtorCallFactory
|
||||
for (potentialFactory in callsFactories) {
|
||||
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
|
||||
logger.v { "Trying factory $potentialFactory for $request" }
|
||||
val resultFromFactory = potentialFactory.makeCall(
|
||||
client,
|
||||
telegramAPIUrlsKeeper,
|
||||
request,
|
||||
jsonFormatter
|
||||
)
|
||||
logger.v { "Result of factory $potentialFactory handling $request: $resultFromFactory" }
|
||||
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(resultFromFactory, request, potentialFactory)
|
||||
logger.v { "Result of pipeline $pipelineStepsHolder handling $resultFromFactory: $result" }
|
||||
if (result != null) {
|
||||
factoryHandledRequest = potentialFactory
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result ?.let {
|
||||
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
|
||||
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
|
||||
result ?.let {
|
||||
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
|
||||
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
|
||||
}.onFailure { e ->
|
||||
pipelineStepsHolder.onRequestExceptionInLimiter(request, e) ?.let { return@let it } ?: throw e
|
||||
}.getOrThrow()
|
||||
}
|
||||
}.let {
|
||||
val result = it.exceptionOrNull() ?.let { e ->
|
||||
|
||||
@@ -31,9 +31,16 @@ open 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,
|
||||
internal val onRequestExceptionInLimiter: (suspend (request: Request<*>, t: Throwable?) -> Any?)? = null,
|
||||
val id: String = uuid4().toString()
|
||||
) : TelegramBotPipelinesHandler {
|
||||
object ResultAbsence : Throwable()
|
||||
|
||||
override suspend fun <T : Any> onRequestExceptionInLimiter(request: Request<T>, t: Throwable): T? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return onRequestExceptionInLimiter ?.invoke(request, t) as? T
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> onRequestException(request: Request<T>, t: Throwable): T? {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return onRequestException ?.invoke(request, t) as? T
|
||||
|
||||
@@ -8,6 +8,7 @@ import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
class TelegramBotMiddlewareBuilder {
|
||||
var onRequestExceptionInLimiter: (suspend (request: Request<*>, t: Throwable?) -> Any?)? = null
|
||||
var onRequestException: (suspend (request: Request<*>, t: Throwable?) -> Any?)? = null
|
||||
var onBeforeSearchCallFactory: (suspend (request: Request<*>, callsFactories: List<KtorCallFactory>) -> Unit)? = null
|
||||
var onBeforeCallFactoryMakeCall: (suspend (request: Request<*>, potentialFactory: KtorCallFactory) -> Unit)? = null
|
||||
@@ -17,6 +18,12 @@ class TelegramBotMiddlewareBuilder {
|
||||
var onRequestReturnResult: (suspend (result: Result<*>, request: Request<*>, callsFactories: List<KtorCallFactory>) -> Result<Any?>?)? = null
|
||||
var id: String = uuid4().toString()
|
||||
|
||||
/**
|
||||
* Useful way to set [onRequestException]
|
||||
*/
|
||||
fun doOnRequestExceptionInLimiter(block: suspend (request: Request<*>, t: Throwable?) -> Any?) {
|
||||
onRequestExceptionInLimiter = block
|
||||
}
|
||||
/**
|
||||
* Useful way to set [onRequestException]
|
||||
*/
|
||||
@@ -63,6 +70,7 @@ class TelegramBotMiddlewareBuilder {
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
fun build(): TelegramBotMiddleware {
|
||||
return TelegramBotMiddleware(
|
||||
onRequestExceptionInLimiter = onRequestExceptionInLimiter,
|
||||
onRequestException = onRequestException,
|
||||
onBeforeSearchCallFactory = onBeforeSearchCallFactory,
|
||||
onBeforeCallFactoryMakeCall = onBeforeCallFactoryMakeCall,
|
||||
@@ -78,6 +86,7 @@ class TelegramBotMiddlewareBuilder {
|
||||
@Warning("This API is experimental and subject of changes")
|
||||
fun from(middleware: TelegramBotMiddleware, additionalSetup: TelegramBotMiddlewareBuilder.() -> Unit): TelegramBotMiddleware {
|
||||
return TelegramBotMiddlewareBuilder().apply {
|
||||
onRequestExceptionInLimiter = middleware.onRequestExceptionInLimiter
|
||||
onRequestException = middleware.onRequestException
|
||||
onBeforeSearchCallFactory = middleware.onBeforeSearchCallFactory
|
||||
onBeforeCallFactoryMakeCall = middleware.onBeforeCallFactoryMakeCall
|
||||
|
||||
@@ -10,6 +10,12 @@ import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
class TelegramBotMiddlewaresPipelinesHandler(
|
||||
private val middlewares: List<TelegramBotMiddleware> = emptyList()
|
||||
) : TelegramBotPipelinesHandler {
|
||||
override suspend fun <T : Any> onRequestExceptionInLimiter(request: Request<T>, t: Throwable): T? {
|
||||
return middlewares.firstNotNullOfOrNull {
|
||||
it.onRequestExceptionInLimiter(request, t)
|
||||
} ?: super.onRequestExceptionInLimiter(request, t)
|
||||
}
|
||||
|
||||
override suspend fun <T : Any> onRequestException(request: Request<T>, t: Throwable): T? {
|
||||
return middlewares.firstNotNullOfOrNull {
|
||||
it.onRequestException(request, t)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package dev.inmo.tgbotapi.bot.settings.limiters
|
||||
|
||||
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
|
||||
import dev.inmo.tgbotapi.utils.isCausedUnresolvedAddressException
|
||||
import io.ktor.util.network.UnresolvedAddressException
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
/**
|
||||
@@ -13,10 +15,10 @@ object ExceptionsOnlyLimiter : RequestLimiter {
|
||||
result = runCatching {
|
||||
block()
|
||||
}.onFailure {
|
||||
if (it is TooMuchRequestsException) {
|
||||
delay(it.retryAfter.leftToRetry)
|
||||
} else {
|
||||
throw it
|
||||
when {
|
||||
it.isCausedUnresolvedAddressException() -> delay(1000L)
|
||||
it is TooMuchRequestsException -> delay(it.retryAfter.leftToRetry)
|
||||
else -> throw it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.inmo.tgbotapi.utils
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun <T : Throwable> Throwable.causedBy(kclass: KClass<T>, additionalFilterOnHappened: (T) -> T? = { it }): T? {
|
||||
var current = this
|
||||
while (kclass.isInstance(current) == false) {
|
||||
when {
|
||||
kclass.isInstance(current) -> return additionalFilterOnHappened(current as T)
|
||||
else -> current = current.cause ?: return null
|
||||
}
|
||||
}
|
||||
|
||||
return current as T
|
||||
}
|
||||
@@ -14,16 +14,9 @@ import kotlinx.coroutines.CancellationException
|
||||
* @return the first [CancellationException] found in the cause chain, or `null` if none present
|
||||
*/
|
||||
fun Throwable.causedCancellationException(): CancellationException? {
|
||||
var current = this
|
||||
while (current !is CancellationException) {
|
||||
when {
|
||||
// It is possible, that API will be changed and cancellation will be caused by something else
|
||||
current is CancellationException && current.cause == null -> return current
|
||||
else -> current = current.cause ?: return null
|
||||
}
|
||||
return causedBy(CancellationException::class) {
|
||||
it.takeIf { it.cause == null }
|
||||
}
|
||||
|
||||
return current
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.inmo.tgbotapi.utils
|
||||
|
||||
import io.ktor.util.network.UnresolvedAddressException
|
||||
|
||||
fun Throwable.causedUnresolvedAddressException(): UnresolvedAddressException? {
|
||||
return causedBy(UnresolvedAddressException::class)
|
||||
}
|
||||
|
||||
fun Throwable.isCausedUnresolvedAddressException(): Boolean = causedUnresolvedAddressException() != null
|
||||
Reference in New Issue
Block a user