From af86a64a48d07418c347f86ae61a42ea4994bca7 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 18:08:00 +0600 Subject: [PATCH 01/12] start 2.1.0 --- CHANGELOG.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63acdb2..e863dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +## 2.1.0 + ## 2.0.0 * `Versions`: diff --git a/gradle.properties b/gradle.properties index c22cb52..0644318 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ kotlin.js.generate.externals=true kotlin.incremental=true group=dev.inmo -version=2.0.0 +version=2.1.0 From 1ba3953dfa23c470a53de501ed0e4e592d2c5c28 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 18:09:19 +0600 Subject: [PATCH 02/12] update dependencies --- CHANGELOG.md | 6 +++++- gradle/libs.versions.toml | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e863dd1..bc409dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## 2.1.0 +* `Versions`: + * `tgbotapi`: `3.1.0` + * `ktor`: `2.1.0` + * `microutils`: `0.12.1` + ## 2.0.0 * `Versions`: @@ -11,7 +16,6 @@ * `kslog`: `0.5.0` * `uuid`: `0.5.0` * `exposed`: `0.39.2` - * `microutils`: `0.12.0` ## 1.4.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 044a050..44f17bb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,8 +4,8 @@ kt = "1.7.10" kt-serialization = "1.4.0-RC" kt-coroutines = "1.6.4" -microutils = "0.12.0" -tgbotapi = "3.0.2" +microutils = "0.12.1" +tgbotapi = "3.1.0" kslog = "0.5.0" jb-exposed = "0.39.2" @@ -16,7 +16,7 @@ sqlite = "3.36.0.3" klock = "3.0.0" uuid = "0.5.0" -ktor = "2.0.3" +ktor = "2.1.0" gh-release = "2.4.1" From fa00d11a395f4a6be756c0d9a543a1303ac3b0db Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 18:09:38 +0600 Subject: [PATCH 03/12] update gradle wrapper --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..ae04661 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 08b797bbc24d50e62cd66b7f46ce8057f20bbc47 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 18:30:33 +0600 Subject: [PATCH 04/12] start integration FSM oriented plagubot version --- fsm/bot/build.gradle | 38 ++++++ fsm/bot/publish.gradle | 81 +++++++++++++ fsm/bot/publish.kpsb | 1 + fsm/bot/src/main/kotlin/App.kt | 25 ++++ fsm/bot/src/main/kotlin/FSMPlaguBot.kt | 112 ++++++++++++++++++ fsm/bot/src/main/kotlin/HelloFSMPlugin.kt | 37 ++++++ fsm/plugin/build.gradle | 21 ++++ fsm/plugin/publish.gradle | 81 +++++++++++++ fsm/plugin/publish.kpsb | 1 + fsm/plugin/src/main/kotlin/FSMPlugin.kt | 24 ++++ .../src/main/kotlin/FSMPluginSerializer.kt | 25 ++++ gradle/libs.versions.toml | 1 + settings.gradle | 13 +- 13 files changed, 456 insertions(+), 4 deletions(-) create mode 100644 fsm/bot/build.gradle create mode 100644 fsm/bot/publish.gradle create mode 100644 fsm/bot/publish.kpsb create mode 100644 fsm/bot/src/main/kotlin/App.kt create mode 100644 fsm/bot/src/main/kotlin/FSMPlaguBot.kt create mode 100644 fsm/bot/src/main/kotlin/HelloFSMPlugin.kt create mode 100644 fsm/plugin/build.gradle create mode 100644 fsm/plugin/publish.gradle create mode 100644 fsm/plugin/publish.kpsb create mode 100644 fsm/plugin/src/main/kotlin/FSMPlugin.kt create mode 100644 fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt diff --git a/fsm/bot/build.gradle b/fsm/bot/build.gradle new file mode 100644 index 0000000..495dfa1 --- /dev/null +++ b/fsm/bot/build.gradle @@ -0,0 +1,38 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id "org.jetbrains.kotlin.plugin.serialization" + id 'application' +} + +project.group="$group" +project.version="$version" + +apply from: "publish.gradle" + +dependencies { + implementation libs.kt.stdlib + api libs.kt.coroutines + api libs.kt.serialization + api libs.jb.exposed.jdbc + + api libs.tgbotapi + api libs.microutils.repos.exposed + api libs.kslog + + api libs.sqlite + + testImplementation libs.kt.test.junit + + api project(":plagubot.bot") + api project(":plagubot.fsm.plugin") +} + +application { + mainClassName = 'dev.inmo.plagubot.AppKt' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + diff --git a/fsm/bot/publish.gradle b/fsm/bot/publish.gradle new file mode 100644 index 0000000..a65131e --- /dev/null +++ b/fsm/bot/publish.gradle @@ -0,0 +1,81 @@ +apply plugin: 'maven-publish' + +task javadocJar(type: Jar) { + from javadoc + classifier = 'javadoc' +} +task sourcesJar(type: Jar) { + from sourceSets.main.allSource + classifier = 'sources' +} + +publishing { + publications { + maven(MavenPublication) { + from components.java + + artifact javadocJar + artifact sourcesJar + + pom { + resolveStrategy = Closure.DELEGATE_FIRST + + description = "Base PlaguBot project" + name = "PlaguBot Bot" + url = "https://github.com/InsanusMokrassar/PlaguBot" + + scm { + developerConnection = "scm:git:[fetch=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git[push=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git" + url = "ssh://git@github.com/InsanusMokrassar/PlaguBot.git" + } + + developers { + + developer { + id = "InsanusMokrassar" + name = "Aleksei Ovsiannikov" + email = "ovsyannikov.alexey95@gmail.com" + } + + } + + licenses { + + license { + name = "Apache Software License 2.0" + url = "https://github.com/InsanusMokrassar/PlaguBot/LICENSE" + } + + } + } + repositories { + if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { + maven { + name = "sonatype" + url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") + credentials { + username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') + password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') + } + } + } + } + } + } +} + +if (project.hasProperty("signing.gnupg.keyName")) { + apply plugin: 'signing' + + signing { + useGpgCmd() + + sign publishing.publications + } + + task signAll { + tasks.withType(Sign).forEach { + dependsOn(it) + } + } +} diff --git a/fsm/bot/publish.kpsb b/fsm/bot/publish.kpsb new file mode 100644 index 0000000..b1382ea --- /dev/null +++ b/fsm/bot/publish.kpsb @@ -0,0 +1 @@ +{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/PlaguBot/LICENSE"}],"mavenConfig":{"name":"PlaguBot Bot","description":"Base PlaguBot project","url":"https://github.com/InsanusMokrassar/PlaguBot","vcsUrl":"ssh://git@github.com/InsanusMokrassar/PlaguBot.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} \ No newline at end of file diff --git a/fsm/bot/src/main/kotlin/App.kt b/fsm/bot/src/main/kotlin/App.kt new file mode 100644 index 0000000..e23073c --- /dev/null +++ b/fsm/bot/src/main/kotlin/App.kt @@ -0,0 +1,25 @@ +package dev.inmo.plagubot + +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.i +import dev.inmo.plagubot.config.Config +import dev.inmo.plagubot.config.defaultJsonFormat +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.json.jsonObject +import java.io.File + +/** + * This method by default expects one argument in [args] field: path to config + */ +@InternalSerializationApi +suspend fun main(args: Array) { + KSLog.default = KSLog("PlaguBot") + val (configPath) = args + val file = File(configPath) + KSLog.i("Start read config from ${file.absolutePath}") + val json = defaultJsonFormat.parseToJsonElement(file.readText()).jsonObject + val config = defaultJsonFormat.decodeFromJsonElement(Config.serializer(), json) + KSLog.i("Config has been read") + + FSMPlaguBot(json, config).start().join() +} diff --git a/fsm/bot/src/main/kotlin/FSMPlaguBot.kt b/fsm/bot/src/main/kotlin/FSMPlaguBot.kt new file mode 100644 index 0000000..b771485 --- /dev/null +++ b/fsm/bot/src/main/kotlin/FSMPlaguBot.kt @@ -0,0 +1,112 @@ +package dev.inmo.plagubot + +import dev.inmo.kslog.common.* +import dev.inmo.micro_utils.common.Warning +import dev.inmo.micro_utils.coroutines.runCatchingSafely +import dev.inmo.plagubot.config.Config +import dev.inmo.plagubot.config.defaultJsonFormat +import dev.inmo.plagubot.fsm.FSMPlugin +import dev.inmo.tgbotapi.bot.ktor.telegramBot +import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook +import dev.inmo.tgbotapi.extensions.behaviour_builder.* +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling +import kotlinx.coroutines.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import kotlinx.serialization.json.JsonObject +import org.jetbrains.exposed.sql.Database +import org.koin.core.Koin +import org.koin.core.KoinApplication +import org.koin.core.context.GlobalContext +import org.koin.core.module.Module +import org.koin.core.scope.Scope +import org.koin.dsl.module + +val Scope.fsmPlagubot: FSMPlaguBot + get() = get() + +val Koin.fsmPlagubot: FSMPlaguBot + get() = get() + +@OptIn(Warning::class) +@Serializable +data class FSMPlaguBot( + private val json: JsonObject, + private val config: Config +) : FSMPlugin { + @Transient + private val bot = telegramBot(config.botToken) + + override fun Module.setupDI(database: Database, params: JsonObject) { + single { config } + single { config.plugins } + single { config.databaseConfig } + single { config.databaseConfig.database } + single { defaultJsonFormat } + single { this@FSMPlaguBot } + + includes( + config.plugins.mapNotNull { + runCatching { + module { + with(it) { + setupDI(database, params) + } + } + }.onFailure { e -> + logger.w("Unable to load DI part of $it", e) + }.getOrNull() + } + ) + } + + override suspend fun BehaviourContextWithFSM<*>.setupBotPlugin(koin: Koin) { + config.plugins.map { plugin -> + launch { + runCatchingSafely { + logger.i("Start loading of $plugin") + with(plugin) { + if (this is FSMPlugin) { + setupBotPlugin(koin) // use setupBotPlugin with BehaviourContextWithFSM as receiver + } else { + setupBotPlugin(koin) // use setupBotPlugin with BehaviourContext as receiver + } + } + }.onFailure { e -> + logger.w("Unable to load bot part of $plugin", e) + }.onSuccess { + logger.i("Complete loading of $plugin") + } + } + }.joinAll() + } + + /** + * This method will create an [Job] which will be the main [Job] of ran instance + */ + suspend fun start( + scope: CoroutineScope = CoroutineScope(Dispatchers.IO) + ): Job { + logger.i("Start initialization") + val koinApp = KoinApplication.init() + koinApp.modules( + module { + setupDI(config.databaseConfig.database, json) + } + ) + logger.i("Modules loaded") + GlobalContext.startKoin(koinApp) + logger.i("Koin started") + lateinit var behaviourContext: BehaviourContext + bot.buildBehaviourWithFSM (scope = scope) { + logger.i("Start setup of bot part") + behaviourContext = this + setupBotPlugin(koinApp.koin) + deleteWebhook() + } + logger.i("Behaviour builder has been setup") + return bot.startGettingOfUpdatesByLongPolling(scope = behaviourContext, updatesFilter = behaviourContext).also { + logger.i("Long polling has been started") + } + } +} diff --git a/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt b/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt new file mode 100644 index 0000000..baa6b5b --- /dev/null +++ b/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt @@ -0,0 +1,37 @@ +package dev.inmo.plagubot + +import dev.inmo.kslog.common.* +import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import org.jetbrains.exposed.sql.Database +import org.koin.core.Koin +import org.koin.core.module.Module + +@Serializable +@SerialName("Hello") +object HelloFSMPlugin : Plugin { + @Serializable + data class HelloPluginConfig( + val print: String + ) + + override fun Module.setupDI(database: Database, params: JsonObject) { + single { + get().decodeFromJsonElement(HelloPluginConfig.serializer(), params["helloPlugin"] ?: return@single null) + } + } + + override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { + logger.d { koin.get().print } + logger.dS { getMe().toString() } + onCommand("hello_world") { + reply(it, "Hello :)") + } + } +} diff --git a/fsm/plugin/build.gradle b/fsm/plugin/build.gradle new file mode 100644 index 0000000..7d57c41 --- /dev/null +++ b/fsm/plugin/build.gradle @@ -0,0 +1,21 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id "org.jetbrains.kotlin.plugin.serialization" +} + +project.group="$group" +project.version="$version" + +apply from: "publish.gradle" + +dependencies { + implementation libs.kt.stdlib + + api libs.tgbotapi.behaviourBuilder.fsm + api project(":plagubot.plugin") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} diff --git a/fsm/plugin/publish.gradle b/fsm/plugin/publish.gradle new file mode 100644 index 0000000..ed05a9d --- /dev/null +++ b/fsm/plugin/publish.gradle @@ -0,0 +1,81 @@ +apply plugin: 'maven-publish' + +task javadocJar(type: Jar) { + from javadoc + classifier = 'javadoc' +} +task sourcesJar(type: Jar) { + from sourceSets.main.allSource + classifier = 'sources' +} + +publishing { + publications { + maven(MavenPublication) { + from components.java + + artifact javadocJar + artifact sourcesJar + + pom { + resolveStrategy = Closure.DELEGATE_FIRST + + description = "Base dependency for whole PlaguBot project" + name = "PlaguBot Plugin" + url = "https://github.com/InsanusMokrassar/PlaguBot" + + scm { + developerConnection = "scm:git:[fetch=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git[push=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git" + url = "ssh://git@github.com/InsanusMokrassar/PlaguBot.git" + } + + developers { + + developer { + id = "InsanusMokrassar" + name = "Aleksei Ovsiannikov" + email = "ovsyannikov.alexey95@gmail.com" + } + + } + + licenses { + + license { + name = "Apache Software License 2.0" + url = "https://github.com/InsanusMokrassar/PlaguBot/LICENSE" + } + + } + } + repositories { + if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { + maven { + name = "sonatype" + url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") + credentials { + username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') + password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') + } + } + } + } + } + } +} + +if (project.hasProperty("signing.gnupg.keyName")) { + apply plugin: 'signing' + + signing { + useGpgCmd() + + sign publishing.publications + } + + task signAll { + tasks.withType(Sign).forEach { + dependsOn(it) + } + } +} diff --git a/fsm/plugin/publish.kpsb b/fsm/plugin/publish.kpsb new file mode 100644 index 0000000..40f786b --- /dev/null +++ b/fsm/plugin/publish.kpsb @@ -0,0 +1 @@ +{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/PlaguBot/LICENSE"}],"mavenConfig":{"name":"PlaguBot Plugin","description":"Base dependency for whole PlaguBot project","url":"https://github.com/InsanusMokrassar/PlaguBot","vcsUrl":"ssh://git@github.com/InsanusMokrassar/PlaguBot.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} \ No newline at end of file diff --git a/fsm/plugin/src/main/kotlin/FSMPlugin.kt b/fsm/plugin/src/main/kotlin/FSMPlugin.kt new file mode 100644 index 0000000..d089c15 --- /dev/null +++ b/fsm/plugin/src/main/kotlin/FSMPlugin.kt @@ -0,0 +1,24 @@ +package dev.inmo.plagubot.fsm + +import dev.inmo.plagubot.Plugin +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonObject +import org.jetbrains.exposed.sql.Database +import org.koin.core.Koin +import org.koin.core.module.Module + +/** + * **ANY REALIZATION OF [FSMPlugin] MUST HAVE CONSTRUCTOR WITH ABSENCE OF INCOMING PARAMETERS** + * + * Use this interface for your bot. It is possible to use [kotlinx.serialization.SerialName] annotations on your plugins + * to set up short name for your plugin. Besides, simple name of your class will be used as key for deserialization + * too. + */ +@Serializable(FSMPluginSerializer::class) +interface FSMPlugin : Plugin { + suspend fun BehaviourContextWithFSM<*>.setupBotPlugin(koin: Koin) { + (this as BehaviourContext).setupBotPlugin(koin) + } +} diff --git a/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt b/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt new file mode 100644 index 0000000..40b6a51 --- /dev/null +++ b/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt @@ -0,0 +1,25 @@ +package dev.inmo.plagubot.fsm + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Serializer(FSMPlugin::class) +class FSMPluginSerializer : KSerializer { + override val descriptor: SerialDescriptor + get() = String.serializer().descriptor + + override fun deserialize(decoder: Decoder): FSMPlugin { + val kclass = Class.forName(decoder.decodeString()).kotlin + return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as FSMPlugin + } + + override fun serialize(encoder: Encoder, value: FSMPlugin) { + encoder.encodeString( + value::class.java.canonicalName + ) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 44f17bb..b6f45ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,6 +32,7 @@ kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", vers kt-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kt-serialization" } tgbotapi = { module = "dev.inmo:tgbotapi", version.ref = "tgbotapi" } +tgbotapi-behaviourBuilder-fsm = { module = "dev.inmo:tgbotapi.behaviour_builder.fsm", version.ref = "tgbotapi" } microutils-repos-exposed = { module = "dev.inmo:micro_utils.repos.exposed", version.ref = "microutils" } kslog = { module = "dev.inmo:kslog", version.ref = "kslog" } diff --git a/settings.gradle b/settings.gradle index ab5d8e7..24fa9bc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,13 @@ -String[] toInclude = [":bot", ":plugin"] +String[] toInclude = [":bot", ":plugin", ":fsm:bot", ":fsm:plugin"] rootProject.name = 'plagubot' -toInclude.each { - include (it) - project(it).name = "${rootProject.name}${it.replace(":", ".")}" +toInclude.each { originalName -> + String projectDirectory = "${rootProject.projectDir.getAbsolutePath()}${originalName.replace(":", File.separator)}" + String projectName = "${rootProject.name}${originalName.replace(":", ".")}" + String projectIdentifier = ":${projectName}" + include projectIdentifier + ProjectDescriptor project = project(projectIdentifier) + project.name = projectName + project.projectDir = new File(projectDirectory) } From 751e9aa66cbab694832ee10f3e5f169beb3c7db0 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 20:09:21 +0600 Subject: [PATCH 05/12] remove redundant modules --- bot/build.gradle | 1 + .../main/kotlin/dev/inmo/plagubot/PlaguBot.kt | 24 +++- .../plagubot/config/FallbackStateHandler.kt | 3 + fsm/bot/build.gradle | 38 ------ fsm/bot/publish.gradle | 81 ------------- fsm/bot/publish.kpsb | 1 - fsm/bot/src/main/kotlin/App.kt | 25 ---- fsm/bot/src/main/kotlin/FSMPlaguBot.kt | 112 ------------------ fsm/bot/src/main/kotlin/HelloFSMPlugin.kt | 37 ------ fsm/plugin/build.gradle | 21 ---- fsm/plugin/publish.gradle | 81 ------------- fsm/plugin/publish.kpsb | 1 - fsm/plugin/src/main/kotlin/FSMPlugin.kt | 24 ---- .../src/main/kotlin/FSMPluginSerializer.kt | 25 ---- .../main/kotlin/dev/inmo/plagubot/Plugin.kt | 5 + settings.gradle | 2 +- 16 files changed, 29 insertions(+), 452 deletions(-) create mode 100644 bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt delete mode 100644 fsm/bot/build.gradle delete mode 100644 fsm/bot/publish.gradle delete mode 100644 fsm/bot/publish.kpsb delete mode 100644 fsm/bot/src/main/kotlin/App.kt delete mode 100644 fsm/bot/src/main/kotlin/FSMPlaguBot.kt delete mode 100644 fsm/bot/src/main/kotlin/HelloFSMPlugin.kt delete mode 100644 fsm/plugin/build.gradle delete mode 100644 fsm/plugin/publish.gradle delete mode 100644 fsm/plugin/publish.kpsb delete mode 100644 fsm/plugin/src/main/kotlin/FSMPlugin.kt delete mode 100644 fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt diff --git a/bot/build.gradle b/bot/build.gradle index fe93273..473692a 100644 --- a/bot/build.gradle +++ b/bot/build.gradle @@ -16,6 +16,7 @@ dependencies { api libs.jb.exposed.jdbc api libs.tgbotapi + api libs.tgbotapi.behaviourBuilder.fsm api libs.microutils.repos.exposed api libs.kslog diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt index b850b49..917da19 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt @@ -3,12 +3,14 @@ package dev.inmo.plagubot import dev.inmo.kslog.common.* import dev.inmo.micro_utils.common.Warning import dev.inmo.micro_utils.coroutines.runCatchingSafely -import dev.inmo.plagubot.config.Config -import dev.inmo.plagubot.config.defaultJsonFormat +import dev.inmo.micro_utils.fsm.common.State +import dev.inmo.micro_utils.fsm.common.StatesManager +import dev.inmo.micro_utils.fsm.common.managers.DefaultStatesManager +import dev.inmo.micro_utils.fsm.common.managers.InMemoryDefaultStatesManagerRepo +import dev.inmo.plagubot.config.* import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour +import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import kotlinx.coroutines.* import kotlinx.serialization.Serializable @@ -94,7 +96,19 @@ data class PlaguBot( GlobalContext.startKoin(koinApp) logger.i("Koin started") lateinit var behaviourContext: BehaviourContext - bot.buildBehaviour(scope = scope) { + bot.buildBehaviourWithFSM( + scope = scope, + defaultExceptionsHandler = { + logger.e("Something went wrong", it) + }, + statesManager = koinApp.koin.getOrNull>() ?: DefaultStatesManager( + InMemoryDefaultStatesManagerRepo() + ), + onStateHandlingErrorHandler = koinApp.koin.getOrNull>() ?: { state, e -> + logger.eS(e) { "Unable to handle state $state" } + null + } + ) { logger.i("Start setup of bot part") behaviourContext = this setupBotPlugin(koinApp.koin) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt b/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt new file mode 100644 index 0000000..05d944f --- /dev/null +++ b/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt @@ -0,0 +1,3 @@ +package dev.inmo.plagubot.config + +typealias FallbackStateHandler = suspend (T, Throwable) -> T? diff --git a/fsm/bot/build.gradle b/fsm/bot/build.gradle deleted file mode 100644 index 495dfa1..0000000 --- a/fsm/bot/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id "org.jetbrains.kotlin.plugin.serialization" - id 'application' -} - -project.group="$group" -project.version="$version" - -apply from: "publish.gradle" - -dependencies { - implementation libs.kt.stdlib - api libs.kt.coroutines - api libs.kt.serialization - api libs.jb.exposed.jdbc - - api libs.tgbotapi - api libs.microutils.repos.exposed - api libs.kslog - - api libs.sqlite - - testImplementation libs.kt.test.junit - - api project(":plagubot.bot") - api project(":plagubot.fsm.plugin") -} - -application { - mainClassName = 'dev.inmo.plagubot.AppKt' -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - diff --git a/fsm/bot/publish.gradle b/fsm/bot/publish.gradle deleted file mode 100644 index a65131e..0000000 --- a/fsm/bot/publish.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'maven-publish' - -task javadocJar(type: Jar) { - from javadoc - classifier = 'javadoc' -} -task sourcesJar(type: Jar) { - from sourceSets.main.allSource - classifier = 'sources' -} - -publishing { - publications { - maven(MavenPublication) { - from components.java - - artifact javadocJar - artifact sourcesJar - - pom { - resolveStrategy = Closure.DELEGATE_FIRST - - description = "Base PlaguBot project" - name = "PlaguBot Bot" - url = "https://github.com/InsanusMokrassar/PlaguBot" - - scm { - developerConnection = "scm:git:[fetch=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git[push=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git" - url = "ssh://git@github.com/InsanusMokrassar/PlaguBot.git" - } - - developers { - - developer { - id = "InsanusMokrassar" - name = "Aleksei Ovsiannikov" - email = "ovsyannikov.alexey95@gmail.com" - } - - } - - licenses { - - license { - name = "Apache Software License 2.0" - url = "https://github.com/InsanusMokrassar/PlaguBot/LICENSE" - } - - } - } - repositories { - if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { - maven { - name = "sonatype" - url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - credentials { - username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') - password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') - } - } - } - } - } - } -} - -if (project.hasProperty("signing.gnupg.keyName")) { - apply plugin: 'signing' - - signing { - useGpgCmd() - - sign publishing.publications - } - - task signAll { - tasks.withType(Sign).forEach { - dependsOn(it) - } - } -} diff --git a/fsm/bot/publish.kpsb b/fsm/bot/publish.kpsb deleted file mode 100644 index b1382ea..0000000 --- a/fsm/bot/publish.kpsb +++ /dev/null @@ -1 +0,0 @@ -{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/PlaguBot/LICENSE"}],"mavenConfig":{"name":"PlaguBot Bot","description":"Base PlaguBot project","url":"https://github.com/InsanusMokrassar/PlaguBot","vcsUrl":"ssh://git@github.com/InsanusMokrassar/PlaguBot.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} \ No newline at end of file diff --git a/fsm/bot/src/main/kotlin/App.kt b/fsm/bot/src/main/kotlin/App.kt deleted file mode 100644 index e23073c..0000000 --- a/fsm/bot/src/main/kotlin/App.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.inmo.plagubot - -import dev.inmo.kslog.common.KSLog -import dev.inmo.kslog.common.i -import dev.inmo.plagubot.config.Config -import dev.inmo.plagubot.config.defaultJsonFormat -import kotlinx.serialization.InternalSerializationApi -import kotlinx.serialization.json.jsonObject -import java.io.File - -/** - * This method by default expects one argument in [args] field: path to config - */ -@InternalSerializationApi -suspend fun main(args: Array) { - KSLog.default = KSLog("PlaguBot") - val (configPath) = args - val file = File(configPath) - KSLog.i("Start read config from ${file.absolutePath}") - val json = defaultJsonFormat.parseToJsonElement(file.readText()).jsonObject - val config = defaultJsonFormat.decodeFromJsonElement(Config.serializer(), json) - KSLog.i("Config has been read") - - FSMPlaguBot(json, config).start().join() -} diff --git a/fsm/bot/src/main/kotlin/FSMPlaguBot.kt b/fsm/bot/src/main/kotlin/FSMPlaguBot.kt deleted file mode 100644 index b771485..0000000 --- a/fsm/bot/src/main/kotlin/FSMPlaguBot.kt +++ /dev/null @@ -1,112 +0,0 @@ -package dev.inmo.plagubot - -import dev.inmo.kslog.common.* -import dev.inmo.micro_utils.common.Warning -import dev.inmo.micro_utils.coroutines.runCatchingSafely -import dev.inmo.plagubot.config.Config -import dev.inmo.plagubot.config.defaultJsonFormat -import dev.inmo.plagubot.fsm.FSMPlugin -import dev.inmo.tgbotapi.bot.ktor.telegramBot -import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook -import dev.inmo.tgbotapi.extensions.behaviour_builder.* -import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling -import kotlinx.coroutines.* -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient -import kotlinx.serialization.json.JsonObject -import org.jetbrains.exposed.sql.Database -import org.koin.core.Koin -import org.koin.core.KoinApplication -import org.koin.core.context.GlobalContext -import org.koin.core.module.Module -import org.koin.core.scope.Scope -import org.koin.dsl.module - -val Scope.fsmPlagubot: FSMPlaguBot - get() = get() - -val Koin.fsmPlagubot: FSMPlaguBot - get() = get() - -@OptIn(Warning::class) -@Serializable -data class FSMPlaguBot( - private val json: JsonObject, - private val config: Config -) : FSMPlugin { - @Transient - private val bot = telegramBot(config.botToken) - - override fun Module.setupDI(database: Database, params: JsonObject) { - single { config } - single { config.plugins } - single { config.databaseConfig } - single { config.databaseConfig.database } - single { defaultJsonFormat } - single { this@FSMPlaguBot } - - includes( - config.plugins.mapNotNull { - runCatching { - module { - with(it) { - setupDI(database, params) - } - } - }.onFailure { e -> - logger.w("Unable to load DI part of $it", e) - }.getOrNull() - } - ) - } - - override suspend fun BehaviourContextWithFSM<*>.setupBotPlugin(koin: Koin) { - config.plugins.map { plugin -> - launch { - runCatchingSafely { - logger.i("Start loading of $plugin") - with(plugin) { - if (this is FSMPlugin) { - setupBotPlugin(koin) // use setupBotPlugin with BehaviourContextWithFSM as receiver - } else { - setupBotPlugin(koin) // use setupBotPlugin with BehaviourContext as receiver - } - } - }.onFailure { e -> - logger.w("Unable to load bot part of $plugin", e) - }.onSuccess { - logger.i("Complete loading of $plugin") - } - } - }.joinAll() - } - - /** - * This method will create an [Job] which will be the main [Job] of ran instance - */ - suspend fun start( - scope: CoroutineScope = CoroutineScope(Dispatchers.IO) - ): Job { - logger.i("Start initialization") - val koinApp = KoinApplication.init() - koinApp.modules( - module { - setupDI(config.databaseConfig.database, json) - } - ) - logger.i("Modules loaded") - GlobalContext.startKoin(koinApp) - logger.i("Koin started") - lateinit var behaviourContext: BehaviourContext - bot.buildBehaviourWithFSM (scope = scope) { - logger.i("Start setup of bot part") - behaviourContext = this - setupBotPlugin(koinApp.koin) - deleteWebhook() - } - logger.i("Behaviour builder has been setup") - return bot.startGettingOfUpdatesByLongPolling(scope = behaviourContext, updatesFilter = behaviourContext).also { - logger.i("Long polling has been started") - } - } -} diff --git a/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt b/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt deleted file mode 100644 index baa6b5b..0000000 --- a/fsm/bot/src/main/kotlin/HelloFSMPlugin.kt +++ /dev/null @@ -1,37 +0,0 @@ -package dev.inmo.plagubot - -import dev.inmo.kslog.common.* -import dev.inmo.tgbotapi.extensions.api.bot.getMe -import dev.inmo.tgbotapi.extensions.api.send.reply -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import org.jetbrains.exposed.sql.Database -import org.koin.core.Koin -import org.koin.core.module.Module - -@Serializable -@SerialName("Hello") -object HelloFSMPlugin : Plugin { - @Serializable - data class HelloPluginConfig( - val print: String - ) - - override fun Module.setupDI(database: Database, params: JsonObject) { - single { - get().decodeFromJsonElement(HelloPluginConfig.serializer(), params["helloPlugin"] ?: return@single null) - } - } - - override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { - logger.d { koin.get().print } - logger.dS { getMe().toString() } - onCommand("hello_world") { - reply(it, "Hello :)") - } - } -} diff --git a/fsm/plugin/build.gradle b/fsm/plugin/build.gradle deleted file mode 100644 index 7d57c41..0000000 --- a/fsm/plugin/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' - id "org.jetbrains.kotlin.plugin.serialization" -} - -project.group="$group" -project.version="$version" - -apply from: "publish.gradle" - -dependencies { - implementation libs.kt.stdlib - - api libs.tgbotapi.behaviourBuilder.fsm - api project(":plagubot.plugin") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} diff --git a/fsm/plugin/publish.gradle b/fsm/plugin/publish.gradle deleted file mode 100644 index ed05a9d..0000000 --- a/fsm/plugin/publish.gradle +++ /dev/null @@ -1,81 +0,0 @@ -apply plugin: 'maven-publish' - -task javadocJar(type: Jar) { - from javadoc - classifier = 'javadoc' -} -task sourcesJar(type: Jar) { - from sourceSets.main.allSource - classifier = 'sources' -} - -publishing { - publications { - maven(MavenPublication) { - from components.java - - artifact javadocJar - artifact sourcesJar - - pom { - resolveStrategy = Closure.DELEGATE_FIRST - - description = "Base dependency for whole PlaguBot project" - name = "PlaguBot Plugin" - url = "https://github.com/InsanusMokrassar/PlaguBot" - - scm { - developerConnection = "scm:git:[fetch=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git[push=]ssh://git@github.com/InsanusMokrassar/PlaguBot.git" - url = "ssh://git@github.com/InsanusMokrassar/PlaguBot.git" - } - - developers { - - developer { - id = "InsanusMokrassar" - name = "Aleksei Ovsiannikov" - email = "ovsyannikov.alexey95@gmail.com" - } - - } - - licenses { - - license { - name = "Apache Software License 2.0" - url = "https://github.com/InsanusMokrassar/PlaguBot/LICENSE" - } - - } - } - repositories { - if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { - maven { - name = "sonatype" - url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - credentials { - username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') - password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') - } - } - } - } - } - } -} - -if (project.hasProperty("signing.gnupg.keyName")) { - apply plugin: 'signing' - - signing { - useGpgCmd() - - sign publishing.publications - } - - task signAll { - tasks.withType(Sign).forEach { - dependsOn(it) - } - } -} diff --git a/fsm/plugin/publish.kpsb b/fsm/plugin/publish.kpsb deleted file mode 100644 index 40f786b..0000000 --- a/fsm/plugin/publish.kpsb +++ /dev/null @@ -1 +0,0 @@ -{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/PlaguBot/LICENSE"}],"mavenConfig":{"name":"PlaguBot Plugin","description":"Base dependency for whole PlaguBot project","url":"https://github.com/InsanusMokrassar/PlaguBot","vcsUrl":"ssh://git@github.com/InsanusMokrassar/PlaguBot.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}},"type":"JVM"} \ No newline at end of file diff --git a/fsm/plugin/src/main/kotlin/FSMPlugin.kt b/fsm/plugin/src/main/kotlin/FSMPlugin.kt deleted file mode 100644 index d089c15..0000000 --- a/fsm/plugin/src/main/kotlin/FSMPlugin.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.inmo.plagubot.fsm - -import dev.inmo.plagubot.Plugin -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonObject -import org.jetbrains.exposed.sql.Database -import org.koin.core.Koin -import org.koin.core.module.Module - -/** - * **ANY REALIZATION OF [FSMPlugin] MUST HAVE CONSTRUCTOR WITH ABSENCE OF INCOMING PARAMETERS** - * - * Use this interface for your bot. It is possible to use [kotlinx.serialization.SerialName] annotations on your plugins - * to set up short name for your plugin. Besides, simple name of your class will be used as key for deserialization - * too. - */ -@Serializable(FSMPluginSerializer::class) -interface FSMPlugin : Plugin { - suspend fun BehaviourContextWithFSM<*>.setupBotPlugin(koin: Koin) { - (this as BehaviourContext).setupBotPlugin(koin) - } -} diff --git a/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt b/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt deleted file mode 100644 index 40b6a51..0000000 --- a/fsm/plugin/src/main/kotlin/FSMPluginSerializer.kt +++ /dev/null @@ -1,25 +0,0 @@ -package dev.inmo.plagubot.fsm - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.Serializer -import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder - -@Serializer(FSMPlugin::class) -class FSMPluginSerializer : KSerializer { - override val descriptor: SerialDescriptor - get() = String.serializer().descriptor - - override fun deserialize(decoder: Decoder): FSMPlugin { - val kclass = Class.forName(decoder.decodeString()).kotlin - return (kclass.objectInstance ?: kclass.constructors.first { it.parameters.isEmpty() }.call()) as FSMPlugin - } - - override fun serialize(encoder: Encoder, value: FSMPlugin) { - encoder.encodeString( - value::class.java.canonicalName - ) - } -} diff --git a/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt b/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt index 87cfdee..cd493c5 100644 --- a/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt +++ b/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt @@ -1,6 +1,8 @@ package dev.inmo.plagubot +import dev.inmo.micro_utils.fsm.common.State import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonObject import org.jetbrains.exposed.sql.Database @@ -26,4 +28,7 @@ interface Plugin { suspend fun BehaviourContext.setupBotPlugin( koin: Koin ) {} + suspend fun BehaviourContextWithFSM.setupBotPlugin(koin: Koin) { + (this as BehaviourContext).setupBotPlugin(koin) + } } diff --git a/settings.gradle b/settings.gradle index 24fa9bc..2e81cdb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -String[] toInclude = [":bot", ":plugin", ":fsm:bot", ":fsm:plugin"] +String[] toInclude = [":bot", ":plugin"] rootProject.name = 'plagubot' From 7dad3581f1a0ea47c254f64645a7d2560bde59dc Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 20:30:10 +0600 Subject: [PATCH 06/12] fixes/fill of docs --- bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt | 4 ++++ bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt | 4 ++-- plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt index 89cb216..67926e2 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt @@ -1,10 +1,14 @@ package dev.inmo.plagubot import dev.inmo.kslog.common.* +import dev.inmo.micro_utils.fsm.common.State +import dev.inmo.plagubot.HelloPlugin.setupBotPlugin import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUnhandledCommand import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt index 917da19..8f721bf 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt @@ -62,7 +62,7 @@ data class PlaguBot( ) } - override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { + override suspend fun BehaviourContextWithFSM.setupBotPlugin(koin: Koin) { config.plugins.map { plugin -> launch { runCatchingSafely { @@ -113,7 +113,7 @@ data class PlaguBot( behaviourContext = this setupBotPlugin(koinApp.koin) deleteWebhook() - } + }.start() logger.i("Behaviour builder has been setup") return bot.startGettingOfUpdatesByLongPolling(scope = behaviourContext, updatesFilter = behaviourContext).also { logger.i("Long polling has been started") diff --git a/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt b/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt index cd493c5..83d6ad6 100644 --- a/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt +++ b/plugin/src/main/kotlin/dev/inmo/plagubot/Plugin.kt @@ -25,9 +25,19 @@ interface Plugin { database: Database, params: JsonObject ) {} + + /** + * Override this method in cases when you want to declare common bot behaviour. In case you wish to use FSM, you + * should override the method with receiver [BehaviourContextWithFSM] + */ suspend fun BehaviourContext.setupBotPlugin( koin: Koin ) {} + /** + * Override this method in cases when you want to declare full behaviour of the plugin. It is recommended to declare + * common logic of plugin in the [setupBotPlugin] with [BehaviourContext] receiver and use override this one + * for the FSM configuration + */ suspend fun BehaviourContextWithFSM.setupBotPlugin(koin: Koin) { (this as BehaviourContext).setupBotPlugin(koin) } From 6f74e6b4cbe97f9f16c6904eb171e635feb25065 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 13 Aug 2022 20:37:17 +0600 Subject: [PATCH 07/12] fill readme + rename fallback state handler --- README.md | 25 +++++++++++++++++++ .../main/kotlin/dev/inmo/plagubot/PlaguBot.kt | 2 +- .../plagubot/config/FallbackStateHandler.kt | 3 --- .../config/StateHandlingErrorHandler.kt | 3 +++ 4 files changed, 29 insertions(+), 4 deletions(-) delete mode 100644 bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt create mode 100644 bot/src/main/kotlin/dev/inmo/plagubot/config/StateHandlingErrorHandler.kt diff --git a/README.md b/README.md index b867809..4386187 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,28 @@ You can create your bot using That is a set of libraries for plagubots. Look at the [PlaguBot Plugin template](https://insanusmokrassar.github.io/PlaguBotPluginTemplate/) to find how to create your bot. + +### Technical help + +In this bot has been used variant with FSM. That means that you may use all the [Behaviour Builder with FSM](https://bookstack.inmo.dev/books/telegrambotapi/page/behaviour-builder-with-fsm) functionality. In case you wish to setup states repo, you should use the next code in the `setupDI` of your plugin: + +```kotlin +single> { + // setup your manager and return here + // Default is: + DefaultStatesManager( + InMemoryDefaultStatesManagerRepo() + ) +} +``` + +Besides, you may setup handling errors lambda in the same function: + +```kotlin +single> { + { state, e -> + logger.eS(e) { "Unable to handle state $state" } // logging by default + null // you should return new state or null, default callback will return null + } +} +``` diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt index 8f721bf..8fcdb1f 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt @@ -104,7 +104,7 @@ data class PlaguBot( statesManager = koinApp.koin.getOrNull>() ?: DefaultStatesManager( InMemoryDefaultStatesManagerRepo() ), - onStateHandlingErrorHandler = koinApp.koin.getOrNull>() ?: { state, e -> + onStateHandlingErrorHandler = koinApp.koin.getOrNull>() ?: { state, e -> logger.eS(e) { "Unable to handle state $state" } null } diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt b/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt deleted file mode 100644 index 05d944f..0000000 --- a/bot/src/main/kotlin/dev/inmo/plagubot/config/FallbackStateHandler.kt +++ /dev/null @@ -1,3 +0,0 @@ -package dev.inmo.plagubot.config - -typealias FallbackStateHandler = suspend (T, Throwable) -> T? diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/config/StateHandlingErrorHandler.kt b/bot/src/main/kotlin/dev/inmo/plagubot/config/StateHandlingErrorHandler.kt new file mode 100644 index 0000000..14b136d --- /dev/null +++ b/bot/src/main/kotlin/dev/inmo/plagubot/config/StateHandlingErrorHandler.kt @@ -0,0 +1,3 @@ +package dev.inmo.plagubot.config + +typealias StateHandlingErrorHandler = suspend (T, Throwable) -> T? From 81ca68dc0692b9123cd2dd4163ca043469d7cf8e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 14 Aug 2022 00:10:45 +0600 Subject: [PATCH 08/12] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc409dc..3016f48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * `kslog`: `0.5.0` * `uuid`: `0.5.0` * `exposed`: `0.39.2` + * `microutils`: `0.12.0` ## 1.4.1 From 53667fa8b8a2ff9f8d031e7ff40abaebdafd47ae Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 16 Aug 2022 00:50:12 +0600 Subject: [PATCH 09/12] Update libs.versions.toml --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b6f45ad..16974bb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ kt-serialization = "1.4.0-RC" kt-coroutines = "1.6.4" microutils = "0.12.1" -tgbotapi = "3.1.0" +tgbotapi = "3.1.1" kslog = "0.5.0" jb-exposed = "0.39.2" From caa028b79970e788e39ac05c68fc4500f1a38db3 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 17 Aug 2022 09:55:54 +0600 Subject: [PATCH 10/12] Update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3016f48..4861d4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,13 @@ ## 2.1.0 * `Versions`: - * `tgbotapi`: `3.1.0` + * `tgbotapi`: `3.1.1` * `ktor`: `2.1.0` * `microutils`: `0.12.1` +* `Plugins`: + * New fum of `Plugin` with `BehaviourContextWithFSM` receiver +* `Bot`: + * Now bot uses `buildBehaviourWithFSM` to be able to setup bot with FSM ## 2.0.0 From 6c2cfbf06fcfbfb282454143a83749f05112ba21 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 18 Aug 2022 12:27:11 +0600 Subject: [PATCH 11/12] a little upgrade of hello plugin --- bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt index 67926e2..2f5a481 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt @@ -31,11 +31,12 @@ object HelloPlugin : Plugin { } } - override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { - logger.d { koin.get().print } + override suspend fun BehaviourContextWithFSM.setupBotPlugin(koin: Koin) { + val toPrint = koin.getOrNull() ?.print ?: "Hello :)" + logger.d { toPrint } logger.dS { getMe().toString() } onCommand("hello_world") { - reply(it, "Hello :)") + reply(it, toPrint) } } } From ec2852beb0ee045356e42875da44f8c2748fb84b Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 18 Aug 2022 12:42:34 +0600 Subject: [PATCH 12/12] complete with including of FSM --- .../kotlin/dev/inmo/plagubot/HelloPlugin.kt | 29 +++++++++++++++++-- .../main/kotlin/dev/inmo/plagubot/PlaguBot.kt | 6 ++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt index 2f5a481..085722e 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/HelloPlugin.kt @@ -5,10 +5,14 @@ import dev.inmo.micro_utils.fsm.common.State import dev.inmo.plagubot.HelloPlugin.setupBotPlugin import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.send.reply -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM +import dev.inmo.tgbotapi.extensions.api.send.sendMessage +import dev.inmo.tgbotapi.extensions.behaviour_builder.* +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitText +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitTextMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUnhandledCommand +import dev.inmo.tgbotapi.types.ChatId +import kotlinx.coroutines.flow.first import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -28,15 +32,34 @@ object HelloPlugin : Plugin { override fun Module.setupDI(database: Database, params: JsonObject) { single { get().decodeFromJsonElement(HelloPluginConfig.serializer(), params["helloPlugin"] ?: return@single null) + + } } + private sealed interface InternalFSMState : State { + override val context: ChatId + data class DidntSaidHello(override val context: ChatId) : InternalFSMState + data class SaidHelloOnce(override val context: ChatId) : InternalFSMState + } + override suspend fun BehaviourContextWithFSM.setupBotPlugin(koin: Koin) { val toPrint = koin.getOrNull() ?.print ?: "Hello :)" logger.d { toPrint } logger.dS { getMe().toString() } onCommand("hello_world") { - reply(it, toPrint) + startChain(InternalFSMState.DidntSaidHello(it.chat.id)) + } + + strictlyOn { state: InternalFSMState.DidntSaidHello -> + sendMessage(state.context, toPrint) + InternalFSMState.SaidHelloOnce(state.context) + } + + strictlyOn { state: InternalFSMState.SaidHelloOnce -> + val message = waitTextMessage().first() + reply(message, "Sorry, I can answer only this: $toPrint") + InternalFSMState.SaidHelloOnce(state.context) } } } diff --git a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt index 8fcdb1f..756aac8 100644 --- a/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt +++ b/bot/src/main/kotlin/dev/inmo/plagubot/PlaguBot.kt @@ -5,8 +5,7 @@ import dev.inmo.micro_utils.common.Warning import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.fsm.common.State import dev.inmo.micro_utils.fsm.common.StatesManager -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.* import dev.inmo.plagubot.config.* import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.webhook.deleteWebhook @@ -102,7 +101,8 @@ data class PlaguBot( logger.e("Something went wrong", it) }, statesManager = koinApp.koin.getOrNull>() ?: DefaultStatesManager( - InMemoryDefaultStatesManagerRepo() + koinApp.koin.getOrNull>() ?: InMemoryDefaultStatesManagerRepo(), + onStartContextsConflictResolver = { _, _ -> false } ), onStateHandlingErrorHandler = koinApp.koin.getOrNull>() ?: { state, e -> logger.eS(e) { "Unable to handle state $state" }