diff --git a/build.gradle b/build.gradle index 3c86d09..716147d 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,8 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version" implementation "dev.inmo:tgbotapi:$tgbotapi_version" + implementation "dev.inmo:micro_utils.repos.exposed:$microutils_version" + api "org.xerial:sqlite-jdbc:$sqlite_version" } application { diff --git a/gradle.properties b/gradle.properties index 91b6e47..9e50bb8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,8 @@ kotlin.incremental=true kotlin_version=1.4.10 kotlin_coroutines_version=1.4.1 kotlin_serialisation_runtime_version=1.0.1 -tgbotapi_version=0.30.0 +tgbotapi_version=0.30.3 +microutils_version=0.3.2 +sqlite_version=3.30.1 version=0.0.1 diff --git a/src/main/kotlin/dev/inmo/plagubot/App.kt b/src/main/kotlin/dev/inmo/plagubot/App.kt index 76ce1df..b9cfc6a 100644 --- a/src/main/kotlin/dev/inmo/plagubot/App.kt +++ b/src/main/kotlin/dev/inmo/plagubot/App.kt @@ -1,13 +1,41 @@ package dev.inmo.plagubot -import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.telegramBot +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling +import dev.inmo.tgbotapi.types.botCommandsLimit +import kotlinx.coroutines.* +import java.io.File /** - * This method by default expects one argument in [args] field: telegram bot token + * This method by default expects one argument in [args] field: path to config */ suspend fun main(args: Array) { - val bot = telegramBot(args.first()) + val (configPath) = args + val file = File(configPath) + val config = configSerialFormat.decodeFromString(Config.serializer(), file.readText()) + + val scope = CoroutineScope(Dispatchers.Default) + + val bot = telegramBot(config.botToken) + + bot.startGettingFlowsUpdatesByLongPolling(scope = scope) { + val commands = config.plugins.flatMap { + it.invoke(bot, config.database.database, this, scope) + it.getCommands() + }.let { + val futureUnavailable = it.drop(botCommandsLimit.last) + if (futureUnavailable.isNotEmpty()) { + println("Next commands are out of range in setting command request and will be unavailable from autocompleting: ${futureUnavailable}") + } + it.take(botCommandsLimit.last) + } + scope.launch { + safelyWithoutExceptions { + bot.setMyCommands(commands) + } + } + } - println(bot.getMe()) } diff --git a/src/main/kotlin/dev/inmo/plagubot/Config.kt b/src/main/kotlin/dev/inmo/plagubot/Config.kt index eb7b62d..a0e1a0e 100644 --- a/src/main/kotlin/dev/inmo/plagubot/Config.kt +++ b/src/main/kotlin/dev/inmo/plagubot/Config.kt @@ -1,9 +1,19 @@ package dev.inmo.plagubot -import kotlinx.serialization.Contextual -import kotlinx.serialization.Serializable +import dev.inmo.plagubot.config.DatabaseConfig +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.serialization.* +import kotlinx.serialization.json.Json + +val configSerialFormat: StringFormat + get() = Json { + ignoreUnknownKeys = true + } @Serializable data class Config( - val plugins: List<@Contextual Plugin> + val plugins: List<@Contextual Plugin>, + val database: DatabaseConfig, + val botToken: String ) diff --git a/src/main/kotlin/dev/inmo/plagubot/Plugin.kt b/src/main/kotlin/dev/inmo/plagubot/Plugin.kt index ce3eb76..b691c48 100644 --- a/src/main/kotlin/dev/inmo/plagubot/Plugin.kt +++ b/src/main/kotlin/dev/inmo/plagubot/Plugin.kt @@ -1,3 +1,17 @@ package dev.inmo.plagubot -interface Plugin +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.types.BotCommand +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import kotlinx.coroutines.CoroutineScope +import org.jetbrains.exposed.sql.Database + +interface Plugin { + suspend fun getCommands(): List = emptyList() + suspend operator fun invoke( + bot: TelegramBot, + database: Database, + updatesFilter: FlowsUpdatesFilter, + scope: CoroutineScope + ) +} diff --git a/src/main/kotlin/dev/inmo/plagubot/config/DatabaseConfig.kt b/src/main/kotlin/dev/inmo/plagubot/config/DatabaseConfig.kt new file mode 100644 index 0000000..cee3daa --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/config/DatabaseConfig.kt @@ -0,0 +1,48 @@ +package dev.inmo.plagubot.config + +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.transactions.transactionManager +import org.sqlite.JDBC +import java.sql.Connection + +@Serializable +data class DatabaseConfig( + val url: String, + val driver: String = JDBC::class.qualifiedName!!, + val username: String = "", + val password: String = "", + val initAutomatically: Boolean = true +) { + @Transient + private lateinit var _database: Database + val database: Database + get() = try { + _database + } catch (e: UninitializedPropertyAccessException) { + Database.connect( + url, + driver, + username, + password + ).also { + _database = it + it.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE // Or Connection.TRANSACTION_READ_UNCOMMITTED + } + } + + init { + if (initAutomatically) { + database // init database + } + } + + @Deprecated( + "Deprecated due to the replacement by lateinit database field with the same functionality", + ReplaceWith("database") + ) + fun connect(): Database { + return database + } +}