diff --git a/FSMBot/README.md b/FSMBot/README.md
new file mode 100644
index 0000000..9ee12ce
--- /dev/null
+++ b/FSMBot/README.md
@@ -0,0 +1,10 @@
+# FSM
+
+This bot contains an example of working with FSM included in project
+[MicroUtils](https://github.com/InsanusMokrassar/MicroUtils)
+
+## Launch
+
+```bash
+../gradlew run --args="BOT_TOKEN"
+```
diff --git a/FSMBot/build.gradle b/FSMBot/build.gradle
new file mode 100644
index 0000000..090f499
--- /dev/null
+++ b/FSMBot/build.gradle
@@ -0,0 +1,22 @@
+buildscript {
+    repositories {
+        jcenter()
+    }
+
+    dependencies {
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+apply plugin: 'kotlin'
+apply plugin: 'application'
+
+mainClassName="SimpleFSMBotKt"
+
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
+
+    implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
+    implementation "dev.inmo:micro_utils.fsm.common:$micro_utils_version"
+}
diff --git a/FSMBot/src/main/kotlin/SimpleFSMBot.kt b/FSMBot/src/main/kotlin/SimpleFSMBot.kt
new file mode 100644
index 0000000..28912c9
--- /dev/null
+++ b/FSMBot/src/main/kotlin/SimpleFSMBot.kt
@@ -0,0 +1,90 @@
+import dev.inmo.micro_utils.fsm.common.State
+import dev.inmo.micro_utils.fsm.common.dsl.buildFSM
+import dev.inmo.micro_utils.fsm.common.dsl.strictlyOn
+import dev.inmo.tgbotapi.extensions.api.send.media.sendMediaGroup
+import dev.inmo.tgbotapi.extensions.api.send.reply
+import dev.inmo.tgbotapi.extensions.api.send.sendMessage
+import dev.inmo.tgbotapi.extensions.behaviour_builder.*
+import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
+import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.command
+import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
+import dev.inmo.tgbotapi.extensions.utils.formatting.*
+import dev.inmo.tgbotapi.extensions.utils.shortcuts.chat
+import dev.inmo.tgbotapi.types.ChatId
+import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
+import dev.inmo.tgbotapi.types.message.content.TextContent
+import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
+import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+
+sealed interface State : State
+data class ExpectContentOrStopState(override val context: ChatId, val sourceMessage: CommonMessage<TextContent>) : State
+data class StopState(override val context: ChatId) : State
+
+fun TextContent.containsStopCommand() = parseCommandsWithParams().keys.firstOrNull { it == "stop" } != null
+
+suspend fun main(args: Array<String>) {
+    val botToken = args.first()
+
+    telegramBotWithBehaviour(botToken, CoroutineScope(Dispatchers.IO)) {
+        val fsm = buildFSM {
+            strictlyOn<ExpectContentOrStopState> {
+                sendMessage(
+                    it.context,
+                    buildEntities {
+                        +"Send me some content or "
+                        botCommand("stop")
+                        +" if you want to stop sending"
+                    }
+                )
+
+                val content = oneOf(
+                    parallel {
+                        waitContentMessage(includeMediaGroups = false) { if (chat.id == it.context) content else null }.also(::println)
+                    },
+                    parallel {
+                        waitMediaGroup { chat ?.id == it.context }.also(::println)
+                    },
+                    parallel {
+                        waitText { if (content.containsStopCommand()) content else null }.also(::println)
+                    }
+                ).first()
+
+                when {
+                    content is TextContent && content.containsStopCommand() -> StopState(it.context) // assume we got "stop" command
+                    content is List<*> -> { // assume it is media group
+                        val casted = (content as List<MediaGroupContent>)
+
+                        reply(it.sourceMessage, "Ok, I got this media group and now will resend it to you")
+                        sendMediaGroup(it.context, casted.map { it.toMediaGroupMemberInputMedia() })
+
+                        it
+                    }
+                    content is MessageContent -> {
+
+                        reply(it.sourceMessage, "Ok, I got this content and now will resend it to you")
+                        execute(content.createResend(it.context))
+
+                        it
+                    }
+                    else -> {
+                        sendMessage(it.context, "Unknown internal error")
+                        it
+                    }
+                }
+            }
+            strictlyOn<StopState> {
+                sendMessage(it.context, "You have stopped sending of content")
+
+                null
+            }
+        }
+
+        command("start") {
+            fsm.startChain(ExpectContentOrStopState(it.chat.id, it))
+        }
+
+        fsm.start(this)
+    }.second.join()
+}
diff --git a/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt b/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt
index a3acbec..d6b6a1e 100644
--- a/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt
+++ b/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt
@@ -1,14 +1,12 @@
-import dev.inmo.micro_utils.coroutines.safely
-import dev.inmo.tgbotapi.bot.Ktor.telegramBot
 import dev.inmo.tgbotapi.extensions.api.downloadFile
 import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
-import dev.inmo.tgbotapi.extensions.utils.flatMap
-import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
-import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
-import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
+import dev.inmo.tgbotapi.extensions.api.send.reply
+import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviour
+import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
+import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia
 import dev.inmo.tgbotapi.utils.filenameFromUrl
-import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
 import java.io.File
 
 /**
@@ -19,24 +17,15 @@ suspend fun main(args: Array<String>) {
     val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("")
     directoryOrFile.mkdirs()
 
-    val bot = telegramBot(botToken)
-    val scope = CoroutineScope(Dispatchers.Default)
-
-    bot.longPolling(scope = scope) {
-        val flow = merge (
-            filterContentMessages<MediaContent>(),
-            mediaGroupMessages().flatMap()
-        )
-        flow.onEach {
-            safely({ it.printStackTrace() }) {
-                val pathedFile = bot.getFileAdditionalInfo(it.content.media)
-                File(directoryOrFile, pathedFile.filePath.filenameFromUrl).apply {
-                    createNewFile()
-                    writeBytes(bot.downloadFile(pathedFile))
-                }
+    telegramBotWithBehaviour(botToken, CoroutineScope(Dispatchers.IO)) {
+        onMedia(includeMediaGroups = true) {
+            val pathedFile = bot.getFileAdditionalInfo(it.content.media)
+            val file = File(directoryOrFile, pathedFile.filePath.filenameFromUrl).apply {
+                createNewFile()
+                writeBytes(bot.downloadFile(pathedFile))
             }
-        }.launchIn(scope)
-    }
-
-    scope.coroutineContext[Job]!!.join()
+            reply(it, "Saved to ${file.absolutePath}")
+        }
+        onContentMessage { println(it) }
+    }.second.join()
 }
diff --git a/ForwardInfoSenderBot/src/main/kotlin/ForwardInfoSenderBot.kt b/ForwardInfoSenderBot/src/main/kotlin/ForwardInfoSenderBot.kt
index 04230e4..b3b93ae 100644
--- a/ForwardInfoSenderBot/src/main/kotlin/ForwardInfoSenderBot.kt
+++ b/ForwardInfoSenderBot/src/main/kotlin/ForwardInfoSenderBot.kt
@@ -1,17 +1,12 @@
-import dev.inmo.micro_utils.coroutines.safely
+import dev.inmo.micro_utils.coroutines.defaultSafelyExceptionHandler
 import dev.inmo.tgbotapi.bot.Ktor.telegramBot
-import dev.inmo.tgbotapi.extensions.api.send.sendTextMessage
-import dev.inmo.tgbotapi.extensions.api.telegramBot
-import dev.inmo.tgbotapi.extensions.utils.*
-import dev.inmo.tgbotapi.extensions.utils.formatting.codeMarkdownV2
-import dev.inmo.tgbotapi.extensions.utils.formatting.regularMarkdownV2
-import dev.inmo.tgbotapi.extensions.utils.shortcuts.mediaGroupMessages
-import dev.inmo.tgbotapi.extensions.utils.updates.asContentMessagesFlow
-import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
+import dev.inmo.tgbotapi.extensions.api.send.reply
+import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviour
+import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
+import dev.inmo.tgbotapi.extensions.utils.formatting.*
 import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
 import dev.inmo.tgbotapi.types.message.*
 import kotlinx.coroutines.*
-import kotlinx.coroutines.flow.*
 
 /**
  * This bot will always return message about forwarder. In cases when sent message was not a forward message it will
@@ -20,26 +15,25 @@ import kotlinx.coroutines.flow.*
 suspend fun main(vararg args: String) {
     val botToken = args.first()
 
-    val bot = telegramBot(botToken)
-
-    val scope = CoroutineScope(Dispatchers.Default)
-
-    bot.longPolling(scope = scope) {
-        (merge(messageFlow.asContentMessagesFlow(), mediaGroupMessages(scope).flatMap())).mapNotNull { it.asPossiblyForwardedMessage() }.onEach { message ->
-            safely({ it.printStackTrace() }) {
-                val toAnswer = when (val forwardInfo = message.forwardInfo) {
-                    null -> "There is no forward info"
-                    is AnonymousForwardInfo -> "Anonymous user which signed as \"${forwardInfo.senderName.codeMarkdownV2()}\""
-                    is UserForwardInfo -> forwardInfo.from.let { user ->
-                        "User ${user.id.chatId.toString().codeMarkdownV2()} " + "(${user.firstName} ${user.lastName}: ${user.username ?.username ?: "Without username"})".regularMarkdownV2()
+    telegramBotWithBehaviour(botToken, CoroutineScope(Dispatchers.IO)) {
+        onContentMessage(includeMediaGroups = true) {
+            val toAnswer = buildEntities {
+                when (val forwardInfo = it.forwardInfo) {
+                    null -> +"There is no forward info"
+                    is AnonymousForwardInfo -> {
+                        regular("Anonymous user which signed as \"") + code(forwardInfo.senderName) + "\""
                     }
-                    is ForwardFromChannelInfo -> "Channel (".regularMarkdownV2() + (forwardInfo.channelChat).title.codeMarkdownV2() + ")".regularMarkdownV2()
-                    is ForwardFromSupergroupInfo -> "Supergroup (".regularMarkdownV2() + (forwardInfo.group).title.codeMarkdownV2() + ")".regularMarkdownV2()
+                    is UserForwardInfo -> {
+                        val user = forwardInfo.from
+                        regular("User ") + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username ?.username ?: "Without username"})"
+                    }
+                    is ForwardFromChannelInfo -> regular("Channel (") + code((forwardInfo.channelChat).title) + ")"
+                    is ForwardFromSupergroupInfo -> regular("Supergroup (") + code((forwardInfo.group).title) + ")"
                 }
-                bot.sendTextMessage(message.chat, toAnswer, MarkdownV2)
             }
-        }.launchIn(scope)
-    }
-
-    scope.coroutineContext[Job]!!.join()
+            reply(it, toAnswer)
+            coroutineContext.job.invokeOnCompletion { println("completance of onContentMessage") }
+        }
+        coroutineContext.job.invokeOnCompletion { println("Completed :)") }
+    }.second.join()
 }
diff --git a/HelloBot/src/main/kotlin/HelloBot.kt b/HelloBot/src/main/kotlin/HelloBot.kt
index 52eaebf..3f171a5 100644
--- a/HelloBot/src/main/kotlin/HelloBot.kt
+++ b/HelloBot/src/main/kotlin/HelloBot.kt
@@ -3,6 +3,8 @@ import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
 import dev.inmo.tgbotapi.extensions.api.send.reply
 import dev.inmo.tgbotapi.extensions.api.send.sendTextMessage
 import dev.inmo.tgbotapi.bot.Ktor.telegramBot
+import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviour
+import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
 import dev.inmo.tgbotapi.extensions.utils.asChannelChat
 import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
 import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
@@ -21,41 +23,30 @@ import kotlinx.coroutines.flow.onEach
 suspend fun main(vararg args: String) {
     val botToken = args.first()
 
-    val bot = telegramBot(botToken)
-
-    val scope = CoroutineScope(Dispatchers.Default)
-
-    bot.longPolling(scope = scope) {
-        messageFlow.onEach {
-            safely {
-                val message = it.data
-                val chat = message.chat
-                val answerText = "Oh, hi, " + when (chat) {
-                    is PrivateChat -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
-                    is User -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
-                    is SupergroupChat -> (chat.username ?.username ?: bot.getChat(chat).inviteLink) ?.let {
-                        chat.title.linkMarkdownV2(it)
-                    } ?: chat.title
-                    is GroupChat -> bot.getChat(chat).inviteLink ?.let {
-                        chat.title.linkMarkdownV2(it)
-                    } ?: chat.title
-                    else -> "Unknown :(".escapeMarkdownV2Common()
-                }
-                bot.reply(
-                    message,
-                    answerText,
-                    MarkdownV2
-                )
+    telegramBotWithBehaviour(botToken, CoroutineScope(Dispatchers.IO)) {
+        onContentMessage { message ->
+            val chat = message.chat
+            if (chat is ChannelChat) {
+                val answer = "Hi everybody in this channel \"${chat.title}\""
+                sendTextMessage(chat, answer, MarkdownV2)
+                return@onContentMessage
             }
-        }.launchIn(scope)
-        channelPostFlow.onEach {
-            safely {
-                val chat = it.data.chat
-                val message = "Hi everybody in this channel \"${(chat.asChannelChat()) ?.title}\""
-                bot.sendTextMessage(chat, message, MarkdownV2)
+            val answerText = "Oh, hi, " + when (chat) {
+                is PrivateChat -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
+                is User -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
+                is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let {
+                    chat.title.linkMarkdownV2(it)
+                } ?: chat.title
+                is GroupChat -> bot.getChat(chat).inviteLink ?.let {
+                    chat.title.linkMarkdownV2(it)
+                } ?: chat.title
+                else -> "Unknown :(".escapeMarkdownV2Common()
             }
-        }.launchIn(scope)
-    }
-
-    scope.coroutineContext[Job]!!.join()
-}
\ No newline at end of file
+            reply(
+                message,
+                answerText,
+                MarkdownV2
+            )
+        }
+    }.second.join()
+}
diff --git a/gradle.properties b/gradle.properties
index 7dc6ed8..ffda2a9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,7 @@
 kotlin.code.style=official
 org.gradle.parallel=true
 
-kotlin_version=1.5.10
-telegram_bot_api_version=0.35.0
+
+kotlin_version=1.5.20
+telegram_bot_api_version=0.35.1
+micro_utils_version=0.5.15
diff --git a/settings.gradle b/settings.gradle
index 48cdeb2..fb925d4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -6,3 +6,4 @@ include ":FilesLoaderBot"
 include ":ResenderBot:ResenderBotLib"
 include ":ResenderBot:jvm_launcher"
 include ":SlotMachineDetectorBot"
+include ":FSMBot"