1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-19 13:55:57 +00:00

Compare commits

..

20 Commits
1.0.0 ... 1.1.2

Author SHA1 Message Date
8a3538d9c7 fixes in relate to main behaviourBuilder fun 2022-05-18 17:05:47 +06:00
e3db4250bb add default copy realization in BehaviourContextWithFSM 2022-05-18 16:52:52 +06:00
4643ac906a StateHandlingErrorHandler 2022-05-18 16:45:08 +06:00
497974e45c fix of #591 2022-05-18 16:31:14 +06:00
78a00f0efb start 1.1.2 2022-05-18 16:27:30 +06:00
14ecb9d948 Merge pull request #590 from InsanusMokrassar/1.1.1
1.1.1
2022-05-17 09:56:47 -04:00
c759b9a466 deprecate hmac and hex extensions for CryptoJS in webapp 2022-05-17 19:54:13 +06:00
58c1f2ee6a fixes in TelegramAPIUrlsKeeper#checkWebAppLink 2022-05-17 19:51:14 +06:00
9d16ca3b7e small refactor of buildBehaviourWithLongPolling 2022-05-16 18:59:30 +06:00
9d40e598f1 buildBehaciour returns BehaviourContext 2022-05-16 18:58:19 +06:00
f1be8bf16e start 1.1.1 2022-05-16 18:53:15 +06:00
56a8804e99 Merge pull request #589 from InsanusMokrassar/1.1.0
1.1.0
2022-05-14 12:21:08 +06:00
1e73aac750 Update Webhook.kt 2022-05-14 02:32:39 +06:00
82c6eda0b7 Update Webhook.kt 2022-05-14 02:31:36 +06:00
e9ff93cde1 improvements in webhooks 2022-05-14 01:35:38 +06:00
9b179ea1c9 start 1.1.0 2022-05-14 00:06:32 +06:00
72d20d2344 Merge pull request #586 from InsanusMokrassar/1.0.1
1.0.1
2022-05-13 00:52:50 +06:00
bcd288fe05 update dependencies 2022-05-12 17:51:42 +06:00
75477060e9 start 1.0.1 2022-05-12 17:50:04 +06:00
60df609486 Merge pull request #564 from InsanusMokrassar/1.0.0
1.0.0
2022-05-12 00:05:05 +06:00
12 changed files with 177 additions and 62 deletions

View File

@@ -1,5 +1,34 @@
# TelegramBotAPI changelog # 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`:
* `MicroUtils.Crypto` will not be provided with that library anymore. Instead, it is recommended to use `Korlibs.Krypto`. You still can add crypto from microutils using next groovy dependency: `dev.inmo:micro_utils.crypto:$micro_utils_version`
* `Core`:
* Improvements in `TelegramAPIUrlsKeeper#checkWebAppLink`
* New field in `TelegramAPIUrlsKeeper#webAppDataSecretKeyHash`
* `Behaviour Builder`:
* Extension `TelegramBot#buildBehaviour` now returns `BehaviourContext`
## 1.1.0
* `Utils`:
* New parameter `additionalApplicationEngineEnvironmentConfigurator` in `RequestsExecutor#setWebhookInfoAndStartListenWebhooks` and `startListenWebhooks`
## 1.0.1
* `Versions`:
* `Serialization`: `1.3.2` -> `1.3.3`
* `MicroUtils`: `0.10.3` -> `0.10.4`
## 1.0.0 ## 1.0.0
__All the `tgbotapi.extensions.*` packages have been removed__ __All the `tgbotapi.extensions.*` packages have been removed__
@@ -7,7 +36,7 @@ __All the `tgbotapi.extensions.*` packages have been removed__
* `Versions`: * `Versions`:
* `Kotlin`: `1.6.10` -> `1.6.21` * `Kotlin`: `1.6.10` -> `1.6.21`
* `Ktor`: `1.6.8` -> `2.0.1` * `Ktor`: `1.6.8` -> `2.0.1`
* `MicroUtils`: `0.9.24` -> `0.10.2` * `MicroUtils`: `0.9.24` -> `0.10.3`
* `Core`: * `Core`:
* **`Ktor` package renamed. Migration:** `dev.inmo.tgbotapi.bot.Ktor` -> `dev.inmo.tgbotapi.bot.ktor` * **`Ktor` package renamed. Migration:** `dev.inmo.tgbotapi.bot.Ktor` -> `dev.inmo.tgbotapi.bot.ktor`
* **`CallbackQuery` package renamed. Migration:** `dev.inmo.tgbotapi.types.CallbackQuery([\s\\.])` -> `dev.inmo.tgbotapi.types.queries.callback$1` * **`CallbackQuery` package renamed. Migration:** `dev.inmo.tgbotapi.types.CallbackQuery([\s\\.])` -> `dev.inmo.tgbotapi.types.queries.callback$1`

View File

@@ -7,12 +7,12 @@ kotlin.incremental.js=true
kotlin_version=1.6.21 kotlin_version=1.6.21
kotlin_coroutines_version=1.6.1 kotlin_coroutines_version=1.6.1
kotlin_serialisation_runtime_version=1.3.2 kotlin_serialisation_runtime_version=1.3.3
klock_version=2.7.0 korlibs_version=2.7.0
uuid_version=0.4.0 uuid_version=0.4.0
ktor_version=2.0.1 ktor_version=2.0.1
micro_utils_version=0.10.3 micro_utils_version=0.10.4
javax_activation_version=1.1.1 javax_activation_version=1.1.1
@@ -20,6 +20,6 @@ javax_activation_version=1.1.1
dokka_version=1.6.21 dokka_version=1.6.21
library_group=dev.inmo library_group=dev.inmo
library_version=1.0.0 library_version=1.1.2
github_release_plugin_version=2.3.7 github_release_plugin_version=2.3.7

View File

@@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.fsm.common.* import dev.inmo.micro_utils.fsm.common.*
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.types.update.abstracts.Update
@@ -50,12 +49,32 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): BehaviourContextWithFSM<T> ): BehaviourContextWithFSM<T>
fun copy(
bot: TelegramBot = this.bot,
scope: CoroutineScope = this.scope,
broadcastChannelsSize: Int = 100,
onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
upstreamUpdatesFlow: Flow<Update>? = null,
triggersHolder: TriggersHolder = this.triggersHolder,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>? = null
): BehaviourContextWithFSM<T> = copy(
bot,
scope,
broadcastChannelsSize,
onBufferOverflow,
upstreamUpdatesFlow,
triggersHolder,
updatesFilter
)
companion object { companion object {
operator fun <T : State> invoke( operator fun <T : State> invoke(
behaviourContext: BehaviourContext, behaviourContext: BehaviourContext,
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>, handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
statesManager: StatesManager<T> statesManager: StatesManager<T>,
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers) onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler)
} }
} }
@@ -84,16 +103,26 @@ inline fun <reified I : O, O: State> BehaviourContextWithFSM<O>.strictlyOn(handl
/** /**
* Default realization of [BehaviourContextWithFSM]. It uses [behaviourContext] as a base for this object as * 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 * [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<T : State>( class DefaultBehaviourContextWithFSM<T : State>(
private val behaviourContext: BehaviourContext, private val behaviourContext: BehaviourContext,
private val statesManager: StatesManager<T>, private val statesManager: StatesManager<T>,
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
private val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> { ) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>() private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>()
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>() private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
private var actualHandlersList = additionalHandlers + handlers private var actualHandlersList = additionalHandlers + handlers
override suspend fun launchStateHandling(state: T, handlers: List<CheckableHandlerHolder<in T, T>>): T? {
return runCatchingSafely {
super.launchStateHandling(state, handlers)
}.getOrElse {
onStateHandlingErrorHandler(state, it)
}
}
private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) { private fun getSubContext(context: Any) = updatesFlows.getOrPut(context) {
createSubContext() createSubContext()
} }
@@ -174,6 +203,23 @@ class DefaultBehaviourContextWithFSM<T : State>(
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter), behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
handlers, handlers,
statesManager statesManager,
onStateHandlingErrorHandler
)
override fun copy(
bot: TelegramBot,
scope: CoroutineScope,
broadcastChannelsSize: Int,
onBufferOverflow: BufferOverflow,
upstreamUpdatesFlow: Flow<Update>?,
triggersHolder: TriggersHolder,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T>,
updatesFilter: BehaviourContextAndTypeReceiver<Boolean, Update>?
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder, updatesFilter),
handlers,
statesManager,
onStateHandlingErrorHandler
) )
} }

View File

@@ -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.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling 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.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlin.reflect.KClass
@Deprecated("Will be removed soon") @Deprecated("Will be removed soon")
typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T> typealias BehaviourContextWithFSMBuilder<T> = BehaviourContextWithFSM<T>
@@ -33,6 +30,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext( DefaultBehaviourContext(
@@ -41,7 +39,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow = upstreamUpdatesFlow upstreamUpdatesFlow = upstreamUpdatesFlow
), ),
presetHandlers, presetHandlers,
statesManager statesManager,
onStateHandlingErrorHandler
).apply { block() } ).apply { block() }
/** /**
@@ -55,6 +54,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM( ): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow, upstreamUpdatesFlow,
@@ -62,6 +62,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,
presetHandlers, presetHandlers,
onStateHandlingErrorHandler,
block block
).run { ).run {
this to scope.launch { this to scope.launch {
@@ -94,6 +95,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM( ): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
DefaultBehaviourContext( DefaultBehaviourContext(
@@ -102,7 +104,8 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow upstreamUpdatesFlow = flowUpdatesFilter.allUpdatesFlow
), ),
presetHandlers, presetHandlers,
statesManager statesManager,
onStateHandlingErrorHandler
).apply { block() } ).apply { block() }
/** /**
@@ -121,6 +124,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let { ) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM( buildBehaviourWithFSM(
@@ -129,6 +133,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,
presetHandlers, presetHandlers,
onStateHandlingErrorHandler,
block block
).run { ).run {
start() start()

View File

@@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
typealias StateHandlingErrorHandler<T> = suspend (T, Throwable) -> T?
val DefaultStateHandlingErrorHandler: StateHandlingErrorHandler<*> = { _, _ -> null }
inline fun <T> defaultStateHandlingErrorHandler(): StateHandlingErrorHandler<T> = DefaultStateHandlingErrorHandler as StateHandlingErrorHandler<T>

View File

@@ -39,6 +39,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false, testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot( ): TelegramBot = telegramBot(
token, token,
@@ -52,6 +53,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,
presetHandlers, presetHandlers,
onStateHandlingErrorHandler,
block block
) )
} }
@@ -76,6 +78,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()), statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(), presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false, testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> { ): Pair<TelegramBot, Job> {
return telegramBot( return telegramBot(
@@ -89,6 +92,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
defaultExceptionsHandler, defaultExceptionsHandler,
statesManager, statesManager,
presetHandlers, presetHandlers,
onStateHandlingErrorHandler,
block block
) )
} }

View File

@@ -7,8 +7,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.PreviewFeature import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.plus
/** /**
* This function is used in [buildBehaviour] extensions to provide default [CoroutineScope] and allow to avoid all * This function is used in [buildBehaviour] extensions to provide default [CoroutineScope] and allow to avoid all
@@ -30,18 +29,18 @@ suspend fun TelegramBot.buildBehaviour(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
block: BehaviourContextReceiver<Unit> block: BehaviourContextReceiver<Unit>
) { ): BehaviourContext = BehaviourContext(
BehaviourContext( this,
this, scope.let {
scope.let { if (defaultExceptionsHandler == null) {
if (defaultExceptionsHandler == null) { it
it } else {
} else { it + ContextSafelyExceptionHandler(defaultExceptionsHandler)
it + ContextSafelyExceptionHandler(defaultExceptionsHandler) }
} },
}, flowUpdatesFilter
flowUpdatesFilter ).apply {
).block() block()
} }
/** /**
@@ -56,15 +55,14 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(), scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null, defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
block: BehaviourContextReceiver<Unit> block: BehaviourContextReceiver<Unit>
) = FlowsUpdatesFilter().let { ): Job {
buildBehaviour( val behaviourContext = buildBehaviour(
it, scope = scope,
scope, defaultExceptionsHandler = defaultExceptionsHandler,
defaultExceptionsHandler, block = block
block
) )
longPolling( return longPolling(
it, behaviourContext,
scope = scope scope = behaviourContext
) )
} }

View File

@@ -47,10 +47,10 @@ kotlin {
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version" api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version"
api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version" api "org.jetbrains.kotlinx:kotlinx-serialization-properties:$kotlin_serialisation_runtime_version"
api "com.soywiz.korlibs.klock:klock:$klock_version" api "com.soywiz.korlibs.klock:klock:$korlibs_version"
api "com.soywiz.korlibs.krypto:krypto:$korlibs_version"
api "com.benasher44:uuid:$uuid_version" api "com.benasher44:uuid:$uuid_version"
api "dev.inmo:micro_utils.crypto:$micro_utils_version"
api "dev.inmo:micro_utils.coroutines:$micro_utils_version" api "dev.inmo:micro_utils.coroutines:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version" api "dev.inmo:micro_utils.serialization.base64:$micro_utils_version"
api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version" api "dev.inmo:micro_utils.serialization.encapsulator:$micro_utils_version"

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.utils package dev.inmo.tgbotapi.utils
import dev.inmo.micro_utils.crypto.hex import com.soywiz.krypto.*
import dev.inmo.micro_utils.crypto.hmacSha256 import io.ktor.http.decodeURLQueryComponent
import io.ktor.utils.io.core.toByteArray
const val telegramBotAPIDefaultUrl = "https://api.telegram.org" const val telegramBotAPIDefaultUrl = "https://api.telegram.org"
@@ -22,9 +23,11 @@ class TelegramAPIUrlsKeeper(
hostUrl: String = telegramBotAPIDefaultUrl, hostUrl: String = telegramBotAPIDefaultUrl,
urlsSuffixes: String = "" urlsSuffixes: String = ""
) { ) {
val webAppDataSecretKey by lazy { val webAppDataSecretKeyHash by lazy {
token.hmacSha256("WebAppData") HMAC.hmacSHA256("WebAppData".toByteArray(), token.toByteArray())
} }
val webAppDataSecretKey
get() = webAppDataSecretKeyHash.hexLower
val commonAPIUrl: String val commonAPIUrl: String
val fileBaseUrl: String val fileBaseUrl: String
@@ -47,5 +50,21 @@ class TelegramAPIUrlsKeeper(
* @param rawData Data from [dev.inmo.tgbotapi.webapps.WebApp.initData] * @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] * @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) = rawData.hmacSha256(webAppDataSecretKey).hex() == hash fun checkWebAppData(rawData: String, hash: String): Boolean {
val preparedData = rawData
.decodeURLQueryComponent()
.split("&")
.filterNot { it.startsWith("hash=") }
.sorted()
.joinToString("\n")
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)
} }

View File

@@ -1,7 +1,6 @@
package dev.inmo.tgbotapi.extensions.utils.updates.retrieving package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
import dev.inmo.micro_utils.coroutines.ExceptionHandler import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.RequestsExecutor import dev.inmo.tgbotapi.bot.RequestsExecutor
import dev.inmo.tgbotapi.extensions.utils.nonstrictJsonFormat import dev.inmo.tgbotapi.extensions.utils.nonstrictJsonFormat
import dev.inmo.tgbotapi.extensions.utils.updates.flowsUpdatesFilter import dev.inmo.tgbotapi.extensions.utils.updates.flowsUpdatesFilter
@@ -10,6 +9,7 @@ import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy import dev.inmo.tgbotapi.types.update.abstracts.UpdateDeserializationStrategy
import dev.inmo.tgbotapi.updateshandlers.* import dev.inmo.tgbotapi.updateshandlers.*
import dev.inmo.tgbotapi.updateshandlers.webhook.WebhookPrivateKeyConfig import dev.inmo.tgbotapi.updateshandlers.webhook.WebhookPrivateKeyConfig
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.engine.* import io.ktor.server.engine.*
import io.ktor.server.request.receiveText import io.ktor.server.request.receiveText
@@ -39,18 +39,22 @@ fun Route.includeWebhookHandlingInRoute(
) { ) {
val transformer = scope.updateHandlerWithMediaGroupsAdaptation(block, mediaGroupsDebounceTimeMillis) val transformer = scope.updateHandlerWithMediaGroupsAdaptation(block, mediaGroupsDebounceTimeMillis)
post { post {
safely( try {
exceptionsHandler ?: {} runCatchingSafely {
) { val asJson = nonstrictJsonFormat.parseToJsonElement(call.receiveText())
val asJson = val update = nonstrictJsonFormat.decodeFromJsonElement(
nonstrictJsonFormat.parseToJsonElement(call.receiveText()) UpdateDeserializationStrategy,
val update = nonstrictJsonFormat.decodeFromJsonElement( asJson
UpdateDeserializationStrategy, )
asJson transformer(update)
) }.onSuccess {
transformer(update) call.respond(HttpStatusCode.OK)
}.onFailure {
call.respond(HttpStatusCode.InternalServerError)
}.getOrThrow()
} catch (e: Throwable) {
exceptionsHandler ?.invoke(e)
} }
call.respond("Ok")
} }
} }
@@ -87,6 +91,7 @@ fun startListenWebhooks(
privateKeyConfig: WebhookPrivateKeyConfig? = null, privateKeyConfig: WebhookPrivateKeyConfig? = null,
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()), scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
mediaGroupsDebounceTimeMillis: Long = 1000L, mediaGroupsDebounceTimeMillis: Long = 1000L,
additionalApplicationEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
block: UpdateReceiver<Update> block: UpdateReceiver<Update>
): ApplicationEngine { ): ApplicationEngine {
val env = applicationEngineEnvironment { val env = applicationEngineEnvironment {
@@ -98,6 +103,7 @@ fun startListenWebhooks(
} ?: includeWebhookHandlingInRoute(scope, exceptionsHandler, mediaGroupsDebounceTimeMillis, block) } ?: includeWebhookHandlingInRoute(scope, exceptionsHandler, mediaGroupsDebounceTimeMillis, block)
} }
} }
privateKeyConfig ?.let { privateKeyConfig ?.let {
sslConnector( sslConnector(
privateKeyConfig.keyStore, privateKeyConfig.keyStore,
@@ -113,6 +119,7 @@ fun startListenWebhooks(
port = listenPort port = listenPort
} }
additionalApplicationEngineEnvironmentConfigurator()
} }
return embeddedServer(engineFactory, env).also { return embeddedServer(engineFactory, env).also {
@@ -142,10 +149,11 @@ suspend fun RequestsExecutor.setWebhookInfoAndStartListenWebhooks(
privateKeyConfig: WebhookPrivateKeyConfig? = null, privateKeyConfig: WebhookPrivateKeyConfig? = null,
scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()), scope: CoroutineScope = CoroutineScope(Executors.newFixedThreadPool(4).asCoroutineDispatcher()),
mediaGroupsDebounceTimeMillis: Long = 1000L, mediaGroupsDebounceTimeMillis: Long = 1000L,
additionalApplicationEngineEnvironmentConfigurator: ApplicationEngineEnvironmentBuilder.() -> Unit = {},
block: UpdateReceiver<Update> block: UpdateReceiver<Update>
): ApplicationEngine = try { ): ApplicationEngine = try {
execute(setWebhookRequest) execute(setWebhookRequest)
startListenWebhooks(listenPort, engineFactory, exceptionsHandler, listenHost, listenRoute, privateKeyConfig, scope, mediaGroupsDebounceTimeMillis, block) startListenWebhooks(listenPort, engineFactory, exceptionsHandler, listenHost, listenRoute, privateKeyConfig, scope, mediaGroupsDebounceTimeMillis, additionalApplicationEngineEnvironmentConfigurator, block)
} catch (e: Exception) { } catch (e: Exception) {
throw e throw e
} }

View File

@@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.crypto.CryptoJs import dev.inmo.micro_utils.crypto.CryptoJs
@Deprecated("Useless")
fun CryptoJs.HmacSHA256(text: String, key: String) = this.asDynamic().HmacSHA256(text, key).unsafeCast<String>() fun CryptoJs.HmacSHA256(text: String, key: String) = this.asDynamic().HmacSHA256(text, key).unsafeCast<String>()
@Deprecated("Useless")
fun CryptoJs.hex(text: String) = this.asDynamic().format.Hex(text).unsafeCast<String>() fun CryptoJs.hex(text: String) = this.asDynamic().format.Hex(text).unsafeCast<String>()

View File

@@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.webapps package dev.inmo.tgbotapi.webapps
import dev.inmo.micro_utils.crypto.CryptoJS
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
external class WebApp { 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.onViewportChanged(eventHandler: ViewportChangedEventHandler) = onEvent(EventType.ViewportChanged, eventHandler)
fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppLink( fun WebApp.isInitDataSafe(botToken: String) = TelegramAPIUrlsKeeper(botToken).checkWebAppData(
initData, initData,
initDataUnsafe.hash initDataUnsafe.hash
) )