diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e9a2c0..a3dab43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ ## 0.1.1 * Update dependencies +* `Triggers` + * `SelectorWithTimer` + * Opportunity to get schedule of posts using `publishing_autoschedule` command ## 0.0.10 diff --git a/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt index 75bad45..fafeb3f 100644 --- a/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt +++ b/ratings/selector/src/commonMain/kotlin/DefaultSelector.kt @@ -11,11 +11,11 @@ class DefaultSelector ( private val ratingsRepo: RatingsRepo, private val postsRepo: PostsRepo ) : Selector { - override suspend fun take(n: Int, now: DateTime): List { + override suspend fun take(n: Int, now: DateTime, exclude: List): List { val result = mutableListOf() do { - val selected = config.active(now.time) ?.rating ?.select(ratingsRepo, postsRepo, result, now) ?: break + val selected = config.active(now.time) ?.rating ?.select(ratingsRepo, postsRepo, result + exclude, now) ?: break result.add(selected) } while (result.size < n) diff --git a/ratings/selector/src/commonMain/kotlin/Selector.kt b/ratings/selector/src/commonMain/kotlin/Selector.kt index 813aa20..134f6b1 100644 --- a/ratings/selector/src/commonMain/kotlin/Selector.kt +++ b/ratings/selector/src/commonMain/kotlin/Selector.kt @@ -4,5 +4,5 @@ import com.soywiz.klock.DateTime import dev.inmo.plaguposter.posts.models.PostId interface Selector { - suspend fun take(n: Int = 1, now: DateTime = DateTime.now()): List + suspend fun take(n: Int = 1, now: DateTime = DateTime.now(), exclude: List = emptyList()): List } diff --git a/settings.gradle b/settings.gradle index 1bb37bc..f356c8c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,7 +11,6 @@ String[] includes = [ ":ratings:gc", ":triggers:command", ":triggers:selector_with_timer", - ":triggers:selector_with_scheduling", ":triggers:timer", ":triggers:timer:disablers:ratings", ":triggers:timer:disablers:autoposts", diff --git a/triggers/selector_with_scheduling/build.gradle b/triggers/selector_with_scheduling/build.gradle deleted file mode 100644 index f95f579..0000000 --- a/triggers/selector_with_scheduling/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" - id "org.jetbrains.kotlin.plugin.serialization" -} - -apply from: "$mppProjectWithSerializationPresetPath" - -kotlin { - sourceSets { - commonMain { - dependencies { - api project(":plaguposter.common") - } - } - } -} diff --git a/triggers/selector_with_scheduling/src/commonMain/kotlin/PackageInfo.kt b/triggers/selector_with_scheduling/src/commonMain/kotlin/PackageInfo.kt deleted file mode 100644 index 6b58a49..0000000 --- a/triggers/selector_with_scheduling/src/commonMain/kotlin/PackageInfo.kt +++ /dev/null @@ -1 +0,0 @@ -package dev.inmo.plaguposter.triggers.selector_with_scheduling diff --git a/triggers/selector_with_scheduling/src/jvmMain/kotlin/Plugin.kt b/triggers/selector_with_scheduling/src/jvmMain/kotlin/Plugin.kt deleted file mode 100644 index d360aa8..0000000 --- a/triggers/selector_with_scheduling/src/jvmMain/kotlin/Plugin.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.inmo.plaguposter.triggers.selector_with_scheduling - -import dev.inmo.plagubot.Plugin -import kotlinx.serialization.json.* -import org.jetbrains.exposed.sql.Database -import org.koin.core.module.Module - -object Plugin : Plugin { - override fun Module.setupDI(database: Database, params: JsonObject) { - } -} diff --git a/triggers/selector_with_scheduling/src/main/AndroidManifest.xml b/triggers/selector_with_scheduling/src/main/AndroidManifest.xml deleted file mode 100644 index acdf85d..0000000 --- a/triggers/selector_with_scheduling/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/triggers/selector_with_timer/src/jvmMain/kotlin/Plugin.kt b/triggers/selector_with_timer/src/jvmMain/kotlin/Plugin.kt index 9fb94fe..42aae1c 100644 --- a/triggers/selector_with_timer/src/jvmMain/kotlin/Plugin.kt +++ b/triggers/selector_with_timer/src/jvmMain/kotlin/Plugin.kt @@ -1,14 +1,38 @@ package dev.inmo.plaguposter.triggers.selector_with_timer +import com.soywiz.klock.DateFormat import dev.inmo.krontab.KrontabTemplate import dev.inmo.krontab.toSchedule import dev.inmo.krontab.utils.asFlowWithDelays +import dev.inmo.krontab.utils.asFlowWithoutDelays +import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.micro_utils.pagination.FirstPagePagination +import dev.inmo.micro_utils.pagination.Pagination +import dev.inmo.micro_utils.pagination.firstIndex +import dev.inmo.micro_utils.pagination.lastIndexExclusive import dev.inmo.plagubot.Plugin +import dev.inmo.plaguposter.common.ChatConfig +import dev.inmo.plaguposter.posts.models.PostId +import dev.inmo.plaguposter.posts.repo.ReadPostsRepo import dev.inmo.plaguposter.posts.sending.PostPublisher import dev.inmo.plaguposter.ratings.selector.Selector +import dev.inmo.tgbotapi.extensions.api.answers.answer +import dev.inmo.tgbotapi.extensions.api.edit.edit +import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery +import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat +import dev.inmo.tgbotapi.extensions.utils.formatting.makeLinkToMessage +import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard +import dev.inmo.tgbotapi.extensions.utils.types.buttons.urlButton +import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup +import dev.inmo.tgbotapi.utils.row import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.collectIndexed +import kotlinx.coroutines.flow.take import kotlinx.serialization.* import kotlinx.serialization.json.* import org.jetbrains.exposed.sql.Database @@ -16,15 +40,21 @@ import org.koin.core.Koin import org.koin.core.module.Module object Plugin : Plugin { - @Serializable + private const val pageCallbackDataQueryPrefix = "publishing_autoschedule page" + private const val pageCallbackDataQuerySize = 5 + @Serializable internal data class Config( @SerialName("krontab") - val krontabTemplate: KrontabTemplate + val krontabTemplate: KrontabTemplate, + val dateTimeFormat: String = "HH:mm:ss, dd.MM.yyyy" ) { @Transient val krontab by lazy { krontabTemplate.toSchedule() } + + @Transient + val format: DateFormat = DateFormat(dateTimeFormat) } override fun Module.setupDI(database: Database, params: JsonObject) { single { get().decodeFromJsonElement(Config.serializer(), params["timer_trigger"] ?: return@single null) } @@ -35,12 +65,89 @@ object Plugin : Plugin { val publisher = koin.get() val selector = koin.get() val filters = koin.getAll().distinct() - koin.get().krontab.asFlowWithDelays().subscribeSafelyWithoutExceptions(this) { dateTime -> + val chatConfig = koin.get() + val postsRepo = koin.get() + + val krontab = koin.get().krontab + val dateTimeFormat = koin.get().format + krontab.asFlowWithDelays().subscribeSafelyWithoutExceptions(this) { dateTime -> selector.take(now = dateTime).forEach { postId -> if (filters.all { it.check(postId, dateTime) }) { publisher.publish(postId) } } } + + suspend fun buildPage(pagination: Pagination = FirstPagePagination(size = pageCallbackDataQuerySize)): InlineKeyboardMarkup { + return inlineKeyboard { + row { + if (pagination.page > 1) { + dataButton("⬅️", "${pageCallbackDataQueryPrefix}0") + } + if (pagination.page > 0) { + dataButton("◀️", "${pageCallbackDataQueryPrefix}${pagination.page - 1}") + } + + dataButton("\uD83D\uDD04 ${pagination.page}", "${pageCallbackDataQueryPrefix}${pagination.page}") + dataButton("▶️", "${pageCallbackDataQueryPrefix}${pagination.page + 1}") + } + + val selected = mutableListOf() + krontab.asFlowWithoutDelays().take(pagination.lastIndexExclusive).collectIndexed { i, dateTime -> + val postId = selector.take(now = dateTime, exclude = selected).firstOrNull() ?.also { postId -> + if (filters.all { it.check(postId, dateTime) }) { + selected.add(postId) + } else { + return@collectIndexed + } + } + + val post = postsRepo.getFirstMessageInfo(postId ?: return@collectIndexed) + if (i < pagination.firstIndex || post == null) { + return@collectIndexed + } + + row { + urlButton( + dateTime.format(dateTimeFormat), + makeLinkToMessage(post.chatId, post.messageId) + ) + } + } + } + } + + onCommand("publishing_autoschedule", initialFilter = { it.sameChat(chatConfig.sourceChatId) }) { + val keyboard = buildPage() + + runCatchingSafely { + edit(it, replyMarkup = keyboard) { + +"Your schedule:" + } + }.onFailure { _ -> + send(it.chat, replyMarkup = keyboard) { + +"Your schedule:" + } + } + } + + onMessageDataCallbackQuery( + Regex("^$pageCallbackDataQueryPrefix"), + initialFilter = { it.message.sameChat(chatConfig.sourceChatId) } + ) { + val page = it.data.removePrefix(pageCallbackDataQueryPrefix).toIntOrNull() ?: let { _ -> + answer(it) + return@onMessageDataCallbackQuery + } + + runCatchingSafely { + edit( + it.message, + replyMarkup = buildPage(Pagination(page, size = pageCallbackDataQuerySize)) + ) + }.onFailure { _ -> + answer(it) + } + } } }