16 Commits

29 changed files with 182 additions and 132 deletions

View File

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

View File

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

View File

@@ -1,5 +1,23 @@
# PlaguPoster # PlaguPoster
## 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 ## 0.5.3
* Dependencies update * Dependencies update

View File

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

View File

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

View File

@@ -1,16 +1,16 @@
[versions] [versions]
kotlin = "1.9.22" kotlin = "1.9.23"
kotlin-serialization = "1.6.2" kotlin-serialization = "1.6.3"
plagubot = "8.1.1" plagubot = "8.4.0"
tgbotapi = "10.0.1" tgbotapi = "15.0.0"
microutils = "0.20.34" microutils = "0.21.1"
kslog = "1.3.2" kslog = "1.3.4"
krontab = "2.2.7" krontab = "2.3.0"
plagubot-plugins = "0.18.1" plagubot-plugins = "0.19.0"
dokka = "1.9.10" dokka = "1.9.20"
psql = "42.6.0" psql = "42.6.0"

View File

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

View File

@@ -4,7 +4,13 @@ project.group = "$group"
apply from: "$publishGradlePath" apply from: "$publishGradlePath"
kotlin { kotlin {
jvm() jvm {
compilations.main {
kotlinOptions {
jvmTarget = "17"
}
}
}
js (IR) { js (IR) {
browser() browser()
nodejs() nodejs()
@@ -38,3 +44,8 @@ kotlin {
} }
} }
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

View File

@@ -30,7 +30,7 @@ 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.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.IdChatIdentifier 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.ReplyParameters
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
@@ -129,7 +129,7 @@ object Plugin : Plugin {
suspend fun refreshPostMessage( suspend fun refreshPostMessage(
postId: PostId, postId: PostId,
chatId: IdChatIdentifier, chatId: IdChatIdentifier,
messageId: MessageIdentifier messageId: MessageId
) { ) {
val post = postsRepo.getById(postId) ?: return val post = postsRepo.getById(postId) ?: return
val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row -> val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row ->

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

View File

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

View File

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

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.models.RegisteredPost
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.tgbotapi.types.IdChatIdentifier 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.CoroutineScope
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -34,7 +34,7 @@ class CachedPostsRepo(
) { ) {
override val removedPostsFlow: Flow<RegisteredPost> by parentRepo::removedPostsFlow 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) { doForAllWithNextPaging(firstPageWithOneElementPagination) {
kvCache.values(it).also { kvCache.values(it).also {
it.results.forEach { 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.KeyValuesRepo
import dev.inmo.micro_utils.repos.exposed.* import dev.inmo.micro_utils.repos.exposed.*
import dev.inmo.plaguposter.posts.models.* import dev.inmo.plaguposter.posts.models.*
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.IdChatIdentifier
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
internal class ExposedContentInfoRepo( internal class ExposedContentInfoRepo(
@@ -21,9 +20,9 @@ internal class ExposedContentInfoRepo(
val ResultRow.asObject val ResultRow.asObject
get() = PostContentInfo( get() = PostContentInfo(
IdChatIdentifier(get(chatIdColumn), get(threadIdColumn)), IdChatIdentifier(RawChatId(get(chatIdColumn)), get(threadIdColumn) ?.let(::MessageThreadId)),
get(messageIdColumn), MessageId(get(messageIdColumn)),
get(groupColumn), get(groupColumn) ?.let(::MediaGroupId),
get(orderColumn) get(orderColumn)
) )

View File

@@ -10,7 +10,7 @@ import dev.inmo.plaguposter.posts.models.*
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.MessageId
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
@@ -93,10 +93,10 @@ class ExposedPostsRepo(
post.content.forEach { contentInfo -> post.content.forEach { contentInfo ->
insert { insert {
it[postIdColumn] = post.id.string it[postIdColumn] = post.id.string
it[chatIdColumn] = contentInfo.chatId.chatId it[chatIdColumn] = contentInfo.chatId.chatId.long
it[threadIdColumn] = contentInfo.chatId.threadId it[threadIdColumn] = contentInfo.chatId.threadId ?.long
it[messageIdColumn] = contentInfo.messageId it[messageIdColumn] = contentInfo.messageId.long
it[groupColumn] = contentInfo.group it[groupColumn] = contentInfo.group ?.string
it[orderColumn] = contentInfo.order it[orderColumn] = contentInfo.order
} }
} }
@@ -153,13 +153,13 @@ class ExposedPostsRepo(
} }
} }
override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageIdentifier): PostId? { override suspend fun getIdByChatAndMessage(chatId: IdChatIdentifier, messageId: MessageId): PostId? {
return transaction(database) { return transaction(database) {
with(contentRepo) { with(contentRepo) {
select { select {
chatIdColumn.eq(chatId.chatId) chatIdColumn.eq(chatId.chatId.long)
.and(chatId.threadId ?.let { threadIdColumn.eq(it) } ?: threadIdColumn.isNull()) .and(chatId.threadId ?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull())
.and(messageIdColumn.eq(messageId)) .and(messageIdColumn.eq(messageId.long))
}.limit(1).firstOrNull() ?.get(postIdColumn) }.limit(1).firstOrNull() ?.get(postIdColumn)
} ?.let(::PostId) } ?.let(::PostId)
} }

View File

@@ -5,15 +5,22 @@ import korlibs.time.seconds
import dev.inmo.krontab.KrontabTemplate import dev.inmo.krontab.KrontabTemplate
import dev.inmo.krontab.toSchedule import dev.inmo.krontab.toSchedule
import dev.inmo.krontab.utils.asFlowWithDelays 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.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.singleWithRandomQualifier
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.plagubot.Plugin 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.plaguposter.posts.models.PostId import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.plaguposter.ratings.models.Rating import dev.inmo.plaguposter.ratings.models.Rating
import dev.inmo.plaguposter.ratings.repo.RatingsRepo import dev.inmo.plaguposter.ratings.repo.RatingsRepo
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext 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 dev.inmo.tgbotapi.types.Seconds
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
@@ -59,12 +66,23 @@ object Plugin : Plugin {
} }
} }
config.autoclear ?.let { autoclear -> config.autoclear ?.let { autoclear ->
val autoClearLogger = KSLog("autoclear")
suspend fun doAutoClear() { suspend fun doAutoClear() {
autoClearLogger.i { "Start autoclear" }
val dropCreatedBefore = DateTime.now() - (autoclear.skipPostAge ?: 0).seconds val dropCreatedBefore = DateTime.now() - (autoclear.skipPostAge ?: 0).seconds
ratingsRepo.getPostsWithRatingLessEq(autoclear.rating).keys.forEach { autoClearLogger.i { "Posts drop created before: ${dropCreatedBefore.toStringDefault()}" }
if ((postsRepo.getPostCreationTime(it) ?: return@forEach) < dropCreatedBefore) { val idsToDelete = ratingsRepo.getPostsWithRatingLessEq(autoclear.rating).keys.also {
postsRepo.deleteById(it) 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 { runCatchingSafely {
@@ -73,6 +91,13 @@ object Plugin : Plugin {
autoclear.autoClearKrontab.toSchedule().asFlowWithDelays().subscribeSafelyWithoutExceptions(scope) { autoclear.autoClearKrontab.toSchedule().asFlowWithDelays().subscribeSafelyWithoutExceptions(scope) {
doAutoClear() doAutoClear()
} }
onCommand("clean_posts_by_ratings") {
doAutoClear()
}
koin.getOrNull<InlineTemplatesRepo>() ?.addTemplate(
OfferTemplate("Force autoclear", listOf(Format("/clean_posts_by_ratings")))
)
} }
} }
} }

View File

@@ -122,7 +122,7 @@ suspend fun BehaviourContext.includeRootNavigationButtonsHandler(
edit( edit(
it.message, 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) { if (prefix == RootButtonsShowRatingData) {
runCatchingSafely { runCatchingSafely {
val rating = ratingRaw.toDoubleOrNull() ?: return@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) answer(it)

View File

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

View File

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

@@ -28,6 +28,7 @@ import dev.inmo.plaguposter.ratings.utils.postsByRatings
import dev.inmo.tgbotapi.extensions.api.answers.answer import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.delete import dev.inmo.tgbotapi.extensions.api.delete
import dev.inmo.tgbotapi.extensions.api.edit.edit 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.reply
import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
@@ -41,7 +42,10 @@ import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.message.textsources.bold import dev.inmo.tgbotapi.types.message.textsources.bold
import dev.inmo.tgbotapi.types.message.textsources.regular import dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.polls.InputPollOption
import dev.inmo.tgbotapi.types.polls.PollOption
import dev.inmo.tgbotapi.utils.buildEntities import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.extensions.makeSourceString
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -112,7 +116,7 @@ object Plugin : Plugin {
onPollUpdates (markerFactory = { it.id }) { poll -> onPollUpdates (markerFactory = { it.id }) { poll ->
val postId = pollsToPostsIdsRepo.get(poll.id) ?: return@onPollUpdates val postId = pollsToPostsIdsRepo.get(poll.id) ?: return@onPollUpdates
val newRating = poll.options.sumOf { 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)) ratingsRepo.set(postId, Rating(newRating))
} }
@@ -129,7 +133,9 @@ object Plugin : Plugin {
val sent = send( val sent = send(
content.chatId, content.chatId,
config.ratingOfferText, config.ratingOfferText,
config.variants.keys.toList(), options = config.variants.map {
InputPollOption(it.key)
},
replyParameters = ReplyParameters(content.chatId, content.messageId) replyParameters = ReplyParameters(content.chatId, content.messageId)
) )
pollsToPostsIdsRepo.set(sent.content.poll.id, postId) pollsToPostsIdsRepo.set(sent.content.poll.id, postId)
@@ -265,7 +271,7 @@ object Plugin : Plugin {
onMessageDataCallbackQuery("ratings_interactive", initialFilter = { it.message.chat.id in chatConfig.allSourceChatIds }) { onMessageDataCallbackQuery("ratings_interactive", initialFilter = { it.message.chat.id in chatConfig.allSourceChatIds }) {
edit( edit(
it.message, 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.initTable
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
import dev.inmo.plaguposter.common.ShortMessageInfo import dev.inmo.plaguposter.common.ShortMessageInfo
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.PollIdentifier
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull
@@ -13,7 +11,7 @@ import org.jetbrains.exposed.sql.statements.*
class ExposedPollsToMessagesInfoRepo( class ExposedPollsToMessagesInfoRepo(
database: Database database: Database
) : PollsToMessagesInfoRepo, AbstractExposedKeyValueRepo<PollIdentifier, ShortMessageInfo>( ) : PollsToMessagesInfoRepo, AbstractExposedKeyValueRepo<PollId, ShortMessageInfo>(
database, database,
"polls_to_their_messages_info" "polls_to_their_messages_info"
) { ) {
@@ -21,31 +19,32 @@ class ExposedPollsToMessagesInfoRepo(
private val chatIdColumn = long("chat_id") private val chatIdColumn = long("chat_id")
private val threadIdColumn = long("thread_id").nullable().default(null) private val threadIdColumn = long("thread_id").nullable().default(null)
private val messageIdColumn = long("message_id") 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> = { override val selectByValue: ISqlExpressionBuilder.(ShortMessageInfo) -> Op<Boolean> = {
chatIdColumn.eq(it.chatId.chatId).and(it.chatId.threadId ?.let { threadIdColumn.eq(it) } ?: threadIdColumn.isNull()).and( chatIdColumn.eq(it.chatId.chatId.long)
messageIdColumn.eq(it.messageId) .and(it.chatId.threadId?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull()).and(
messageIdColumn.eq(it.messageId.long)
) )
} }
override val ResultRow.asKey: PollIdentifier override val ResultRow.asKey: PollId
get() = get(keyColumn) get() = PollId(get(keyColumn))
override val ResultRow.asObject: ShortMessageInfo override val ResultRow.asObject: ShortMessageInfo
get() = ShortMessageInfo( get() = ShortMessageInfo(
IdChatIdentifier(get(chatIdColumn), get(threadIdColumn)), IdChatIdentifier(RawChatId(get(chatIdColumn)), get(threadIdColumn) ?.let(::MessageThreadId)),
get(messageIdColumn) MessageId(get(messageIdColumn))
) )
init { init {
initTable() initTable()
} }
override fun update(k: PollIdentifier, v: ShortMessageInfo, it: UpdateBuilder<Int>) { override fun update(k: PollId, v: ShortMessageInfo, it: UpdateBuilder<Int>) {
it[chatIdColumn] = v.chatId.chatId it[chatIdColumn] = v.chatId.chatId.long
it[threadIdColumn] = v.chatId.threadId it[threadIdColumn] = v.chatId.threadId ?.long
it[messageIdColumn] = v.messageId it[messageIdColumn] = v.messageId.long
} }
override fun insertKey(k: PollIdentifier, v: ShortMessageInfo, it: InsertStatement<Number>) { override fun insertKey(k: PollId, v: ShortMessageInfo, it: InsertStatement<Number>) {
it[keyColumn] = k 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.initTable
import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo import dev.inmo.micro_utils.repos.exposed.keyvalue.AbstractExposedKeyValueRepo
import dev.inmo.plaguposter.posts.models.PostId 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.*
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.sql.statements.*
class ExposedPollsToPostsIdsRepo( class ExposedPollsToPostsIdsRepo(
database: Database database: Database
) : PollsToPostsIdsRepo, AbstractExposedKeyValueRepo<PollIdentifier, PostId>(database, "polls_to_posts") { ) : PollsToPostsIdsRepo, AbstractExposedKeyValueRepo<PollId, PostId>(database, "polls_to_posts") {
override val keyColumn = text("poll_id") override val keyColumn = text("poll_id")
val postIdColumn = text("postId") 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 selectByValue: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { postIdColumn.eq(it.string) }
override val ResultRow.asKey: PollIdentifier override val ResultRow.asKey: PollId
get() = get(keyColumn) get() = PollId(get(keyColumn))
override val ResultRow.asObject: PostId override val ResultRow.asObject: PostId
get() = get(postIdColumn).let(::PostId) get() = get(postIdColumn).let(::PostId)
@@ -23,11 +23,11 @@ class ExposedPollsToPostsIdsRepo(
initTable() 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 it[postIdColumn] = v.string
} }
override fun insertKey(k: PollIdentifier, v: PostId, it: InsertStatement<Number>) { override fun insertKey(k: PollId, v: PostId, it: InsertStatement<Number>) {
it[keyColumn] = k it[keyColumn] = k.string
} }
} }

View File

@@ -1,8 +1,10 @@
package dev.inmo.plaguposter.ratings.repo package dev.inmo.plaguposter.ratings.repo
import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging 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.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.micro_utils.repos.cache.full.FullKeyValueCacheRepo
import dev.inmo.plaguposter.posts.models.PostId import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.ratings.models.Rating import dev.inmo.plaguposter.ratings.models.Rating
@@ -11,28 +13,32 @@ import kotlinx.coroutines.CoroutineScope
class CachedRatingsRepo( class CachedRatingsRepo(
private val base: RatingsRepo, private val base: RatingsRepo,
private val scope: CoroutineScope, 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) { ) : 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( override suspend fun getPosts(
range: ClosedRange<Rating>, range: ClosedRange<Rating>,
reversed: Boolean, reversed: Boolean,
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
): Map<PostId, Rating> { ): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
val result = mutableMapOf<PostId, Rating>() it in range
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()
} }
override suspend fun getPostsWithRatingGreaterEq( override suspend fun getPostsWithRatingGreaterEq(
@@ -40,22 +46,16 @@ class CachedRatingsRepo(
reversed: Boolean, reversed: Boolean,
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
): Map<PostId, Rating> = getPosts( ): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
then .. Rating(Double.MAX_VALUE), it >= then
reversed, }
count,
exclude
)
override suspend fun getPostsWithRatingLessEq( override suspend fun getPostsWithRatingLessEq(
then: Rating, then: Rating,
reversed: Boolean, reversed: Boolean,
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
): Map<PostId, Rating> = getPosts( ): Map<PostId, Rating> = getPosts(reversed, count, exclude) {
Rating(Double.MIN_VALUE) .. then, it <= then
reversed, }
count,
exclude
)
} }

View File

@@ -5,7 +5,6 @@ import dev.inmo.micro_utils.koin.singleWithBinds
import dev.inmo.micro_utils.repos.unset import dev.inmo.micro_utils.repos.unset
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.useCache import dev.inmo.plaguposter.common.useCache
import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.plaguposter.ratings.exposed.ExposedRatingsRepo import dev.inmo.plaguposter.ratings.exposed.ExposedRatingsRepo
import dev.inmo.plaguposter.ratings.repo.* import dev.inmo.plaguposter.ratings.repo.*
@@ -14,7 +13,6 @@ import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin import org.koin.core.Koin
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.dsl.binds
object Plugin : Plugin { object Plugin : Plugin {
override fun Module.setupDI(database: Database, params: JsonObject) { override fun Module.setupDI(database: Database, params: JsonObject) {

View File

@@ -49,7 +49,7 @@ class ExposedRatingsRepo (
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
): Map<PostId, Rating> = transaction(database) { ): Map<PostId, Rating> = transaction(database) {
select { selectAll().where {
ratingsColumn.greaterEq(range.start.double).and( ratingsColumn.greaterEq(range.start.double).and(
ratingsColumn.lessEq(range.endInclusive.double) ratingsColumn.lessEq(range.endInclusive.double)
).and( ).and(
@@ -66,7 +66,7 @@ class ExposedRatingsRepo (
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
) = transaction(database) { ) = 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 it.asKey to it.asObject
} }
}.toMap() }.toMap()
@@ -77,7 +77,7 @@ class ExposedRatingsRepo (
count: Int?, count: Int?,
exclude: List<PostId> exclude: List<PostId>
): Map<PostId, Rating> = transaction(database) { ): 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 it.asKey to it.asObject
} }
}.toMap() }.toMap()

View File

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

View File

@@ -24,7 +24,7 @@ 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.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.ChatId 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.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.message.textsources.regular import dev.inmo.tgbotapi.types.message.textsources.regular
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@@ -38,8 +38,8 @@ object Plugin : Plugin {
@Serializable @Serializable
private data class PublishState( private data class PublishState(
override val context: ChatId, override val context: ChatId,
val sourceMessageId: MessageIdentifier, val sourceMessageId: MessageId,
val messageInReply: MessageIdentifier val messageInReply: MessageId
) : State ) : State
@Serializable @Serializable
internal data class Config( internal data class Config(