Skip to content

Commit 1b64c69

Browse files
committed
longPolling 增加配置 handleRetry, 配置文件方式支持 retryhandleRetry 配置
handleRetry 默认模式为 `TIMEOUT_ONLY` (与之前行为一致)
1 parent 3bbb681 commit 1b64c69

File tree

4 files changed

+107
-14
lines changed
  • simbot-component-telegram-api/src/commonMain/kotlin/love/forte/simbot/telegram/api/update
  • simbot-component-telegram-core/src/commonMain/kotlin/love/forte/simbot/component/telegram/core/bot
  • simbot-component-telegram-stdlib/src/commonMain/kotlin/love/forte/simbot/telegram/stdlib/bot

4 files changed

+107
-14
lines changed

simbot-component-telegram-api/src/commonMain/kotlin/love/forte/simbot/telegram/api/update/GetUpdatesApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public inline fun getUpdateFlow(
151151
eachLimit: Int? = null,
152152
allowedUpdates: Collection<String>? = null,
153153
crossinline onEachResult: (List<Update>) -> List<Update> = { it },
154-
crossinline onError: (Throwable) -> List<Update> = {
154+
crossinline onError: suspend (Throwable) -> List<Update> = {
155155
if (it is HttpRequestTimeoutException) emptyList() else throw it
156156
},
157157
crossinline requestor: suspend (GetUpdatesApi) -> List<Update>

simbot-component-telegram-core/src/commonMain/kotlin/love/forte/simbot/component/telegram/core/bot/SerializableTelegramBotConfiguration.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@ public data class SerializableTelegramBotConfiguration(
6868
val limit: Int? = null,
6969
val timeout: Int? = BotConfiguration.DefaultLongPollingTimeout.inWholeSeconds.toInt(),
7070
val allowedUpdates: Collection<String>? = null,
71+
val retry: LongPolling.Retry? = null,
72+
val handleRetry: LongPolling.HandleRetry? = null,
7173
) {
7274
public fun toBotLongPolling(): LongPolling = LongPolling(
7375
limit = limit,
7476
timeout = timeout,
7577
allowedUpdates = allowedUpdates,
76-
)
78+
retry = retry,
79+
).also {
80+
this.handleRetry?.also { thisHr -> it.handleRetry = thisHr }
81+
}
7782
}
7883

7984
public fun toBotConfiguration(): TelegramBotConfiguration {

simbot-component-telegram-stdlib/src/commonMain/kotlin/love/forte/simbot/telegram/stdlib/bot/Bot.kt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ public enum class SubscribeSequence {
358358
* The difference is that [timeout] defaults to [DefaultLongPollingTimeout]
359359
* instead of `null`.
360360
*
361+
* @property retry Retry config. [retry] is based on the Ktor plugin
362+
* [HttpTimeout][io.ktor.client.plugins.HttpRequestRetry] implementation.
363+
*
364+
* @property handleRetry Retry configuration for exception handling for long polling flow.
365+
* Unlike [retry], [handleRetry] is based on `onError` in [getUpdateFlow].
366+
*
361367
* @see getUpdateFlow
362368
*/
363369
@OptIn(InternalSimbotAPI::class)
@@ -366,15 +372,45 @@ public data class LongPolling(
366372
val timeout: Int? = DefaultLongPollingTimeout.inWholeSeconds.toInt(),
367373
val allowedUpdates: Collection<String>? = null,
368374
// retry times on error
369-
val retry: Retry? = null
375+
val retry: Retry? = null,
370376
) {
377+
var handleRetry: HandleRetry = HandleRetry.DEFAULT
371378

372379
@Serializable
373380
public data class Retry(
374381
val maxRetries: Int = 3,
375382
val delayMillis: Long = 5000,
376383
val isDelayMillisMultiplyByRetryTimes: Boolean = false
377384
)
385+
386+
@Serializable
387+
public data class HandleRetry(
388+
val strategy: HandleRetryStrategy = HandleRetryStrategy.TIMEOUT_ONLY,
389+
val delayMillis: Long = 5000,
390+
) {
391+
public companion object {
392+
internal val DEFAULT: HandleRetry = HandleRetry()
393+
}
394+
}
395+
396+
397+
public enum class HandleRetryStrategy {
398+
/**
399+
* Throw every exception, no retry.
400+
*/
401+
NONE,
402+
403+
/**
404+
* Catch [io.ktor.client.plugins.HttpRequestTimeoutException] only.
405+
*/
406+
TIMEOUT_ONLY,
407+
408+
/**
409+
* Always retry (except [CancellationException]).
410+
*/
411+
ALL,
412+
}
413+
378414
// multiplyBy
379415
}
380416

simbot-component-telegram-stdlib/src/commonMain/kotlin/love/forte/simbot/telegram/stdlib/bot/internal/BotImpl.kt

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ internal class BotImpl(
294294
}
295295
}
296296

297+
@Suppress("ThrowsCount")
297298
private suspend fun HttpClient.longPolling(
298299
token: String,
299300
server: Url?,
@@ -309,25 +310,76 @@ internal class BotImpl(
309310
eachLimit = limit,
310311
allowedUpdates = allowedUpdates,
311312
onError = { error ->
312-
when (error) {
313-
is CancellationException -> {
314-
eventLogger.trace("Handle an cancellation error on long polling task: {}", error.message, error)
315-
// throw, and stop the job.
313+
if (error is CancellationException) {
314+
eventLogger.trace("Handle an cancellation error on long polling task: {}", error.message, error)
315+
// throw, and stop the job.
316+
throw error
317+
}
318+
319+
val handleRetry = longPolling?.handleRetry ?: LongPolling.HandleRetry.DEFAULT
320+
val strategy = handleRetry.strategy
321+
val delay = handleRetry.delayMillis
322+
323+
when (strategy) {
324+
LongPolling.HandleRetryStrategy.NONE -> {
325+
eventLogger.error(
326+
"Handle an error on long polling task " +
327+
"with handle retry strategy {}: {}, " +
328+
"bot will be shutdown.",
329+
strategy,
330+
error.message,
331+
error
332+
)
333+
job.cancel(CancellationException("LongPolling on failure", error))
316334
throw error
317335
}
318336

319-
is HttpRequestTimeoutException -> {
320-
eventLogger.trace("Handle an timeout error on long polling task: {}", error.message, error)
337+
LongPolling.HandleRetryStrategy.TIMEOUT_ONLY -> {
338+
if (error is HttpRequestTimeoutException) {
339+
eventLogger.debug(
340+
"Handle an timeout error " +
341+
"on long polling task: {}, just re-poll.",
342+
error.message,
343+
error
344+
)
345+
} else {
346+
eventLogger.error(
347+
"Handle an error on long polling task " +
348+
"with handle retry strategy {}: {}, " +
349+
"bot will be shutdown.",
350+
strategy,
351+
error.message,
352+
error
353+
)
354+
// throw to Bot
355+
job.cancel(CancellationException("LongPolling on failure", error))
356+
throw error
357+
}
321358
}
322359

323-
else -> {
324-
eventLogger.error("Handle an error on long polling task: {}", error.message, error)
325-
// throw to Bot
326-
job.cancel(CancellationException("LongPolling on failure", error))
327-
throw error
360+
LongPolling.HandleRetryStrategy.ALL -> {
361+
if (error is HttpRequestTimeoutException) {
362+
eventLogger.debug(
363+
"Handle an timeout error " +
364+
"on long polling task: {}, just re-poll.",
365+
error.message,
366+
error
367+
)
368+
} else {
369+
eventLogger.debug(
370+
"Handle an error on long polling task " +
371+
"with handle retry strategy {}: {}, " +
372+
"retry in {} ms",
373+
strategy,
374+
error.message,
375+
delay,
376+
error
377+
)
378+
}
328379
}
329380
}
330381

382+
delay(delay)
331383
emptyList()
332384
}
333385
) { api ->

0 commit comments

Comments
 (0)