From d062ab86aeb0712ceab8194945b24bd8d524d160 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Mon, 28 Jun 2021 01:14:16 +0600
Subject: [PATCH 1/4] update up to 0.35.1

---
 .../src/main/kotlin/FilesLoaderBot.kt         | 43 +++++--------
 .../src/main/kotlin/ForwardInfoSenderBot.kt   | 52 +++++++--------
 HelloBot/src/main/kotlin/HelloBot.kt          | 63 ++++++++-----------
 gradle.properties                             |  4 +-
 4 files changed, 68 insertions(+), 94 deletions(-)

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 47d92d0..890916b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
 kotlin.code.style=official
 org.gradle.parallel=true
 
-kotlin_version=1.4.32
-telegram_bot_api_version=0.34.1
+kotlin_version=1.5.20
+telegram_bot_api_version=0.35.1

From 522d1b55ba706b1c8e960a9d83eb336ebb9d44b8 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Wed, 30 Jun 2021 13:12:15 +0600
Subject: [PATCH 2/4] add example with oneof and fsm

---
 FSMBot/README.md                       | 10 +++
 FSMBot/build.gradle                    | 22 +++++++
 FSMBot/src/main/kotlin/SimpleFSMBot.kt | 90 ++++++++++++++++++++++++++
 gradle.properties                      |  3 +-
 settings.gradle                        |  1 +
 5 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 FSMBot/README.md
 create mode 100644 FSMBot/build.gradle
 create mode 100644 FSMBot/src/main/kotlin/SimpleFSMBot.kt

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..a25fd20
--- /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="HelloBotKt"
+
+
+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/gradle.properties b/gradle.properties
index 890916b..d4b82fa 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,4 +2,5 @@ kotlin.code.style=official
 org.gradle.parallel=true
 
 kotlin_version=1.5.20
-telegram_bot_api_version=0.35.1
+telegram_bot_api_version=0.35.1-branch_0.35.1-build280
+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"

From ef74b33d6835239c44f6710b14122dc9909f08b3 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Wed, 30 Jun 2021 13:50:08 +0600
Subject: [PATCH 3/4] fix main in FSMBot

---
 FSMBot/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/FSMBot/build.gradle b/FSMBot/build.gradle
index a25fd20..090f499 100644
--- a/FSMBot/build.gradle
+++ b/FSMBot/build.gradle
@@ -11,7 +11,7 @@ buildscript {
 apply plugin: 'kotlin'
 apply plugin: 'application'
 
-mainClassName="HelloBotKt"
+mainClassName="SimpleFSMBotKt"
 
 
 dependencies {

From cd9eba03939d5c34d4480db86e8ce2544c1c4fd1 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Wed, 30 Jun 2021 20:25:08 +0600
Subject: [PATCH 4/4] Update gradle.properties

---
 gradle.properties | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gradle.properties b/gradle.properties
index d4b82fa..69a2811 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,5 +2,5 @@ kotlin.code.style=official
 org.gradle.parallel=true
 
 kotlin_version=1.5.20
-telegram_bot_api_version=0.35.1-branch_0.35.1-build280
+telegram_bot_api_version=0.35.1
 micro_utils_version=0.5.15