production ready?

This commit is contained in:
InsanusMokrassar 2022-09-04 15:46:45 +06:00
parent 1e393103c8
commit e8637a2de6
12 changed files with 130 additions and 19 deletions

View File

@ -1,4 +1,4 @@
package dev.inmo.plaguposter.common package dev.inmo.plaguposter.common
const val SuccessfulSymbol = "" const val SuccessfulSymbol = ""
const val UnsuccessfulSymbol = "" const val UnsuccessfulSymbol = ""

View File

@ -1,4 +1,5 @@
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

View File

@ -3,7 +3,7 @@
kotlin = "1.7.10" kotlin = "1.7.10"
kotlin-serialization = "1.4.0" kotlin-serialization = "1.4.0"
plagubot = "2.2.0" plagubot = "2.3.0"
tgbotapi = "3.2.0" tgbotapi = "3.2.0"
microutils = "0.12.6" microutils = "0.12.6"
kslog = "0.5.1" kslog = "0.5.1"

View File

@ -3,5 +3,8 @@ package dev.inmo.plaguposter.posts.repo
import dev.inmo.micro_utils.repos.ReadCRUDRepo import dev.inmo.micro_utils.repos.ReadCRUDRepo
import dev.inmo.micro_utils.repos.WriteCRUDRepo import dev.inmo.micro_utils.repos.WriteCRUDRepo
import dev.inmo.plaguposter.posts.models.* import dev.inmo.plaguposter.posts.models.*
import kotlinx.coroutines.flow.Flow
interface WritePostsRepo : WriteCRUDRepo<RegisteredPost, PostId, NewPost> interface WritePostsRepo : WriteCRUDRepo<RegisteredPost, PostId, NewPost> {
val removedPostsFlow: Flow<RegisteredPost>
}

View File

@ -2,20 +2,37 @@ package dev.inmo.plaguposter.posts
import dev.inmo.kslog.common.logger import dev.inmo.kslog.common.logger
import dev.inmo.kslog.common.w import dev.inmo.kslog.common.w
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.deleteById
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.SuccessfulSymbol
import dev.inmo.plaguposter.common.UnsuccessfulSymbol
import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo
import dev.inmo.plaguposter.posts.models.ChatConfig import dev.inmo.plaguposter.posts.models.ChatConfig
import dev.inmo.plaguposter.posts.repo.* import dev.inmo.plaguposter.posts.repo.*
import dev.inmo.plaguposter.posts.sending.PostPublisher import dev.inmo.plaguposter.posts.sending.PostPublisher
import dev.inmo.tgbotapi.extensions.api.delete
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.ChatId import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.message.textsources.regular
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.* 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.module.Module import org.koin.core.module.Module
import org.koin.dsl.binds import org.koin.dsl.binds
object Plugin : Plugin { object Plugin : Plugin {
@Serializable
data class Config(
val chats: ChatConfig,
val autoRemoveMessages: Boolean = true
)
override fun Module.setupDI(database: Database, params: JsonObject) { override fun Module.setupDI(database: Database, params: JsonObject) {
val configJson = params["posts"] ?: this@Plugin.let { val configJson = params["posts"] ?: this@Plugin.let {
it.logger.w { it.logger.w {
@ -23,7 +40,8 @@ object Plugin : Plugin {
} }
return return
} }
single { get<Json>().decodeFromJsonElement(ChatConfig.serializer(), configJson) } single { get<Json>().decodeFromJsonElement(Config.serializer(), configJson) }
single { get<Config>().chats }
single { ExposedPostsRepo(database) } binds arrayOf( single { ExposedPostsRepo(database) } binds arrayOf(
PostsRepo::class, PostsRepo::class,
ReadPostsRepo::class, ReadPostsRepo::class,
@ -34,4 +52,39 @@ object Plugin : Plugin {
PostPublisher(get(), get(), config.cacheChatId, config.targetChatId) PostPublisher(get(), get(), config.cacheChatId, config.targetChatId)
} }
} }
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
val postsRepo = koin.get<PostsRepo>()
val config = koin.get<Config>()
if (config.autoRemoveMessages) {
postsRepo.removedPostsFlow.subscribeSafelyWithoutExceptions(this) {
it.content.forEach {
runCatchingSafely {
delete(it.chatId, it.messageId)
}
}
}
}
onCommand("delete_post", requireOnlyCommandInMessage = true) {
val messageInReply = it.replyTo ?: run {
reply(it, "Reply some message of post to delete it")
return@onCommand
}
val postId = postsRepo.getIdByChatAndMessage(messageInReply.chat.id, messageInReply.messageId) ?: run {
reply(it, "Unable to find post id by message")
return@onCommand
}
postsRepo.deleteById(postId)
if (postsRepo.contains(postId)) {
edit(it, it.content.textSources + regular(UnsuccessfulSymbol))
} else {
edit(it, it.content.textSources + regular(SuccessfulSymbol))
}
}
}
} }

View File

@ -8,6 +8,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.MessageIdentifier import dev.inmo.tgbotapi.types.MessageIdentifier
import kotlinx.coroutines.flow.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.InsertStatement
@ -45,6 +46,9 @@ class ExposedPostsRepo(
) )
} }
private val _removedPostsFlow = MutableSharedFlow<RegisteredPost>()
override val removedPostsFlow: Flow<RegisteredPost> = _removedPostsFlow.asSharedFlow()
init { init {
initTable() initTable()
} }
@ -93,24 +97,29 @@ class ExposedPostsRepo(
override suspend fun deleteById(ids: List<PostId>) { override suspend fun deleteById(ids: List<PostId>) {
onBeforeDelete(ids) onBeforeDelete(ids)
val posts = ids.mapNotNull {
getById(it)
}.associateBy { it.id }
val existsIds = posts.keys.toList()
transaction(db = database) { transaction(db = database) {
val deleted = deleteWhere(null, null) { val deleted = deleteWhere(null, null) {
selectByIds(ids) selectByIds(existsIds)
} }
with(contentRepo) { with(contentRepo) {
deleteWhere { deleteWhere {
postIdColumn.inList(ids.map { it.string }) postIdColumn.inList(existsIds.map { it.string })
} }
} }
if (deleted == ids.size) { if (deleted == existsIds.size) {
ids existsIds
} else { } else {
ids.filter { existsIds.filter {
select { selectById(it) }.limit(1).none() select { selectById(it) }.limit(1).none()
} }
} }
}.forEach { }.forEach {
_deletedObjectsIdsFlow.emit(it) _deletedObjectsIdsFlow.emit(it)
_removedPostsFlow.emit(posts[it] ?: return@forEach)
} }
} }

View File

@ -1,19 +1,17 @@
package dev.inmo.plaguposter.posts.registrar package dev.inmo.plaguposter.posts.registrar
import com.benasher44.uuid.uuid4 import dev.inmo.micro_utils.coroutines.*
import dev.inmo.kslog.common.logger
import dev.inmo.kslog.common.w
import dev.inmo.micro_utils.coroutines.firstOf
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.fsm.common.State import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.micro_utils.repos.create import dev.inmo.micro_utils.repos.create
import dev.inmo.micro_utils.repos.deleteById
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.FirstSourceIsCommandsFilter import dev.inmo.plaguposter.common.*
import dev.inmo.plaguposter.posts.models.* import dev.inmo.plaguposter.posts.models.*
import dev.inmo.plaguposter.posts.registrar.state.RegistrationState import dev.inmo.plaguposter.posts.registrar.state.RegistrationState
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
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.reply
import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.* import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
@ -30,6 +28,7 @@ import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.regular
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,20 +1,32 @@
package dev.inmo.plaguposter.ratings package dev.inmo.plaguposter.ratings
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.unset
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo import dev.inmo.plaguposter.posts.exposed.ExposedPostsRepo
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.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import kotlinx.serialization.json.* 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.module.Module import org.koin.core.module.Module
import org.koin.dsl.binds 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) {
single { ExposedRatingsRepo(database, get<ExposedPostsRepo>().idColumn) } binds arrayOf( single { ExposedRatingsRepo(database) } binds arrayOf(
RatingsRepo::class, RatingsRepo::class,
ReadRatingsRepo::class, ReadRatingsRepo::class,
WriteRatingsRepo::class, WriteRatingsRepo::class,
) )
} }
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
val ratingsRepo = koin.get<RatingsRepo>()
koin.get<PostsRepo>().deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(this) {
ratingsRepo.unset(it)
}
}
} }

View File

@ -10,11 +10,10 @@ import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
class ExposedRatingsRepo( class ExposedRatingsRepo(
database: Database, database: Database
postIdColumnReference: Column<String>
) : RatingsRepo, KeyValueRepo<PostId, Rating> by ExposedKeyValueRepo( ) : RatingsRepo, KeyValueRepo<PostId, Rating> by ExposedKeyValueRepo(
database, database,
{ text("post_id") references postIdColumnReference }, { text("post_id") },
{ double("rating") }, { double("rating") },
"ratings" "ratings"
).withMapper( ).withMapper(

7
runner/Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM adoptopenjdk/openjdk11
USER 1000
ENTRYPOINT ["/plaguposter.runner/bin/plaguposter.runner", "/config.json"]
ADD ./build/distributions/plaguposter.runner.tar /

View File

@ -4,6 +4,8 @@ plugins {
id 'application' id 'application'
} }
project.version = null
dependencies { dependencies {
implementation libs.kotlin implementation libs.kotlin
api libs.plagubot.bot api libs.plagubot.bot

26
runner/deploy.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
function send_notification() {
echo "$1"
}
function assert_success() {
"${@}"
local status=${?}
if [ ${status} -ne 0 ]; then
send_notification "### Error ${status} at: ${BASH_LINENO[*]} ###"
exit ${status}
fi
}
app=plaguposter
version=0.0.1
server=docker.inmo.dev
assert_success ../gradlew build
# scp ./build/distributions/AutoPostTestTelegramBot-1.0.0.zip ./config.json developer@insanusmokrassar.dev:/tmp/
assert_success sudo docker build -t $app:"$version" .
assert_success sudo docker tag $app:"$version" $server/$app:$version
assert_success sudo docker tag $app:"$version" $server/$app:latest
assert_success sudo docker push $server/$app:$version
assert_success sudo docker push $server/$app:latest