diff --git a/CHANGELOG.md b/CHANGELOG.md index 96438400f0..f3de349b49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # TelegramBotAPI changelog +## 1.1.2 + +* `Core`: + * Rename of `TelegramAPIUrlsKeeper#checkWebAppLink` -> `TelegramAPIUrlsKeeper#checkWebAppData` (fix of [#591](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/591)) +* `Behaviour Builder with FSM`: + * New typealias `StateHandlingErrorHandler` + * `BehaviourBuilderWithFSM` now accepts new parameter `onStateHandlingErrorHandler` which will be used in case if state has not been successfully completed + ## 1.1.1 * `Versions`: diff --git a/gradle.properties b/gradle.properties index 2d0fa06f61..38dfa5cc1e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,6 +20,6 @@ javax_activation_version=1.1.1 dokka_version=1.6.21 library_group=dev.inmo -library_version=1.1.1 +library_version=1.1.2 github_release_plugin_version=2.3.7 diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt index 30ebf58bc3..caeb9b2b2e 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSM.kt @@ -1,7 +1,6 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder -import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions -import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.micro_utils.coroutines.* import dev.inmo.micro_utils.fsm.common.* import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.types.update.abstracts.Update @@ -50,12 +49,32 @@ interface BehaviourContextWithFSM : BehaviourContext, StatesMachine? ): BehaviourContextWithFSM + fun copy( + bot: TelegramBot = this.bot, + scope: CoroutineScope = this.scope, + broadcastChannelsSize: Int = 100, + onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND, + upstreamUpdatesFlow: Flow? = null, + triggersHolder: TriggersHolder = this.triggersHolder, + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), + updatesFilter: BehaviourContextAndTypeReceiver? = null + ): BehaviourContextWithFSM = copy( + bot, + scope, + broadcastChannelsSize, + onBufferOverflow, + upstreamUpdatesFlow, + triggersHolder, + updatesFilter + ) + companion object { operator fun invoke( behaviourContext: BehaviourContext, handlers: List>, - statesManager: StatesManager - ) = DefaultBehaviourContextWithFSM(behaviourContext, statesManager, handlers) + statesManager: StatesManager, + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler() + ) = DefaultBehaviourContextWithFSM(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler) } } @@ -84,16 +103,26 @@ inline fun BehaviourContextWithFSM.strictlyOn(handl /** * Default realization of [BehaviourContextWithFSM]. It uses [behaviourContext] as a base for this object as * [BehaviourContext], but managing substates contexts updates for avoiding of updates lost between states + * @param onStateHandlingErrorHandler Will be used in case if state handling has not been successfully completed in [launchStateHandling] */ class DefaultBehaviourContextWithFSM( private val behaviourContext: BehaviourContext, private val statesManager: StatesManager, - private val handlers: List> + private val handlers: List>, + private val onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler() ) : BehaviourContext by behaviourContext, BehaviourContextWithFSM { private val updatesFlows = mutableMapOf>() private val additionalHandlers = mutableListOf>() private var actualHandlersList = additionalHandlers + handlers + override suspend fun launchStateHandling(state: T, handlers: List>): T? { + return runCatchingSafely { + super.launchStateHandling(state, handlers) + }.getOrElse { + onStateHandlingErrorHandler(state, it) + } + } + private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) { createSubContext() } @@ -174,6 +203,23 @@ class DefaultBehaviourContextWithFSM( ): DefaultBehaviourContextWithFSM = BehaviourContextWithFSM( behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter), handlers, - statesManager + statesManager, + onStateHandlingErrorHandler + ) + + override fun copy( + bot: TelegramBot, + scope: CoroutineScope, + broadcastChannelsSize: Int, + onBufferOverflow: BufferOverflow, + upstreamUpdatesFlow: Flow?, + triggersHolder: TriggersHolder, + onStateHandlingErrorHandler: StateHandlingErrorHandler, + updatesFilter: BehaviourContextAndTypeReceiver? + ): DefaultBehaviourContextWithFSM = BehaviourContextWithFSM( + behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter), + handlers, + statesManager, + onStateHandlingErrorHandler ) } diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt index 9bcc15e42a..eef8542281 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/BehaviourContextWithFSMBuilder.kt @@ -6,13 +6,10 @@ import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling -import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter -import dev.inmo.tgbotapi.utils.PreviewFeature import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow -import kotlin.reflect.KClass @Deprecated("Will be removed soon") typealias BehaviourContextWithFSMBuilder = BehaviourContextWithFSM @@ -33,6 +30,7 @@ suspend fun TelegramBot.buildBehaviourWithFSM( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ): DefaultBehaviourContextWithFSM = BehaviourContextWithFSM( DefaultBehaviourContext( @@ -41,7 +39,8 @@ suspend fun TelegramBot.buildBehaviourWithFSM( upstreamUpdatesFlow = upstreamUpdatesFlow ), presetHandlers, - statesManager + statesManager, + onStateHandlingErrorHandler ).apply { block() } /** @@ -55,6 +54,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ): Pair, Job> = buildBehaviourWithFSM( upstreamUpdatesFlow, @@ -62,6 +62,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler, statesManager, presetHandlers, + onStateHandlingErrorHandler, block ).run { this to scope.launch { @@ -94,6 +95,7 @@ suspend fun TelegramBot.buildBehaviourWithFSM( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ): DefaultBehaviourContextWithFSM = BehaviourContextWithFSM( DefaultBehaviourContext( @@ -102,7 +104,8 @@ suspend fun TelegramBot.buildBehaviourWithFSM( upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow ), presetHandlers, - statesManager + statesManager, + onStateHandlingErrorHandler ).apply { block() } /** @@ -121,6 +124,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler: ExceptionHandler? = null, statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ) = FlowsUpdatesFilter().let { buildBehaviourWithFSM( @@ -129,6 +133,7 @@ suspend fun TelegramBot.buildBehaviourWithFSMAndStartLongPolling( defaultExceptionsHandler, statesManager, presetHandlers, + onStateHandlingErrorHandler, block ).run { start() diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/StateHandlingErrorHandler.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/StateHandlingErrorHandler.kt new file mode 100644 index 0000000000..26e9a06fb4 --- /dev/null +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/StateHandlingErrorHandler.kt @@ -0,0 +1,5 @@ +package dev.inmo.tgbotapi.extensions.behaviour_builder + +typealias StateHandlingErrorHandler = suspend (T, Throwable) -> T? +val DefaultStateHandlingErrorHandler: StateHandlingErrorHandler<*> = { _, _ -> null } +inline fun defaultStateHandlingErrorHandler(): StateHandlingErrorHandler = DefaultStateHandlingErrorHandler as StateHandlingErrorHandler diff --git a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt index bcbe3d7116..9588f1e648 100644 --- a/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt +++ b/tgbotapi.behaviour_builder.fsm/src/commonMain/kotlin/dev/inmo/tgbotapi/extensions/behaviour_builder/TelegramBotWithFSM.kt @@ -39,6 +39,7 @@ suspend fun telegramBotWithBehaviourAndFSM( statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), testServer: Boolean = false, + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ): TelegramBot = telegramBot( token, @@ -52,6 +53,7 @@ suspend fun telegramBotWithBehaviourAndFSM( defaultExceptionsHandler, statesManager, presetHandlers, + onStateHandlingErrorHandler, block ) } @@ -76,6 +78,7 @@ suspend fun telegramBotWithBehaviourAndFSMAndStartLongPolling( statesManager: StatesManager = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), presetHandlers: List> = listOf(), testServer: Boolean = false, + onStateHandlingErrorHandler: StateHandlingErrorHandler = defaultStateHandlingErrorHandler(), block: CustomBehaviourContextReceiver, Unit> ): Pair { return telegramBot( @@ -89,6 +92,7 @@ suspend fun telegramBotWithBehaviourAndFSMAndStartLongPolling( defaultExceptionsHandler, statesManager, presetHandlers, + onStateHandlingErrorHandler, block ) } diff --git a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt index 83f390cf94..16df51532b 100644 --- a/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt +++ b/tgbotapi.core/src/commonMain/kotlin/dev/inmo/tgbotapi/utils/TelegramAPIUrlsKeeper.kt @@ -50,7 +50,7 @@ class TelegramAPIUrlsKeeper( * @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData] * @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash] */ - fun checkWebAppLink(rawData: String, hash: String): Boolean { + fun checkWebAppData(rawData: String, hash: String): Boolean { val preparedData = rawData .decodeURLQueryComponent() .split("&") @@ -60,4 +60,11 @@ class TelegramAPIUrlsKeeper( return HMAC.hmacSHA256(webAppDataSecretKeyHash.bytes, preparedData.toByteArray()).hexLower == hash.lowercase() } + + /** + * @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData] + * @param hash Data from [dev.inmo.tgbotapi.webapps.WebApp.initDataUnsafe] from the field [dev.inmo.tgbotapi.webapps.WebAppInitData.hash] + */ + @Deprecated("Renamed", ReplaceWith("checkWebAppData")) + inline fun checkWebAppLink(rawData: String, hash: String) = checkWebAppData(rawData, hash) } diff --git a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt index dd1d788940..356282cf93 100644 --- a/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt +++ b/tgbotapi.webapps/src/jsMain/kotlin/dev/inmo/tgbotapi/webapps/WebApp.kt @@ -1,6 +1,5 @@ package dev.inmo.tgbotapi.webapps -import dev.inmo.micro_utils.crypto.CryptoJS import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper external class WebApp { @@ -77,7 +76,7 @@ fun WebApp.onMainButtonClicked(eventHandler: EventHandler) = onEvent(EventType.M */ fun WebApp.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler) -fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink( +fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppData( initData, initDataUnsafe.hash )