24 Commits

Author SHA1 Message Date
194a26fa36 update plagubot version 2026-03-15 21:26:12 +06:00
e190ace1f2 start 0.11.1 2026-03-15 20:58:45 +06:00
0f2a0c24b2 Merge pull request #31 from InsanusMokrassar/0.11.0
0.11.0
2026-03-12 18:01:13 +06:00
a65e7598ba small optimization of panel ratings button 2026-03-12 17:56:05 +06:00
53cc851944 small optimization of panelApi.forceRefresh in onPollUpdates 2026-03-12 17:39:47 +06:00
815391d3f6 add refreshing of panel after poll update 2026-03-12 17:24:48 +06:00
f06d91e38f add templating of panelButtonText for ratings 2026-03-12 17:04:08 +06:00
028f1f48e8 add coloring of inline buttons 2026-03-12 16:31:29 +06:00
18180e67f6 made it buildable again 2026-03-12 16:19:00 +06:00
1091f1141b start 0.11.0 2026-03-12 16:19:00 +06:00
7dbc5ef254 Merge pull request #30 from InsanusMokrassar/0.10.1
0.10.1
2025-08-06 17:52:03 +06:00
380e6e1117 update tgbotapi version 2025-08-06 17:50:56 +06:00
d5d924401b start 0.10.1 2025-08-06 17:50:21 +06:00
53e74745aa Merge pull request #29 from InsanusMokrassar/0.10.0
0.10.0
2025-08-04 14:21:52 +06:00
bae2c67136 update dependencies 2025-08-04 14:21:34 +06:00
c579fb576c start 0.10.0 2025-08-04 14:19:32 +06:00
a1cffd688e fix license section in publish.gradle 2025-06-30 14:41:40 +06:00
5cab93a7fb Merge pull request #28 from InsanusMokrassar/0.9.0
0.9.0
2025-06-27 10:40:03 +06:00
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
27 changed files with 279 additions and 137 deletions

View File

@@ -1,5 +1,23 @@
# PlaguPoster # PlaguPoster
## 0.11.1
* Update `PlaguBot` dependency to allow using of `proxy` section in config
## 0.11.0
## 0.10.1
* Dependencies update
## 0.10.0
* Dependencies update
## 0.9.0
* Dependencies update
## 0.8.0 ## 0.8.0
* Dependencies update * Dependencies update

View File

@@ -2,23 +2,41 @@ buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
mavenLocal()
maven { url "https://plugins.gradle.org/m2/" } maven { url "https://plugins.gradle.org/m2/" }
mavenLocal()
} }
dependencies { dependencies {
classpath libs.kotlin.gradle.plugin classpath libs.kotlin.gradle.plugin
classpath libs.kotlin.serialization.plugin classpath libs.kotlin.serialization.plugin
classpath libs.kotlin.dokka.plugin classpath libs.kotlin.dokka.plugin
classpath libs.github.release
}
}
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 { allprojects {
repositories { repositories {
mavenLocal()
mavenCentral() mavenCentral()
google() google()
maven { url "https://nexus.inmo.dev/repository/maven-releases/" } maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
mavenLocal()
} }
} }

0
changelog_parser.sh Normal file → Executable file
View File

View File

@@ -11,21 +11,28 @@ private String getCurrentVersionChangelog() {
return changelogDataOS.toString().trim() return changelogDataOS.toString().trim()
} }
if (new File(projectDir, "secret.gradle").exists()) { def githubTokenVariableName = "GITHUB_RELEASE_TOKEN"
apply from: './secret.gradle' 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" apply plugin: "com.github.breadmoirai.github-release"
def githubReleaseToken = project.hasProperty(githubTokenVariableName) ? project.property(githubTokenVariableName).toString() : githubTokenVariableFromEnv
githubRelease { githubRelease {
token "${project.property('GITHUB_RELEASE_TOKEN')}" token githubReleaseToken
owner "InsanusMokrassar" owner = "InsanusMokrassar"
repo "PlaguPoster" repo = "PlaguPoster"
tagName "v${project.version}" tagName = "v${project.version}"
releaseName "${project.version}" releaseName = "${project.version}"
targetCommitish "${project.version}" targetCommitish = "${project.version}"
body getCurrentVersionChangelog() body = getCurrentVersionChangelog()
} }
} }

View File

@@ -9,4 +9,4 @@ android.enableJetifier=true
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.8.0 version=0.11.1

View File

@@ -1,19 +1,23 @@
[versions] [versions]
kotlin = "2.0.21" kotlin = "2.3.10"
kotlin-serialization = "1.7.3" kotlin-serialization = "1.10.0"
plagubot = "10.1.0" plagubot = "11.1.0"
tgbotapi = "20.0.0" tgbotapi = "32.0.0"
microutils = "0.23.0" microutils = "0.29.1"
kslog = "1.3.6" kslog = "1.6.0"
krontab = "2.6.1" krontab = "2.8.0"
plagubot-plugins = "0.24.0" plagubot-plugins = "0.25.0"
dokka = "1.9.20" nmcp = "1.4.4"
dokka = "2.0.0"
psql = "42.6.0" psql = "42.6.0"
github-release = "2.5.2"
[libraries] [libraries]
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
@@ -42,8 +46,11 @@ psql = { module = "org.postgresql:postgresql", version.ref = "psql" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-serialization-plugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" } kotlin-serialization-plugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
kotlin-dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } kotlin-dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" }
github-release = { module = "com.github.breadmoirai:github-release", version.ref = "github-release" }
[plugins] [plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -5,7 +5,6 @@ import dev.inmo.kslog.common.w
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
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

View File

@@ -33,6 +33,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.MilliSeconds import dev.inmo.tgbotapi.types.MilliSeconds
import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
import dev.inmo.tgbotapi.utils.bold import dev.inmo.tgbotapi.utils.bold
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@@ -148,7 +149,7 @@ object Plugin : Plugin {
message, message,
text = "Are you sure want to trigger posts garbage collecting?", text = "Are you sure want to trigger posts garbage collecting?",
replyMarkup = flatInlineKeyboard { replyMarkup = flatInlineKeyboard {
dataButton("Sure", yesData) dataButton("Sure", yesData, style = KeyboardButtonStyle.Primary)
dataButton("No", noData) dataButton("No", noData)
} }
) )

View File

@@ -1,13 +1,12 @@
package dev.inmo.plaguposter.posts.panel package dev.inmo.plaguposter.posts.panel
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.coroutines.runCatchingLogging
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.getAllDistinct import dev.inmo.micro_utils.koin.getAllDistinct
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.cache.cache.FullKVCache
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.micro_utils.repos.cache.full.fullyCached
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.registerConfig import dev.inmo.plagubot.registerConfig
@@ -35,6 +34,7 @@ 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
import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
import dev.inmo.tgbotapi.types.message.ParseMode import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.utils.bold import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities import dev.inmo.tgbotapi.utils.buildEntities
@@ -42,7 +42,6 @@ import dev.inmo.tgbotapi.utils.italic
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
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
@@ -73,7 +72,8 @@ object Plugin : Plugin {
PanelButtonBuilder { PanelButtonBuilder {
CallbackDataInlineKeyboardButton( CallbackDataInlineKeyboardButton(
text, text,
"refresh ${it.id.string}" "refresh ${it.id.string}",
style = KeyboardButtonStyle.Primary
) )
} }
} }
@@ -101,7 +101,7 @@ object Plugin : Plugin {
basePostsMessages basePostsMessages
} }
postsRepo.newObjectsFlow.subscribeSafelyWithoutExceptions(this) { postsRepo.newObjectsFlow.subscribeLoggingDropExceptions(this) {
val firstContent = it.content.first() val firstContent = it.content.first()
val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row -> val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row ->
row.mapNotNull { builder -> row.mapNotNull { builder ->
@@ -166,7 +166,7 @@ object Plugin : Plugin {
edit( edit(
query.message, query.message,
replyMarkup = flatInlineKeyboard { replyMarkup = flatInlineKeyboard {
dataButton("\uD83D\uDDD1", approveData) dataButton("\uD83D\uDDD1", approveData, style = KeyboardButtonStyle.Danger)
api.RootPanelButtonBuilder.buildButton(post) ?.let(::add) api.RootPanelButtonBuilder.buildButton(post) ?.let(::add)
} }
) )
@@ -188,7 +188,7 @@ object Plugin : Plugin {
val postId = query.data.removePrefix("refresh ").let(::PostId) val postId = query.data.removePrefix("refresh ").let(::PostId)
val (chatId, messageId) = postsMessages.get(postId) ?: return@onMessageDataCallbackQuery val (chatId, messageId) = postsMessages.get(postId) ?: return@onMessageDataCallbackQuery
runCatchingSafely { runCatching {
refreshPostMessage( refreshPostMessage(
postId, postId,
chatId, chatId,

View File

@@ -8,7 +8,7 @@ import dev.inmo.tgbotapi.types.*
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.v1.jdbc.Database
private val ChatIdToMessageSerializer = PairSerializer(FullChatIdentifierSerializer, MessageId.serializer()) private val ChatIdToMessageSerializer = PairSerializer(FullChatIdentifierSerializer, MessageId.serializer())

View File

@@ -24,7 +24,7 @@ import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.reply
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.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.Serializable
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import org.koin.core.Koin import org.koin.core.Koin
@@ -90,9 +90,9 @@ object Plugin : Plugin {
postsRepo.deleteById(postId) postsRepo.deleteById(postId)
if (postsRepo.contains(postId)) { if (postsRepo.contains(postId)) {
edit(it, it.content.textSources + regular(UnsuccessfulSymbol)) edit(it, it.content.textSources + regularTextSource(UnsuccessfulSymbol))
} else { } else {
edit(it, it.content.textSources + regular(SuccessfulSymbol)) edit(it, it.content.textSources + regularTextSource(SuccessfulSymbol))
} }
} }

View File

@@ -5,7 +5,11 @@ 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.* import dev.inmo.tgbotapi.types.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.v1.core.Column
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.jdbc.Database
internal class ExposedContentInfoRepo( internal class ExposedContentInfoRepo(
override val database: Database, override val database: Database,

View File

@@ -13,11 +13,19 @@ import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageId 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.v1.core.Op
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.v1.core.inList
import org.jetbrains.exposed.v1.core.isNull
import org.jetbrains.exposed.v1.core.statements.InsertStatement
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
class ExposedPostsRepo( class ExposedPostsRepo(
override val database: Database override val database: Database
@@ -37,8 +45,8 @@ class ExposedPostsRepo(
override val primaryKey: PrimaryKey = PrimaryKey(idColumn) override val primaryKey: PrimaryKey = PrimaryKey(idColumn)
override val selectById: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { idColumn.eq(it.string) } override val selectById: (PostId) -> Op<Boolean> = { idColumn.eq(it.string) }
override val selectByIds: ISqlExpressionBuilder.(List<PostId>) -> Op<Boolean> = { idColumn.inList(it.map { it.string }) } override val selectByIds: (List<PostId>) -> Op<Boolean> = { idColumn.inList(it.map { it.string }) }
override val ResultRow.asId: PostId override val ResultRow.asId: PostId
get() = PostId(get(idColumn)) get() = PostId(get(idColumn))
override val ResultRow.asObject: RegisteredPost override val ResultRow.asObject: RegisteredPost
@@ -132,8 +140,8 @@ class ExposedPostsRepo(
}.associateBy { it.id } }.associateBy { it.id }
val existsIds = posts.keys.toList() val existsIds = posts.keys.toList()
transaction(db = database) { transaction(db = database) {
val deleted = deleteWhere(null, null) { val deleted = deleteWhere {
selectByIds(it, existsIds) selectByIds(existsIds)
} }
with(contentRepo) { with(contentRepo) {
deleteWhere { deleteWhere {

View File

@@ -22,6 +22,7 @@ import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
import dev.inmo.tgbotapi.utils.regular import dev.inmo.tgbotapi.utils.regular
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@@ -56,7 +57,8 @@ object Plugin : Plugin {
flatInlineKeyboard { flatInlineKeyboard {
dataButton( dataButton(
"Finish", "Finish",
buttonUuid buttonUuid,
style = KeyboardButtonStyle.Primary,
) )
} }
} else { } else {

View File

@@ -1,3 +1,4 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
@@ -19,46 +20,30 @@ publishing {
} }
developers { developers {
developer {
developer { id = "InsanusMokrassar"
id = "InsanusMokrassar" name = "Aleksei Ovsiannikov"
name = "Aleksei Ovsiannikov" email = "ovsyannikov.alexey95@gmail.com"
email = "ovsyannikov.alexey95@gmail.com" }
}
} }
licenses { licenses {
license {
} name = "MIT License"
} url = "https://opensource.org/licenses/MIT"
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)
}
} }
} }
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { }
maven { }
name = "sonatype" repositories {
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven {
credentials { name = "InmoNexus"
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') url = uri("https://nexus.inmo.dev/repository/maven-releases/")
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
} 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')
} }
} }
} }
@@ -67,13 +52,13 @@ publishing {
if (project.hasProperty("signing.gnupg.keyName")) { if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing' apply plugin: 'signing'
signing { signing {
useGpgCmd() useGpgCmd()
sign publishing.publications sign publishing.publications
} }
task signAll { task signAll {
tasks.withType(Sign).forEach { tasks.withType(Sign).forEach {
dependsOn(it) 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":[{"id":"MIT","title":"MIT License","url":"https://opensource.org/licenses/MIT"}],"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/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -25,7 +25,6 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onComman
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.*
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

View File

@@ -3,6 +3,8 @@ package dev.inmo.plaguposter.ratings.source
import com.benasher44.uuid.uuid4 import com.benasher44.uuid.uuid4
import dev.inmo.kslog.common.e import dev.inmo.kslog.common.e
import dev.inmo.kslog.common.logger import dev.inmo.kslog.common.logger
import dev.inmo.micro_utils.common.fixed
import dev.inmo.micro_utils.coroutines.runCatchingLogging
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.pagination.firstPageWithOneElementPagination import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
@@ -42,23 +44,28 @@ import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
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.message.textsources.bold import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
import dev.inmo.tgbotapi.types.message.textsources.regular import dev.inmo.tgbotapi.types.keyboardButtonRequestUserLimit
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.InputPollOption
import dev.inmo.tgbotapi.types.polls.PollOption import dev.inmo.tgbotapi.types.polls.PollOption
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.extensions.makeSourceString 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
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
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.core.qualifier.named import org.koin.core.qualifier.named
import kotlin.math.roundToInt
object Plugin : Plugin { object Plugin : Plugin {
private val ratingVariantsQualifier = named("ratingsVariants") private val ratingVariantsQualifier = named("ratingsVariants")
private const val attachedSymbolControlSymbol = "\${attached_symbol}"
private const val currentRatingControlSymbol = "\${rating}"
@Serializable @Serializable
internal data class Config( internal data class Config(
@@ -66,7 +73,9 @@ object Plugin : Plugin {
val variants: RatingsVariants, val variants: RatingsVariants,
val autoAttach: Boolean, val autoAttach: Boolean,
val ratingOfferText: String, val ratingOfferText: String,
val panelButtonText: String = "Ratings" val panelButtonText: String = "Ratings $attachedSymbolControlSymbol",
val ratingEnabledStyle: KeyboardButtonStyle? = null,
val ratingDisabledStyle: KeyboardButtonStyle? = null,
) )
override fun Module.setupDI(params: JsonObject) { override fun Module.setupDI(params: JsonObject) {
@@ -113,12 +122,22 @@ object Plugin : Plugin {
val panelApi = koin.getOrNull<PanelButtonsAPI>() val panelApi = koin.getOrNull<PanelButtonsAPI>()
val chatConfig = koin.get<ChatConfig>() val chatConfig = koin.get<ChatConfig>()
val panelApiOnPollUpdatesUpdateTrigger: suspend (PostId) -> Unit = if (config.panelButtonText.contains(currentRatingControlSymbol)) {
{
panelApi ?.forceRefresh(it)
}
} else {
{
}
}
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.textSources.makeSourceString()) ?.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))
panelApiOnPollUpdatesUpdateTrigger(postId)
} }
suspend fun attachPoll(postId: PostId): Boolean { suspend fun attachPoll(postId: PostId): Boolean {
@@ -198,17 +217,17 @@ object Plugin : Plugin {
} }
if (attachPoll(postId)) { if (attachPoll(postId)) {
runCatchingSafely { runCatchingLogging {
edit( edit(
it, it,
it.content.textSources + regular(" $SuccessfulSymbol") it.content.textSources + regularTextSource(" $SuccessfulSymbol")
) )
} }
} else { } else {
runCatchingSafely { runCatchingLogging {
edit( edit(
it, it,
it.content.textSources + regular(" $UnsuccessfulSymbol") it.content.textSources + regularTextSource(" $UnsuccessfulSymbol")
) )
} }
} }
@@ -233,17 +252,17 @@ object Plugin : Plugin {
if (detachPoll(postId)) { if (detachPoll(postId)) {
runCatchingSafely { runCatchingLogging {
edit( edit(
it, it,
it.content.textSources + regular(" $SuccessfulSymbol") it.content.textSources + regularTextSource(" $SuccessfulSymbol")
) )
} }
} else { } else {
runCatchingSafely { runCatchingLogging {
edit( edit(
it, it,
it.content.textSources + regular(" $UnsuccessfulSymbol") it.content.textSources + regularTextSource(" $UnsuccessfulSymbol")
) )
} }
} }
@@ -295,13 +314,69 @@ object Plugin : Plugin {
panelApi ?.apply { panelApi ?.apply {
add( add(
PanelButtonBuilder { PanelButtonBuilder {
suspend fun isEnabled() = pollsToPostsIdsRepo.keys(it.id, firstPageWithOneElementPagination).results.any()
val enabled = let { _ ->
var enabled: Boolean? = null
suspend {
enabled ?: (isEnabled().also { enabled = it })
}
}
val rating = let { _ ->
var rating: Rating? = null
suspend {
if (isEnabled()) {
rating ?: ratingsRepo.get(it.id) ?.also { rating = it }
} else {
null
}
}
}
val resultText = config.panelButtonText
.let {
if (it.contains(attachedSymbolControlSymbol)) {
it.replace(
attachedSymbolControlSymbol,
if (enabled()) {
SuccessfulSymbol
} else {
UnsuccessfulSymbol
}
)
} else {
it
}
}
.let {
if (it.contains(currentRatingControlSymbol)) {
val rating = rating()
if (rating != null) {
it.replace(
currentRatingControlSymbol,
rating.double.roundToInt().toString()
)
} else {
it.replace(
currentRatingControlSymbol,
UnsuccessfulSymbol
)
}
} else {
it
}
}
.take(12) // maximal text appropriate for this button
CallbackDataInlineKeyboardButton( CallbackDataInlineKeyboardButton(
config.panelButtonText + if (pollsToPostsIdsRepo.keys(it.id, firstPageWithOneElementPagination).results.any()) { resultText,
SuccessfulSymbol "toggle_ratings ${it.id.string}",
style = if (config.ratingEnabledStyle != null || config.ratingDisabledStyle != null) {
if (enabled()) {
config.ratingEnabledStyle
} else {
config.ratingDisabledStyle
}
} else { } else {
UnsuccessfulSymbol null
}, }
"toggle_ratings ${it.id.string}"
) )
} }
) )

View File

@@ -4,10 +4,13 @@ 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.* import dev.inmo.tgbotapi.types.*
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.sql.SqlExpressionBuilder.isNull import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.isNull
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
import org.jetbrains.exposed.v1.jdbc.Database
class ExposedPollsToMessagesInfoRepo( class ExposedPollsToMessagesInfoRepo(
database: Database database: Database
@@ -19,8 +22,8 @@ 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.(PollId) -> Op<Boolean> = { keyColumn.eq(it.string) } override val selectById: (PollId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(ShortMessageInfo) -> Op<Boolean> = { override val selectByValue: (ShortMessageInfo) -> Op<Boolean> = {
chatIdColumn.eq(it.chatId.chatId.long) chatIdColumn.eq(it.chatId.chatId.long)
.and(it.chatId.threadId?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull()).and( .and(it.chatId.threadId?.let { threadIdColumn.eq(it.long) } ?: threadIdColumn.isNull()).and(
messageIdColumn.eq(it.messageId.long) messageIdColumn.eq(it.messageId.long)

View File

@@ -4,16 +4,19 @@ 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.PollId import dev.inmo.tgbotapi.types.PollId
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
import org.jetbrains.exposed.v1.jdbc.Database
class ExposedPollsToPostsIdsRepo( class ExposedPollsToPostsIdsRepo(
database: Database database: Database
) : PollsToPostsIdsRepo, AbstractExposedKeyValueRepo<PollId, 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.(PollId) -> Op<Boolean> = { keyColumn.eq(it.string) } override val selectById: (PollId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { postIdColumn.eq(it.string) } override val selectByValue: (PostId) -> Op<Boolean> = { postIdColumn.eq(it.string) }
override val ResultRow.asKey: PollId override val ResultRow.asKey: PollId
get() = PollId(get(keyColumn)) get() = PollId(get(keyColumn))
override val ResultRow.asObject: PostId override val ResultRow.asObject: PostId

View File

@@ -6,9 +6,17 @@ 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.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 org.jetbrains.exposed.sql.* import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.sql.statements.* import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.greaterEq
import org.jetbrains.exposed.v1.core.lessEq
import org.jetbrains.exposed.v1.core.notInList
import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.Query
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
class ExposedRatingsRepo ( class ExposedRatingsRepo (
database: Database database: Database
@@ -18,8 +26,8 @@ class ExposedRatingsRepo (
) { ) {
override val keyColumn = text("post_id") override val keyColumn = text("post_id")
val ratingsColumn = double("rating") val ratingsColumn = double("rating")
override val selectById: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { keyColumn.eq(it.string) } override val selectById: (PostId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(Rating) -> Op<Boolean> = { ratingsColumn.eq(it.double) } override val selectByValue: (Rating) -> Op<Boolean> = { ratingsColumn.eq(it.double) }
override val ResultRow.asKey: PostId override val ResultRow.asKey: PostId
get() = get(keyColumn).let(::PostId) get() = get(keyColumn).let(::PostId)
override val ResultRow.asObject: Rating override val ResultRow.asObject: Rating

View File

@@ -41,7 +41,11 @@
"Results": 0 "Results": 0
}, },
"autoAttach": true, "autoAttach": true,
"ratingOfferText": "What do you think about it?" "ratingOfferText": "What do you think about it?",
"panelButtonText": "Ratings ${attached_symbol}",
"ratingEnabledStyle": "success",
"ratingDisabledStyle": "danger",
"_note": "For panelButtonText you may use two control symbols: ${attached_symbol} to attach icon about enabled state and ${rating} to enable rating showing in button"
}, },
"selector": { "selector": {
"items": [ "items": [

View File

@@ -27,11 +27,10 @@ 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.MessageId 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.regularTextSource
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
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
@@ -90,7 +89,7 @@ object Plugin : Plugin {
edit( edit(
it, it,
it.content.textSources + regular(SuccessfulSymbol) it.content.textSources + regularTextSource(SuccessfulSymbol)
) )
} }
@@ -114,7 +113,7 @@ object Plugin : Plugin {
PanelButtonBuilder { PanelButtonBuilder {
CallbackDataInlineKeyboardButton( CallbackDataInlineKeyboardButton(
text, text,
"publish ${it.id.string}" "publish ${it.id.string}",
) )
} }
) )

View File

@@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.take
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
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

View File

@@ -6,6 +6,7 @@ import dev.inmo.plaguposter.posts.models.RegisteredPost
import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.InlineKeyboardButton import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.InlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
class TimerPanelButton( class TimerPanelButton(
private val timersRepo: TimersRepo private val timersRepo: TimersRepo
@@ -18,7 +19,12 @@ class TimerPanelButton(
return CallbackDataInlineKeyboardButton( return CallbackDataInlineKeyboardButton(
"${ if (publishingTime == null) UnsuccessfulSymbol else SuccessfulSymbol }", "${ if (publishingTime == null) UnsuccessfulSymbol else SuccessfulSymbol }",
"$timerSetPrefix ${post.id}" "$timerSetPrefix ${post.id}",
style = if (publishingTime == null) {
null
} else {
KeyboardButtonStyle.Success
}
) )
} }

View File

@@ -11,15 +11,12 @@ 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.triggers.timer.TimersRepo import dev.inmo.plaguposter.triggers.timer.TimersRepo
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.v1.core.ResultRow
import org.jetbrains.exposed.sql.ISqlExpressionBuilder import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.sql.Op import org.jetbrains.exposed.v1.core.statements.UpdateBuilder
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateBuilder
import org.jetbrains.exposed.sql.transactions.transaction
class ExposedTimersRepo( class ExposedTimersRepo(
database: Database, database: Database,
@@ -31,8 +28,8 @@ class ExposedTimersRepo(
) { ) {
override val keyColumn = text("post_id") override val keyColumn = text("post_id")
private val dateTimeColumn = long("date_time") private val dateTimeColumn = long("date_time")
override val selectById: ISqlExpressionBuilder.(PostId) -> Op<Boolean> = { keyColumn.eq(it.string) } override val selectById: (PostId) -> Op<Boolean> = { keyColumn.eq(it.string) }
override val selectByValue: ISqlExpressionBuilder.(DateTime) -> Op<Boolean> = { dateTimeColumn.eq(it.unixMillisLong) } override val selectByValue: (DateTime) -> Op<Boolean> = { dateTimeColumn.eq(it.unixMillisLong) }
override val ResultRow.asKey: PostId override val ResultRow.asKey: PostId
get() = PostId(get(keyColumn)) get() = PostId(get(keyColumn))
override val ResultRow.asObject: DateTime override val ResultRow.asObject: DateTime