39 Commits

Author SHA1 Message Date
1d200c38e4 update gradle wrapper 2025-06-27 10:38:46 +06:00
9ebadfe1e4 update dependencies and fix build 2025-06-27 10:37:53 +06:00
333f807c97 include nmcp 2025-06-27 10:26:33 +06:00
9ae7238974 update publishing way 2025-06-27 10:25:58 +06:00
275cc13ea7 start 0.9.0 2025-06-27 10:08:56 +06:00
cdcdb4ef00 Merge pull request #27 from InsanusMokrassar/0.8.0
0.8.0
2024-11-06 20:50:52 +06:00
aeb3bf1f3d ratings auto clearup 2024-11-06 20:48:21 +06:00
569f15330c fixes after update 2024-11-06 20:04:54 +06:00
defc83740d update dependencies 2024-09-22 18:53:55 +06:00
66b2b21c1c start 0.8.0 2024-09-22 18:43:21 +06:00
63800ce19c Merge pull request #26 from InsanusMokrassar/0.7.0
0.7.0
2024-09-22 18:42:43 +06:00
c2612ec6e9 Revert "rework to use just Java target"
This reverts commit 0273284023.
2024-09-07 22:58:56 +06:00
0273284023 rework to use just Java target 2024-09-07 22:55:33 +06:00
9af72d5d0f fix build 2024-09-07 22:42:13 +06:00
b879426f9d Update mppJsProject.gradle 2024-08-13 02:11:33 +06:00
18b8ce0d3d Update mppProjectWithSerialization.gradle 2024-08-13 02:10:58 +06:00
a4686a56df update dependencies 2024-08-13 00:08:21 +06:00
e567f5b3b4 update dependencies 2024-07-13 21:39:37 +06:00
3941c1dfed start 0.7.0 2024-07-13 21:28:37 +06:00
0bdd965a4a Merge pull request #25 from InsanusMokrassar/0.6.0
0.6.0
2024-07-10 22:08:34 +06:00
ad967c002a update github ci/cd scripts 2024-06-28 13:28:12 +06:00
e3958aff3f update dependencies 2024-06-28 13:24:13 +06:00
ae6993efba start 0.6.0 2024-06-28 13:09:37 +06:00
991b0ef3d3 improvements in cachedratingsrepo 2024-05-13 21:42:57 +06:00
aa6c62d66e fixes in CachedRatingsRepo 2024-05-13 21:25:13 +06:00
e6a0d67444 improve of autoclear 2024-05-13 19:57:12 +06:00
16bd62da51 Merge pull request #24 from InsanusMokrassar/0.5.6
0.5.6
2024-05-13 19:43:19 +06:00
edc75a09ee add opportunity to trigger autoclean 2024-05-13 19:41:17 +06:00
0f958f94cb fix in plugin of ratings gc 2024-05-13 19:36:09 +06:00
69320a3b62 start 0.5.6 2024-05-13 19:34:10 +06:00
b6fa2a6cd3 Merge pull request #23 from InsanusMokrassar/0.5.5
0.5.5
2024-04-24 12:15:59 +06:00
e24237beb3 update dependencies 2024-04-24 12:14:33 +06:00
d2d3665c5e start 0.5.5 2024-04-24 11:58:27 +06:00
9ef9de2b8a Merge pull request #22 from InsanusMokrassar/0.5.4
0.5.4
2024-02-18 22:45:21 +06:00
cef350667b update dependencies 2024-02-18 22:41:33 +06:00
80ed679241 start 0.5.4 2024-02-18 21:26:02 +06:00
39391636ac Merge pull request #20 from InsanusMokrassar/several_sources
Several sources
2024-02-15 20:38:17 +06:00
29a19df7fc update dependencies and version 2024-02-15 20:30:10 +06:00
250f88e2fe preview adding of several sources chats 2024-02-15 20:21:48 +06:00
51 changed files with 402 additions and 280 deletions

View File

@@ -14,6 +14,7 @@ jobs:
java-version: 17
- name: Rewrite version
run: |
printf "\norg.gradle.jvmargs=-Xmx1g -Xms500m\nkotlin.daemon.jvmargs=-Xmx1g -Xms500m\norg.gradle.daemon=false" >> gradle.properties
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
cat gradle.properties | sed -e "s/^version=\([0-9\.]*\)/version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp
rm gradle.properties

View File

@@ -16,6 +16,7 @@ jobs:
java-version: 17
- name: Rewrite version
run: |
printf "\norg.gradle.jvmargs=-Xmx1g -Xms500m\nkotlin.daemon.jvmargs=-Xmx1g -Xms500m\norg.gradle.daemon=false" >> gradle.properties
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
if [[ "$branch" != "master" ]]; then
cat gradle.properties | sed -e "s/^version=\([0-9\.]*\)/version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.idea
.kotlin/
out/*
*.iml
target

View File

@@ -1,5 +1,41 @@
# PlaguPoster
## 0.9.0
* Dependencies update
## 0.8.0
* Dependencies update
* `Ratings`:
* Add autoclearing of ratings without target posts each half hours
## 0.7.0
* Dependencies update
## 0.6.0
* Dependencies update
## 0.5.6
* `Ratings`:
* `GC`:
* Now GC will autoclear ratings even if post is absent in posts repo
## 0.5.5
* Dependencies update
## 0.5.4
* Dependencies update
## 0.5.3
* Dependencies update
## 0.5.2
* Dependencies update

View File

@@ -2,8 +2,8 @@ buildscript {
repositories {
google()
mavenCentral()
mavenLocal()
maven { url "https://plugins.gradle.org/m2/" }
mavenLocal()
}
dependencies {
@@ -13,12 +13,29 @@ buildscript {
}
}
plugins {
alias(libs.plugins.nmcp.aggregation)
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
nmcpAggregation {
centralPortal {
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')
validationTimeout = Duration.ofHours(4)
publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED"
}
publishAllProjectsProbablyBreakingProjectIsolation()
}
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
google()
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" }
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
mavenLocal()
}
}

View File

@@ -12,15 +12,20 @@ data class ChatConfig(
val targetChatId: IdChatIdentifier? = null,
@SerialName("sourceChat")
@Serializable(FullChatIdentifierSerializer::class)
val sourceChatId: IdChatIdentifier,
val sourceChatId: IdChatIdentifier?,
@SerialName("cacheChat")
@Serializable(FullChatIdentifierSerializer::class)
val cacheChatId: IdChatIdentifier,
@SerialName("targetChats")
val targetChatIds: List<@Serializable(FullChatIdentifierSerializer::class) IdChatIdentifier> = emptyList(),
@SerialName("sourceChats")
val sourceChatIds: List<@Serializable(FullChatIdentifierSerializer::class) IdChatIdentifier> = emptyList(),
) {
val allTargetChatIds by lazy {
listOfNotNull(targetChatId) + targetChatIds
(listOfNotNull(targetChatId) + targetChatIds).toSet()
}
val allSourceChatIds by lazy {
(listOfNotNull(sourceChatId) + sourceChatIds).toSet()
}
init {
@@ -30,8 +35,8 @@ data class ChatConfig(
}
fun check(chatId: IdChatIdentifier) = when (chatId) {
targetChatId,
sourceChatId,
in allTargetChatIds,
in allSourceChatIds,
cacheChatId -> true
else -> false
}

View File

@@ -1,9 +1,6 @@
package dev.inmo.plaguposter.common
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.FullChatIdentifierSerializer
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.message.abstracts.Message
import kotlinx.serialization.Serializable
@@ -11,7 +8,7 @@ import kotlinx.serialization.Serializable
data class ShortMessageInfo(
@Serializable(FullChatIdentifierSerializer::class)
val chatId: IdChatIdentifier,
val messageId: MessageIdentifier
val messageId: MessageId
)
fun Message.short() = ShortMessageInfo(chat.id, messageId)

View File

@@ -11,15 +11,14 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.booleanOrNull
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
object CommonPlugin : Plugin {
private val Log = logger
override fun Module.setupDI(database: Database, params: JsonObject) {
override fun Module.setupDI(config: JsonObject) {
single { CoroutineScope(Dispatchers.Default + SupervisorJob()) }
val useCache = (params["useCache"] as? JsonPrimitive) ?.booleanOrNull ?: true
val useCache = (config["useCache"] as? JsonPrimitive) ?.booleanOrNull ?: true
useCache(useCache)
}
@@ -27,7 +26,7 @@ object CommonPlugin : Plugin {
val config = koin.get<ChatConfig>()
Log.iS { "Target chats info: ${config.allTargetChatIds.map { getChat(it) }.joinToString()}" }
Log.iS { "Source chat info: ${getChat(config.sourceChatId)}" }
Log.iS { "Source chats info: ${config.allSourceChatIds.map { getChat(it) }.joinToString()}" }
Log.iS { "Cache chat info: ${getChat(config.cacheChatId)}" }
}
}

View File

@@ -11,12 +11,19 @@ private String getCurrentVersionChangelog() {
return changelogDataOS.toString().trim()
}
if (new File(projectDir, "secret.gradle").exists()) {
apply from: './secret.gradle'
def githubTokenVariableName = "GITHUB_RELEASE_TOKEN"
def githubTokenVariableFromEnv = System.getenv(githubTokenVariableName)
def secretFile = new File(projectDir, "secret.gradle")
if (secretFile.exists() || project.hasProperty(githubTokenVariableName) || (githubTokenVariableFromEnv != "" && githubTokenVariableFromEnv != null)) {
if (secretFile.exists()) {
apply from: './secret.gradle'
}
apply plugin: "com.github.breadmoirai.github-release"
def githubReleaseToken = project.hasProperty(githubTokenVariableName) ? project.property(githubTokenVariableName).toString() : githubTokenVariableFromEnv
githubRelease {
token "${project.property('GITHUB_RELEASE_TOKEN')}"
token githubReleaseToken
owner "InsanusMokrassar"
repo "PlaguPoster"

View File

@@ -1,5 +1,4 @@
kotlin.code.style=official
org.gradle.jvmargs=-Xmx1024m
org.gradle.parallel=true
kotlin.js.generate.externals=true
kotlin.incremental=true
@@ -10,4 +9,4 @@ android.enableJetifier=true
# Project data
group=dev.inmo
version=0.5.2
version=0.9.0

View File

@@ -1,16 +1,18 @@
[versions]
kotlin = "1.9.21"
kotlin-serialization = "1.6.2"
kotlin = "2.1.20"
kotlin-serialization = "1.8.1"
plagubot = "7.4.1"
tgbotapi = "9.4.2"
microutils = "0.20.19"
kslog = "1.3.1"
krontab = "2.2.4"
plagubot-plugins = "0.17.1"
plagubot = "10.6.0"
tgbotapi = "26.0.0"
microutils = "0.25.8"
kslog = "1.4.2"
krontab = "2.7.2"
plagubot-plugins = "0.24.4"
dokka = "1.9.10"
nmcp="0.2.1"
dokka = "2.0.0"
psql = "42.6.0"
@@ -47,3 +49,5 @@ kotlin-dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", vers
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
nmcp-aggregation = { id = "com.gradleup.nmcp.aggregation", version.ref = "nmcp" }

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -14,7 +14,7 @@ private val actualPlugin = dev.inmo.plagubot.plugins.inline.queries.Plugin
object Plugin : Plugin by actualPlugin {
private val log = TagLogger("InlinePlugin")
override fun Module.setupDI(database: Database, params: JsonObject) {
override fun Module.setupDI(params: JsonObject) {
single { actualPlugin }
}

View File

@@ -25,7 +25,6 @@ kotlin {
jsTest {
dependencies {
implementation libs.kotlin.test.js
implementation libs.kotlin.test.junit
}
}
}

View File

@@ -4,7 +4,13 @@ project.group = "$group"
apply from: "$publishGradlePath"
kotlin {
jvm()
jvm {
compilations.main {
kotlinOptions {
jvmTarget = "17"
}
}
}
js (IR) {
browser()
nodejs()
@@ -31,10 +37,14 @@ kotlin {
jsTest {
dependencies {
implementation libs.kotlin.test.js
implementation libs.kotlin.test.junit
}
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

@@ -14,6 +14,7 @@ import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.plugins.inline.queries.models.Format
import dev.inmo.plagubot.plugins.inline.queries.models.OfferTemplate
import dev.inmo.plagubot.plugins.inline.queries.repos.InlineTemplatesRepo
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.common.ChatConfig
import dev.inmo.plaguposter.posts.models.NewPost
import dev.inmo.plaguposter.posts.models.PostContentInfo
@@ -38,7 +39,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
@@ -49,10 +49,8 @@ object Plugin : Plugin {
val throttlingMillis: MilliSeconds = 1000,
val doFullCheck: Boolean = false
)
override fun Module.setupDI(database: Database, params: JsonObject) {
params["messagesChecker"] ?.let { element ->
single { get<Json>().decodeFromJsonElement(Config.serializer(), element) }
}
override fun Module.setupDI(config: JsonObject) {
registerConfig<Config>("messagesChecker") { null }
}
private val gcLogger = KSLog("GarbageCollector")
@@ -104,10 +102,12 @@ object Plugin : Plugin {
return@forEach
}
send(
chatsConfig.sourceChatId,
"Can't find any messages for post $postId. So, deleting it"
)
runCatching {
send(
chatsConfig.cacheChatId,
"Can't find any messages for post $postId. So, deleting it"
)
}
runCatching {
postsRepo.deleteById(postId)
}

View File

@@ -10,6 +10,7 @@ import dev.inmo.micro_utils.repos.cache.cached
import dev.inmo.micro_utils.repos.cache.full.cached
import dev.inmo.micro_utils.repos.cache.full.fullyCached
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.common.ChatConfig
import dev.inmo.plaguposter.common.UnsuccessfulSymbol
import dev.inmo.plaguposter.common.useCache
@@ -30,7 +31,8 @@ import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.message.ParseMode
@@ -54,10 +56,8 @@ object Plugin : Plugin {
val rootButtonText: String = "◀️",
val refreshButtonText: String? = "\uD83D\uDD04"
)
override fun Module.setupDI(database: Database, params: JsonObject) {
params["panel"] ?.let { element ->
single { get<Json>().decodeFromJsonElement(Config.serializer(), element) }
}
override fun Module.setupDI(params: JsonObject) {
registerConfig<Config>("panel") { null }
single {
val config = getOrNull<Config>() ?: Config()
val builtInButtons = listOfNotNull(
@@ -112,7 +112,7 @@ object Plugin : Plugin {
firstContent.chatId,
text = config.text,
parseMode = config.parseMode,
replyToMessageId = firstContent.messageId,
replyParameters = ReplyParameters(firstContent.chatId, firstContent.messageId),
replyMarkup = InlineKeyboardMarkup(buttons),
disableNotification = true
).also { sentMessage ->
@@ -128,7 +128,7 @@ object Plugin : Plugin {
suspend fun refreshPostMessage(
postId: PostId,
chatId: IdChatIdentifier,
messageId: MessageIdentifier
messageId: MessageId
) {
val post = postsRepo.getById(postId) ?: return
val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row ->
@@ -146,7 +146,7 @@ object Plugin : Plugin {
onMessageDataCallbackQuery (
initialFilter = {
it.data.startsWith(PanelButtonsAPI.openGlobalMenuDataPrefix) && it.message.chat.id == chatsConfig.sourceChatId
it.data.startsWith(PanelButtonsAPI.openGlobalMenuDataPrefix) && it.message.chat.id in chatsConfig.allSourceChatIds
}
) {
val postId = it.data.removePrefix(PanelButtonsAPI.openGlobalMenuDataPrefix).let(::PostId)
@@ -155,7 +155,7 @@ object Plugin : Plugin {
}
onMessageDataCallbackQuery(
initialFilter = {
it.data.startsWith("delete ") && it.message.chat.id == chatsConfig.sourceChatId
it.data.startsWith("delete ") && it.message.chat.id in chatsConfig.allSourceChatIds
}
) { query ->
val postId = query.data.removePrefix("delete ").let(::PostId)
@@ -182,7 +182,7 @@ object Plugin : Plugin {
}
onMessageDataCallbackQuery(
initialFilter = {
it.data.startsWith("refresh ") && it.message.chat.id == chatsConfig.sourceChatId
it.data.startsWith("refresh ") && it.message.chat.id in chatsConfig.allSourceChatIds
}
) { query ->
val postId = query.data.removePrefix("refresh ").let(::PostId)

View File

@@ -4,21 +4,18 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.FullChatIdentifierSerializer
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.Database
private val ChatIdToMessageSerializer = PairSerializer(FullChatIdentifierSerializer, MessageIdentifier.serializer())
private val ChatIdToMessageSerializer = PairSerializer(FullChatIdentifierSerializer, MessageId.serializer())
fun PostsMessages(
database: Database,
json: Json
): KeyValueRepo<PostId, Pair<IdChatIdentifier, MessageIdentifier>> = ExposedKeyValueRepo<String, String>(
): KeyValueRepo<PostId, Pair<IdChatIdentifier, MessageId>> = ExposedKeyValueRepo<String, String>(
database,
{ text("postId") },
{ text("chatToMessage") },

View File

@@ -1,10 +1,7 @@
package dev.inmo.plaguposter.posts.models
import dev.inmo.tgbotapi.extensions.utils.possiblyMediaGroupMessageOrNull
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.FullChatIdentifierSerializer
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import kotlinx.serialization.Serializable
@@ -13,8 +10,8 @@ import kotlinx.serialization.Serializable
data class PostContentInfo(
@Serializable(FullChatIdentifierSerializer::class)
val chatId: IdChatIdentifier,
val messageId: MessageIdentifier,
val group: String?,
val messageId: MessageId,
val group: MediaGroupId?,
val order: Int
) {
companion object {

View File

@@ -5,10 +5,10 @@ import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.plaguposter.posts.models.*
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.MessageId
interface ReadPostsRepo : ReadCRUDRepo<RegisteredPost, PostId> {
suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageIdentifier): PostId?
suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageId): PostId?
suspend fun getPostCreationTime(postId: PostId): DateTime?
suspend fun getFirstMessageInfo(postId: PostId): PostContentInfo?
}

View File

@@ -40,13 +40,13 @@ class PostPublisher(
contents.singleOrNull() ?.also {
targetChatIds.forEach { targetChatId ->
runCatching {
bot.copyMessage(targetChatId, it.chatId, it.messageId)
bot.copyMessage(fromChatId = it.chatId, messageId = it.messageId, toChatId = targetChatId)
}.onFailure { _ ->
runCatching {
bot.forwardMessage(
fromChatId = it.chatId,
toChatId = cachingChatId,
messageId = it.messageId
messageId = it.messageId,
toChatId = cachingChatId
)
}.onSuccess {
bot.copyMessage(targetChatId, it)

View File

@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithBinds
import dev.inmo.micro_utils.repos.deleteById
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.database
import dev.inmo.plaguposter.common.SuccessfulSymbol
import dev.inmo.plaguposter.common.UnsuccessfulSymbol
import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo
@@ -23,10 +24,9 @@ import dev.inmo.tgbotapi.extensions.api.edit.edit
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 dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
@@ -37,8 +37,8 @@ object Plugin : Plugin {
val autoRemoveMessages: Boolean = true,
val deleteAfterPublishing: Boolean = true
)
override fun Module.setupDI(database: Database, params: JsonObject) {
val configJson = params["posts"] ?: this@Plugin.let {
override fun Module.setupDI(config: JsonObject) {
val configJson = config["posts"] ?: this@Plugin.let {
it.logger.w {
"Unable to load posts plugin due to absence of `posts` key in config"
}
@@ -58,7 +58,7 @@ object Plugin : Plugin {
}
single {
val config = get<Config>()
PostPublisher(get(), get(), config.chats.cacheChatId, config.chats.allTargetChatIds, config.deleteAfterPublishing)
PostPublisher(get(), get(), config.chats.cacheChatId, config.chats.allTargetChatIds.toList(), config.deleteAfterPublishing)
}
}
@@ -90,9 +90,9 @@ object Plugin : Plugin {
postsRepo.deleteById(postId)
if (postsRepo.contains(postId)) {
edit(it, it.content.textSources + regular(UnsuccessfulSymbol))
edit(it, it.content.textSources + regularTextSource(UnsuccessfulSymbol))
} else {
edit(it, it.content.textSources + regular(SuccessfulSymbol))
edit(it, it.content.textSources + regularTextSource(SuccessfulSymbol))
}
}

View File

@@ -16,7 +16,7 @@ import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.models.RegisteredPost
import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.MessageId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -34,7 +34,7 @@ class CachedPostsRepo(
) {
override val removedPostsFlow: Flow<RegisteredPost> by parentRepo::removedPostsFlow
override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageIdentifier): PostId? {
override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageId): PostId? {
doForAllWithNextPaging(firstPageWithOneElementPagination) {
kvCache.values(it).also {
it.results.forEach {

View File

@@ -4,8 +4,7 @@ import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.repos.KeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.plaguposter.posts.models.*
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.*
import org.jetbrains.exposed.sql.*
internal class ExposedContentInfoRepo(
@@ -21,9 +20,9 @@ internal class ExposedContentInfoRepo(
val ResultRow.asObject
get() = PostContentInfo(
IdChatIdentifier(get(chatIdColumn), get(threadIdColumn)),
get(messageIdColumn),
get(groupColumn),
IdChatIdentifier(RawChatId(get(chatIdColumn)), get(threadIdColumn) ?.let(::MessageThreadId)),
MessageId(get(messageIdColumn)),
get(groupColumn) ?.let(::MediaGroupId),
get(orderColumn)
)

View File

@@ -10,7 +10,7 @@ import dev.inmo.plaguposter.posts.models.*
import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.MessageId
import kotlinx.coroutines.flow.*
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.*
@@ -48,7 +48,7 @@ class ExposedPostsRepo(
id,
DateTime(get(createdColumn)),
with(contentRepo) {
select { postIdColumn.eq(id.string) }.map {
selectAll().where { postIdColumn.eq(id.string) }.map {
it.asObject
}
}
@@ -69,14 +69,14 @@ class ExposedPostsRepo(
id,
DateTime(get(createdColumn)),
with(contentRepo) {
select { postIdColumn.eq(id.string) }.map {
selectAll().where { postIdColumn.eq(id.string) }.map {
it.asObject
}
}
)
}
override fun createAndInsertId(value: NewPost, it: InsertStatement<Number>): PostId {
override fun createAndInsertId(value: NewPost, it: UpdateBuilder<Int>): PostId {
val id = PostId(uuid4().toString())
it[idColumn] = id.string
return id
@@ -93,10 +93,10 @@ class ExposedPostsRepo(
post.content.forEach { contentInfo ->
insert {
it[postIdColumn] = post.id.string
it[chatIdColumn] = contentInfo.chatId.chatId
it[threadIdColumn] = contentInfo.chatId.threadId
it[messageIdColumn] = contentInfo.messageId
it[groupColumn] = contentInfo.group
it[chatIdColumn] = contentInfo.chatId.chatId.long
it[threadIdColumn] = contentInfo.chatId.threadId ?.long
it[messageIdColumn] = contentInfo.messageId.long
it[groupColumn] = contentInfo.group ?.string
it[orderColumn] = contentInfo.order
}
}
@@ -104,7 +104,7 @@ class ExposedPostsRepo(
}
}
override fun insert(value: NewPost, it: InsertStatement<Number>) {
override fun insert(value: NewPost, it: UpdateBuilder<Int>) {
super.insert(value, it)
it[createdColumn] = DateTime.now().unixMillis
}
@@ -132,7 +132,7 @@ class ExposedPostsRepo(
}.associateBy { it.id }
val existsIds = posts.keys.toList()
transaction(db = database) {
val deleted = deleteWhere(null, null) {
val deleted = deleteWhere {
selectByIds(it, existsIds)
}
with(contentRepo) {
@@ -144,7 +144,7 @@ class ExposedPostsRepo(
existsIds
} else {
existsIds.filter {
select { selectById(it) }.limit(1).none()
selectAll().where { selectById(it) }.limit(1).none()
}
}
}.forEach {
@@ -153,25 +153,25 @@ class ExposedPostsRepo(
}
}
override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageIdentifier): PostId? {
override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageId): PostId? {
return transaction(database) {
with(contentRepo) {
select {
chatIdColumn.eq(chatId.chatId)
.and(chatId.threadId ?.let { threadIdColumn.eq(it) } ?: threadIdColumn.isNull())
.and(messageIdColumn.eq(messageId))
selectAll().where {
chatIdColumn.eq(chatId.chatId.long)
.and(chatId.threadId ?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull())
.and(messageIdColumn.eq(messageId.long))
}.limit(1).firstOrNull() ?.get(postIdColumn)
} ?.let(::PostId)
}
}
override suspend fun getPostCreationTime(postId: PostId): DateTime? = transaction(database) {
select { selectById(postId) }.limit(1).firstOrNull() ?.get(createdColumn) ?.let(::DateTime)
selectAll().where { selectById(postId) }.limit(1).firstOrNull() ?.get(createdColumn) ?.let(::DateTime)
}
override suspend fun getFirstMessageInfo(postId: PostId): PostContentInfo? = transaction(database) {
with(contentRepo) {
select { postIdColumn.eq(postId.string) }.limit(1).firstOrNull() ?.asObject
selectAll().where { postIdColumn.eq(postId.string) }.limit(1).firstOrNull() ?.asObject
}
}
}

View File

@@ -119,12 +119,12 @@ object Plugin : Plugin {
null
}
onCommand("start_post", initialFilter = { it.sameChat(config.sourceChatId) }) {
onCommand("start_post", initialFilter = { config.allSourceChatIds.any { chatId -> it.sameChat(chatId) } }) {
startChain(RegistrationState.InProcess(it.chat.id, emptyList()))
}
onContentMessage(
initialFilter = { it.sameChat(config.sourceChatId) && !FirstSourceIsCommandsFilter(it) }
initialFilter = { config.allSourceChatIds.any { chatId -> it.sameChat(chatId) } && !FirstSourceIsCommandsFilter(it) }
) {
startChain(RegistrationState.Finish(it.chat.id, PostContentInfo.fromMessage(it)))
}

View File

@@ -1,3 +1,4 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
@@ -19,46 +20,37 @@ publishing {
}
developers {
developer {
id = "InsanusMokrassar"
name = "Aleksei Ovsiannikov"
email = "ovsyannikov.alexey95@gmail.com"
}
developer {
id = "InsanusMokrassar"
name = "Aleksei Ovsiannikov"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
}
}
repositories {
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven {
name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
repositories {
if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven {
name = "InmoNexus"
url = uri("https://nexus.inmo.dev/repository/maven-releases/")
credentials {
username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
}
}
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('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://ossrh-staging-api.central.sonatype.com/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')
}
}
}
@@ -67,13 +59,13 @@ publishing {
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)

View File

@@ -1 +1 @@
{"licenses":[],"mavenConfig":{"name":"${project.name}","description":"${project.name}","url":"https://github.com/InsanusMokrassar/PlaguPoster","vcsUrl":"https://github.com/InsanusMokrassar/PlaguPoster.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
{"licenses":[],"mavenConfig":{"name":"${project.name}","description":"${project.name}","url":"https://github.com/InsanusMokrassar/PlaguPoster","vcsUrl":"https://github.com/InsanusMokrassar/PlaguPoster.git","developers":[{"id":"InsanusMokrassar","name":"Aleksei Ovsiannikov","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -11,6 +11,7 @@ kotlin {
dependencies {
api project(":plaguposter.common")
api project(":plaguposter.posts")
api libs.krontab
}
}
}

View File

@@ -5,15 +5,23 @@ import korlibs.time.seconds
import dev.inmo.krontab.KrontabTemplate
import dev.inmo.krontab.toSchedule
import dev.inmo.krontab.utils.asFlowWithDelays
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.i
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithRandomQualifier
import dev.inmo.micro_utils.repos.*
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.plugins.inline.queries.models.Format
import dev.inmo.plagubot.plugins.inline.queries.models.OfferTemplate
import dev.inmo.plagubot.plugins.inline.queries.repos.InlineTemplatesRepo
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.plaguposter.ratings.models.Rating
import dev.inmo.plaguposter.ratings.repo.RatingsRepo
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.types.Seconds
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
@@ -34,8 +42,8 @@ object Plugin : Plugin {
val skipPostAge: Seconds? = null
)
}
override fun Module.setupDI(database: Database, params: JsonObject) {
single { get<Json>().decodeFromJsonElement(Config.serializer(), params["gc"] ?: return@single null) }
override fun Module.setupDI(params: JsonObject) {
registerConfig<Config>("gc") { null }
}
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
@@ -59,12 +67,23 @@ object Plugin : Plugin {
}
}
config.autoclear ?.let { autoclear ->
val autoClearLogger = KSLog("autoclear")
suspend fun doAutoClear() {
autoClearLogger.i { "Start autoclear" }
val dropCreatedBefore = DateTime.now() - (autoclear.skipPostAge ?: 0).seconds
ratingsRepo.getPostsWithRatingLessEq(autoclear.rating).keys.forEach {
if ((postsRepo.getPostCreationTime(it) ?: return@forEach) < dropCreatedBefore) {
postsRepo.deleteById(it)
}
autoClearLogger.i { "Posts drop created before: ${dropCreatedBefore.toStringDefault()}" }
val idsToDelete = ratingsRepo.getPostsWithRatingLessEq(autoclear.rating).keys.also {
autoClearLogger.i { "Selected posts by rating: $it" }
}.filter {
val postCreationDateTime = postsRepo.getPostCreationTime(it) ?: (dropCreatedBefore - 1.seconds) // do dropping if post creation time is not available
postCreationDateTime < dropCreatedBefore
}
autoClearLogger.i { "Filtered posts by datetime: $idsToDelete" }
if (idsToDelete.isNotEmpty()) {
runCatching { ratingsRepo.unset(idsToDelete) }
autoClearLogger.i { "Ratings dropped" }
runCatching { postsRepo.deleteById(idsToDelete) }
autoClearLogger.i { "Posts dropped" }
}
}
runCatchingSafely {
@@ -73,6 +92,13 @@ object Plugin : Plugin {
autoclear.autoClearKrontab.toSchedule().asFlowWithDelays().subscribeSafelyWithoutExceptions(scope) {
doAutoClear()
}
onCommand("clean_posts_by_ratings") {
doAutoClear()
}
koin.getOrNull<InlineTemplatesRepo>() ?.addTemplate(
OfferTemplate("Force autoclear", listOf(Format("/clean_posts_by_ratings")))
)
}
}
}

View File

@@ -4,17 +4,17 @@ import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.ratings.selector.models.SelectorConfig
import korlibs.time.DateTime
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.module.Module
import org.koin.core.qualifier.qualifier
object Plugin : Plugin {
override fun Module.setupDI(database: Database, params: JsonObject) {
single { get<Json>().decodeFromJsonElement(SelectorConfig.serializer(), params["selector"] ?: return@single null) }
override fun Module.setupDI(config: JsonObject) {
registerConfig<SelectorConfig>("selector") { null }
single<KeyValueRepo<PostId, DateTime>>(qualifier("latestChosenRepo")) {
ExposedKeyValueRepo(
get(),

View File

@@ -122,7 +122,7 @@ suspend fun BehaviourContext.includeRootNavigationButtonsHandler(
edit(
it.message,
onPageUpdate(SimplePagination(page, size), args.drop(3).toTypedArray()) ?: return@runCatchingSafely
replyMarkup = onPageUpdate(SimplePagination(page, size), args.drop(3).toTypedArray()) ?: return@runCatchingSafely
)
}
@@ -155,7 +155,7 @@ suspend fun BehaviourContext.includeRootNavigationButtonsHandler(
if (prefix == RootButtonsShowRatingData) {
runCatchingSafely {
val rating = ratingRaw.toDoubleOrNull() ?: return@runCatchingSafely
edit(it.message, ratingsRepo.buildRatingButtons(postsRepo, Rating(rating)))
edit(it.message, replyMarkup = ratingsRepo.buildRatingButtons(postsRepo, Rating(rating)))
}
answer(it)

View File

@@ -1,14 +1,15 @@
package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.full.fullyCached
import dev.inmo.plaguposter.common.ShortMessageInfo
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.PollId
import kotlinx.coroutines.CoroutineScope
class CachedPollsToMessagesInfoRepo(
private val repo: PollsToMessagesInfoRepo,
private val scope: CoroutineScope,
private val kvCache: FullKVCache<PollIdentifier, ShortMessageInfo> = FullKVCache()
) : PollsToMessagesInfoRepo, KeyValueRepo<PollIdentifier, ShortMessageInfo> by repo.fullyCached(kvCache, scope)
private val kvCache: KeyValueRepo<PollId, ShortMessageInfo> = MapKeyValueRepo()
) : PollsToMessagesInfoRepo, KeyValueRepo<PollId, ShortMessageInfo> by repo.fullyCached(kvCache, scope)

View File

@@ -1,14 +1,15 @@
package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.cache.full.fullyCached
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.PollId
import kotlinx.coroutines.CoroutineScope
class CachedPollsToPostsIdsRepo(
private val repo: PollsToPostsIdsRepo,
private val scope: CoroutineScope,
private val kvCache: FullKVCache<PollIdentifier, PostId> = FullKVCache()
) : PollsToPostsIdsRepo, KeyValueRepo<PollIdentifier, PostId> by repo.fullyCached(kvCache, scope)
private val kvCache: KeyValueRepo<PollId, PostId> = MapKeyValueRepo()
) : PollsToPostsIdsRepo, KeyValueRepo<PollId, PostId> by repo.fullyCached(kvCache, scope)

View File

@@ -3,6 +3,6 @@ package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.plaguposter.common.ShortMessageInfo
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.PollId
interface PollsToMessagesInfoRepo : KeyValueRepo<PollIdentifier, ShortMessageInfo>
interface PollsToMessagesInfoRepo : KeyValueRepo<PollId, ShortMessageInfo>

View File

@@ -2,6 +2,6 @@ package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.PollId
interface PollsToPostsIdsRepo : KeyValueRepo<PollIdentifier, PostId>
interface PollsToPostsIdsRepo : KeyValueRepo<PollId, PostId>

View File

@@ -3,6 +3,7 @@ package dev.inmo.plaguposter.ratings.source
import com.benasher44.uuid.uuid4
import dev.inmo.kslog.common.e
import dev.inmo.kslog.common.logger
import dev.inmo.micro_utils.coroutines.runCatchingLogging
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
@@ -10,10 +11,12 @@ import dev.inmo.micro_utils.repos.id
import dev.inmo.micro_utils.repos.pagination.getAll
import dev.inmo.micro_utils.repos.set
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.database
import dev.inmo.plaguposter.common.*
import dev.inmo.plagubot.plugins.inline.queries.models.Format
import dev.inmo.plagubot.plugins.inline.queries.models.OfferTemplate
import dev.inmo.plagubot.plugins.inline.queries.repos.InlineTemplatesRepo
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder
import dev.inmo.plaguposter.posts.panel.PanelButtonsAPI
@@ -28,6 +31,7 @@ import dev.inmo.plaguposter.ratings.utils.postsByRatings
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.delete
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
@@ -37,10 +41,15 @@ import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.message.textsources.bold
import dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.message.textsources.boldTextSource
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
import dev.inmo.tgbotapi.types.polls.InputPollOption
import dev.inmo.tgbotapi.types.polls.PollOption
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.extensions.makeSourceString
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
@@ -62,10 +71,8 @@ object Plugin : Plugin {
val panelButtonText: String = "Ratings"
)
override fun Module.setupDI(database: Database, params: JsonObject) {
single {
get<Json>().decodeFromJsonElement(Config.serializer(), params["ratingsPolls"] ?: error("Unable to load config for rating polls in $params"))
}
override fun Module.setupDI(params: JsonObject) {
registerConfig<Config>("ratingsPolls")
single<RatingsVariants>(ratingVariantsQualifier) { get<Config>().variants }
single { ExposedPollsToPostsIdsRepo(database) }
@@ -111,7 +118,7 @@ object Plugin : Plugin {
onPollUpdates (markerFactory = { it.id }) { poll ->
val postId = pollsToPostsIdsRepo.get(poll.id) ?: return@onPollUpdates
val newRating = poll.options.sumOf {
(variantsTransformer(it.text) ?.double ?.times(it.votes)) ?: 0.0
(variantsTransformer(it.textSources.makeSourceString()) ?.double ?.times(it.votes)) ?: 0.0
}
ratingsRepo.set(postId, Rating(newRating))
}
@@ -128,8 +135,10 @@ object Plugin : Plugin {
val sent = send(
content.chatId,
config.ratingOfferText,
config.variants.keys.toList(),
replyToMessageId = content.messageId
options = config.variants.map {
InputPollOption(it.key)
},
replyParameters = ReplyParameters(content.chatId, content.messageId)
)
pollsToPostsIdsRepo.set(sent.content.poll.id, postId)
pollsToMessageInfoRepo.set(sent.content.poll.id, sent.short())
@@ -191,17 +200,17 @@ object Plugin : Plugin {
}
if (attachPoll(postId)) {
runCatchingSafely {
runCatchingLogging {
edit(
it,
it.content.textSources + regular(" $SuccessfulSymbol")
it.content.textSources + regularTextSource(" $SuccessfulSymbol")
)
}
} else {
runCatchingSafely {
runCatchingLogging {
edit(
it,
it.content.textSources + regular(" $UnsuccessfulSymbol")
it.content.textSources + regularTextSource(" $UnsuccessfulSymbol")
)
}
}
@@ -226,23 +235,23 @@ object Plugin : Plugin {
if (detachPoll(postId)) {
runCatchingSafely {
runCatchingLogging {
edit(
it,
it.content.textSources + regular(" $SuccessfulSymbol")
it.content.textSources + regularTextSource(" $SuccessfulSymbol")
)
}
} else {
runCatchingSafely {
runCatchingLogging {
edit(
it,
it.content.textSources + regular(" $UnsuccessfulSymbol")
it.content.textSources + regularTextSource(" $UnsuccessfulSymbol")
)
}
}
}
onCommand("ratings", requireOnlyCommandInMessage = true) {
if (it.chat.id == chatConfig.sourceChatId) {
if (it.chat.id in chatConfig.allSourceChatIds) {
val ratings = ratingsRepo.postsByRatings().toList().sortedByDescending { it.first }
val textSources = buildEntities {
+ "Ratings amount: " + bold("${ratings.sumOf { it.second.size }}") + "\n\n"
@@ -260,11 +269,11 @@ object Plugin : Plugin {
}
}
}
includeRootNavigationButtonsHandler(setOf(chatConfig.sourceChatId), ratingsRepo, postsRepo)
onMessageDataCallbackQuery("ratings_interactive", initialFilter = { it.message.chat.id == chatConfig.sourceChatId }) {
includeRootNavigationButtonsHandler(chatConfig.allSourceChatIds, ratingsRepo, postsRepo)
onMessageDataCallbackQuery("ratings_interactive", initialFilter = { it.message.chat.id in chatConfig.allSourceChatIds }) {
edit(
it.message,
ratingsRepo.buildRootButtons()
replyMarkup = ratingsRepo.buildRootButtons()
)
}

View File

@@ -3,9 +3,7 @@ package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
import dev.inmo.plaguposter.common.ShortMessageInfo
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull
@@ -13,7 +11,7 @@ import org.jetbrains.exposed.sql.statements.*
class ExposedPollsToMessagesInfoRepo(
database: Database
) : PollsToMessagesInfoRepo, AbstractExposedKeyValueRepo<PollIdentifier, ShortMessageInfo>(
) : PollsToMessagesInfoRepo, AbstractExposedKeyValueRepo<PollId, ShortMessageInfo>(
database,
"polls_to_their_messages_info"
) {
@@ -21,31 +19,32 @@ class ExposedPollsToMessagesInfoRepo(
private val chatIdColumn = long("chat_id")
private val threadIdColumn = long("thread_id").nullable().default(null)
private val messageIdColumn = long("message_id")
override val selectById: ISqlExpressionBuilder.(PollIdentifier) -> Op<Boolean> = { keyColumn.eq(it) }
override val selectById: ISqlExpressionBuilder.(PollId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(ShortMessageInfo) -> Op<Boolean> = {
chatIdColumn.eq(it.chatId.chatId).and(it.chatId.threadId ?.let { threadIdColumn.eq(it) } ?: threadIdColumn.isNull()).and(
messageIdColumn.eq(it.messageId)
chatIdColumn.eq(it.chatId.chatId.long)
.and(it.chatId.threadId?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull()).and(
messageIdColumn.eq(it.messageId.long)
)
}
override val ResultRow.asKey: PollIdentifier
get() = get(keyColumn)
override val ResultRow.asKey: PollId
get() = PollId(get(keyColumn))
override val ResultRow.asObject: ShortMessageInfo
get() = ShortMessageInfo(
IdChatIdentifier(get(chatIdColumn), get(threadIdColumn)),
get(messageIdColumn)
IdChatIdentifier(RawChatId(get(chatIdColumn)), get(threadIdColumn) ?.let(::MessageThreadId)),
MessageId(get(messageIdColumn))
)
init {
initTable()
}
override fun update(k: PollIdentifier, v: ShortMessageInfo, it: UpdateBuilder<Int>) {
it[chatIdColumn] = v.chatId.chatId
it[threadIdColumn] = v.chatId.threadId
it[messageIdColumn] = v.messageId
override fun update(k: PollId, v: ShortMessageInfo, it: UpdateBuilder<Int>) {
it[chatIdColumn] = v.chatId.chatId.long
it[threadIdColumn] = v.chatId.threadId ?.long
it[messageIdColumn] = v.messageId.long
}
override fun insertKey(k: PollIdentifier, v: ShortMessageInfo, it: InsertStatement<Number>) {
it[keyColumn] = k
override fun insertKey(k: PollId, v: ShortMessageInfo, it: UpdateBuilder<Int>) {
it[keyColumn] = k.string
}
}

View File

@@ -3,19 +3,19 @@ package dev.inmo.plaguposter.ratings.source.repos
import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.PollId
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.*
class ExposedPollsToPostsIdsRepo(
database: Database
) : PollsToPostsIdsRepo, AbstractExposedKeyValueRepo<PollIdentifier, PostId>(database, "polls_to_posts") {
) : PollsToPostsIdsRepo, AbstractExposedKeyValueRepo<PollId, PostId>(database, "polls_to_posts") {
override val keyColumn = text("poll_id")
val postIdColumn = text("postId")
override val selectById: ISqlExpressionBuilder.(PollIdentifier) -> Op<Boolean> = { keyColumn.eq(it) }
override val selectById: ISqlExpressionBuilder.(PollId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { postIdColumn.eq(it.string) }
override val ResultRow.asKey: PollIdentifier
get() = get(keyColumn)
override val ResultRow.asKey: PollId
get() = PollId(get(keyColumn))
override val ResultRow.asObject: PostId
get() = get(postIdColumn).let(::PostId)
@@ -23,11 +23,11 @@ class ExposedPollsToPostsIdsRepo(
initTable()
}
override fun update(k: PollIdentifier, v: PostId, it: UpdateBuilder<Int>) {
override fun update(k: PollId, v: PostId, it: UpdateBuilder<Int>) {
it[postIdColumn] = v.string
}
override fun insertKey(k: PollIdentifier, v: PostId, it: InsertStatement<Number>) {
it[keyColumn] = k
override fun insertKey(k: PollId, v: PostId, it: UpdateBuilder<Int>) {
it[keyColumn] = k.string
}
}

View File

@@ -0,0 +1,18 @@
package dev.inmo.plaguposter.ratings.models
import dev.inmo.krontab.EveryHourScheduler
import dev.inmo.krontab.KrontabTemplate
import dev.inmo.krontab.buildSchedule
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable
internal data class RatingsConfig(
@SerialName("manualRecheckKrontab")
val manualRecheckKrontabTemplate: KrontabTemplate = "0 /30 *"
) {
@Transient
val manualRecheckKrontab
get() = buildSchedule(manualRecheckKrontabTemplate)
}

View File

@@ -1,8 +1,10 @@
package dev.inmo.plaguposter.ratings.repo
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging
import dev.inmo.micro_utils.pagination.utils.optionallyReverse
import dev.inmo.micro_utils.pagination.utils.paginate
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
import dev.inmo.micro_utils.repos.MapKeyValueRepo
import dev.inmo.micro_utils.repos.cache.full.FullKeyValueCacheRepo
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.ratings.models.Rating
@@ -11,28 +13,32 @@ import kotlinx.coroutines.CoroutineScope
class CachedRatingsRepo(
private val base: RatingsRepo,
private val scope: CoroutineScope,
private val kvCache: FullKVCache<PostId, Rating> = FullKVCache()
private val kvCache: MapKeyValueRepo<PostId, Rating> = MapKeyValueRepo()
) : RatingsRepo, KeyValueRepo<PostId, Rating> by FullKeyValueCacheRepo(base, kvCache, scope) {
private suspend fun getPosts(
reversed: Boolean,
count: Int?,
exclude: List<PostId>,
ratingFilter: (Rating) -> Boolean
): Map<PostId, Rating> {
return kvCache.getAll().filter { (it, rating) ->
it !in exclude && ratingFilter(rating)
}.let {
if (count == null) {
it
} else {
val keys = it.keys.optionallyReverse(reversed).take(count)
keys.associateWith { id -> it.getValue(id) }
}
}
}
override suspend fun getPosts(
range: ClosedRange<Rating>,
reversed: Boolean,
count: Int?,
exclude: List<PostId>
): Map<PostId, Rating> {
val result = mutableMapOf<PostId, Rating>()
doForAllWithNextPaging {
kvCache.keys(it).also {
it.results.forEach {
val rating = get(it) ?: return@forEach
if (it !in exclude && rating in range) {
result[it] = rating
}
}
}
}
return result.toMap()
): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
it in range
}
override suspend fun getPostsWithRatingGreaterEq(
@@ -40,22 +46,16 @@ class CachedRatingsRepo(
reversed: Boolean,
count: Int?,
exclude: List<PostId>
): Map<PostId, Rating> = getPosts(
then .. Rating(Double.MAX_VALUE),
reversed,
count,
exclude
)
): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
it >= then
}
override suspend fun getPostsWithRatingLessEq(
then: Rating,
reversed: Boolean,
count: Int?,
exclude: List<PostId>
): Map<PostId, Rating> = getPosts(
Rating(Double.MIN_VALUE) .. then,
reversed,
count,
exclude
)
): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
it <= then
}
}

View File

@@ -1,23 +1,30 @@
package dev.inmo.plaguposter.ratings
import dev.inmo.krontab.utils.asTzFlowWithDelays
import dev.inmo.kslog.common.TagLogger
import dev.inmo.kslog.common.i
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithBinds
import dev.inmo.micro_utils.repos.unset
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.config
import dev.inmo.plagubot.database
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.common.useCache
import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo
import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.plaguposter.ratings.Plugin.setupBotPlugin
import dev.inmo.plaguposter.ratings.exposed.ExposedRatingsRepo
import dev.inmo.plaguposter.ratings.models.RatingsConfig
import dev.inmo.plaguposter.ratings.repo.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.dsl.binds
object Plugin : Plugin {
override fun Module.setupDI(database: Database, params: JsonObject) {
private val Log = TagLogger("RatingsPlugin")
override fun Module.setupDI(config: JsonObject) {
single { ExposedRatingsRepo(database) }
singleWithBinds<RatingsRepo> {
val base = get<ExposedRatingsRepo>()
@@ -28,12 +35,24 @@ object Plugin : Plugin {
base
}
}
registerConfig(RatingsConfig.serializer(), "ratings") { RatingsConfig() }
}
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
override suspend fun startPlugin(koin: Koin) {
super.startPlugin(koin)
val config = koin.config<RatingsConfig>()
val scope = koin.get<CoroutineScope>()
val ratingsRepo = koin.get<RatingsRepo>()
koin.get<PostsRepo>().deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(this) {
val postsRepo = koin.get<PostsRepo>()
postsRepo.deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(scope) {
ratingsRepo.unset(it)
}
config.manualRecheckKrontab.asTzFlowWithDelays().subscribeSafelyWithoutExceptions(scope) {
Log.i { "Start clearing ratings without registered posts" }
val postsIdsToRemove = ratingsRepo.getAll().keys - postsRepo.getAll().keys
Log.i { "Posts to remove: $postsIdsToRemove" }
ratingsRepo.unset(postsIdsToRemove.toList())
}
}
}

View File

@@ -33,7 +33,7 @@ class ExposedRatingsRepo (
it[ratingsColumn] = v.double
}
override fun insertKey(k: PostId, v: Rating, it: InsertStatement<Number>) {
override fun insertKey(k: PostId, v: Rating, it: UpdateBuilder<Int>) {
it[keyColumn] = k.string
}
@@ -49,7 +49,7 @@ class ExposedRatingsRepo (
count: Int?,
exclude: List<PostId>
): Map<PostId, Rating> = transaction(database) {
select {
selectAll().where {
ratingsColumn.greaterEq(range.start.double).and(
ratingsColumn.lessEq(range.endInclusive.double)
).and(
@@ -66,7 +66,7 @@ class ExposedRatingsRepo (
count: Int?,
exclude: List<PostId>
) = transaction(database) {
select { ratingsColumn.greaterEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
selectAll().where { ratingsColumn.greaterEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
it.asKey to it.asObject
}
}.toMap()
@@ -77,7 +77,7 @@ class ExposedRatingsRepo (
count: Int?,
exclude: List<PostId>
): Map<PostId, Rating> = transaction(database) {
select { ratingsColumn.lessEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
selectAll().where { ratingsColumn.lessEq(then.double).and(keyColumn.notInList(exclude.map { it.string })) }.optionallyLimit(count).optionallyReverse(reversed).map {
it.asKey to it.asObject
}
}.toMap()

View File

@@ -15,7 +15,7 @@ function assert_success() {
app=plaguposter
version="`grep ../gradle.properties -e "^version=" | sed -e "s/version=\(.*\)/\1/"`"
server=insanusmokrassar
server=docker.inmo.dev
assert_success ../gradlew build
assert_success sudo docker build -t $app:"$version" .

View File

@@ -7,6 +7,7 @@ import dev.inmo.plaguposter.common.SuccessfulSymbol
import dev.inmo.plagubot.plugins.inline.queries.models.Format
import dev.inmo.plagubot.plugins.inline.queries.models.OfferTemplate
import dev.inmo.plagubot.plugins.inline.queries.repos.InlineTemplatesRepo
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder
import dev.inmo.plaguposter.posts.panel.PanelButtonsAPI
@@ -24,9 +25,9 @@ import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
@@ -38,17 +39,15 @@ object Plugin : Plugin {
@Serializable
private data class PublishState(
override val context: ChatId,
val sourceMessageId: MessageIdentifier,
val messageInReply: MessageIdentifier
val sourceMessageId: MessageId,
val messageInReply: MessageId
) : State
@Serializable
internal data class Config(
val panelButtonText: String? = "Publish"
)
override fun Module.setupDI(database: Database, params: JsonObject) {
params["publish_command"] ?.let { configJson ->
single { get<Json>().decodeFromJsonElement(Config.serializer(), configJson) }
}
override fun Module.setupDI(params: JsonObject) {
registerConfig<Config>("publish_command") { null }
}
override suspend fun BehaviourContextWithFSM<State>.setupBotPlugin(koin: Koin) {
@@ -91,7 +90,7 @@ object Plugin : Plugin {
edit(
it,
it.content.textSources + regular(SuccessfulSymbol)
it.content.textSources + regularTextSource(SuccessfulSymbol)
)
}

View File

@@ -16,6 +16,7 @@ import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.plugins.inline.queries.models.Format
import dev.inmo.plagubot.plugins.inline.queries.models.OfferTemplate
import dev.inmo.plagubot.plugins.inline.queries.repos.InlineTemplatesRepo
import dev.inmo.plagubot.registerConfig
import dev.inmo.plaguposter.common.ChatConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.repo.ReadPostsRepo
@@ -62,8 +63,8 @@ object Plugin : Plugin {
@Transient
val format: DateFormat = DateFormat(dateTimeFormat)
}
override fun Module.setupDI(database: Database, params: JsonObject) {
single { get<Json>().decodeFromJsonElement(Config.serializer(), params["timer_trigger"] ?: return@single null) }
override fun Module.setupDI(params: JsonObject) {
registerConfig<Config>("timer_trigger") { null }
}
@OptIn(FlowPreview::class)
@@ -151,7 +152,7 @@ object Plugin : Plugin {
}
}
onCommand("autoschedule_panel", initialFilter = { it.sameChat(chatConfig.sourceChatId) }) {
onCommand("autoschedule_panel", initialFilter = { chatConfig.allSourceChatIds.any { chatId -> it.sameChat(chatId) } }) {
val keyboard = buildPage()
runCatchingSafely {
@@ -167,7 +168,7 @@ object Plugin : Plugin {
onMessageDataCallbackQuery(
Regex("^$pageCallbackDataQueryPrefix\\d+"),
initialFilter = { it.message.sameChat(chatConfig.sourceChatId) }
initialFilter = { chatConfig.allSourceChatIds.any { sourceChatId -> it.message.sameChat(sourceChatId) } }
) {
val page = it.data.removePrefix(pageCallbackDataQueryPrefix).toIntOrNull() ?: let { _ ->
answer(it)

View File

@@ -1,21 +1,15 @@
package dev.inmo.plaguposter.triggers.timer.disablers.autoposts
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithRandomQualifier
import dev.inmo.micro_utils.koin.singleWithRandomQualifierAndBinds
import dev.inmo.micro_utils.pagination.FirstPagePagination
import dev.inmo.micro_utils.repos.unset
import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.ratings.repo.RatingsRepo
import dev.inmo.plaguposter.triggers.selector_with_timer.AutopostFilter
import dev.inmo.plaguposter.triggers.timer.TimersRepo
import kotlinx.coroutines.CoroutineScope
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) {
override fun Module.setupDI(config: JsonObject) {
singleWithRandomQualifier<AutopostFilter> {
val timersRepo = get<TimersRepo>()
AutopostFilter { _, dateTime ->

View File

@@ -8,11 +8,10 @@ import dev.inmo.plaguposter.ratings.repo.RatingsRepo
import dev.inmo.plaguposter.triggers.timer.TimersRepo
import kotlinx.coroutines.CoroutineScope
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) {
override fun Module.setupDI(config: JsonObject) {
singleWithRandomQualifier(createdAtStart = true) {
val timersRepo = get<TimersRepo>()
val ratingsRepo = get<RatingsRepo>()

View File

@@ -7,6 +7,7 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.unset
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.sending.PostPublisher
import korlibs.time.millisecondsLong
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay

View File

@@ -1,7 +1,5 @@
package dev.inmo.plaguposter.triggers.timer
import korlibs.time.DateTime
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithRandomQualifierAndBinds
import dev.inmo.micro_utils.repos.set
@@ -9,24 +7,19 @@ import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.ChatConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.PanelButtonsAPI
import dev.inmo.plaguposter.posts.repo.ReadPostsRepo
import dev.inmo.plaguposter.triggers.timer.repo.ExposedTimersRepo
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.reply
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 kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.dsl.binds
object Plugin : Plugin {
override fun Module.setupDI(database: Database, params: JsonObject) {
override fun Module.setupDI(config: JsonObject) {
single { ExposedTimersRepo(get(), get(), get()) } binds arrayOf(TimersRepo::class)
single(createdAtStart = true) { TimersHandler(get(), get(), get()) }
singleWithRandomQualifierAndBinds { TimerPanelButton(get()) }

View File

@@ -50,7 +50,7 @@ class ExposedTimersRepo(
it[dateTimeColumn] = v.unixMillisLong
}
override fun insertKey(k: PostId, v: DateTime, it: InsertStatement<Number>) {
override fun insertKey(k: PostId, v: DateTime, it: UpdateBuilder<Int>) {
it[keyColumn] = k.string
}