Merge pull request #2 from InsanusMokrassar/0.0.2

0.0.2
This commit is contained in:
InsanusMokrassar 2022-09-15 02:22:44 +06:00 committed by GitHub
commit 609c6b97fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 393 additions and 14 deletions

View File

@ -10,5 +10,5 @@ android.enableJetifier=true
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.0.1 version=0.0.2
android_code_version=1 android_code_version=2

View File

@ -5,9 +5,9 @@ kotlin-serialization = "1.4.0"
plagubot = "2.3.1" plagubot = "2.3.1"
tgbotapi = "3.2.1" tgbotapi = "3.2.1"
microutils = "0.12.11" microutils = "0.12.13"
kslog = "0.5.1" kslog = "0.5.2"
krontab = "0.8.0" krontab = "0.8.1"
tgbotapi-libraries = "0.5.3" tgbotapi-libraries = "0.5.3"
psql = "42.3.6" psql = "42.3.6"
@ -39,6 +39,7 @@ plagubot-bot = { module = "dev.inmo:plagubot.bot", version.ref = "plagubot" }
microutils-repos-common = { module = "dev.inmo:micro_utils.repos.common", version.ref = "microutils" } microutils-repos-common = { module = "dev.inmo:micro_utils.repos.common", version.ref = "microutils" }
microutils-repos-exposed = { module = "dev.inmo:micro_utils.repos.exposed", version.ref = "microutils" } microutils-repos-exposed = { module = "dev.inmo:micro_utils.repos.exposed", version.ref = "microutils" }
microutils-repos-cache = { module = "dev.inmo:micro_utils.repos.cache", version.ref = "microutils" } microutils-repos-cache = { module = "dev.inmo:micro_utils.repos.cache", version.ref = "microutils" }
microutils-koin = { module = "dev.inmo:micro_utils.koin", version.ref = "microutils" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" } kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
krontab = { module = "dev.inmo:krontab", version.ref = "krontab" } krontab = { module = "dev.inmo:krontab", version.ref = "krontab" }

19
posts/panel/build.gradle Normal file
View File

@ -0,0 +1,19 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api project(":plaguposter.common")
api project(":plaguposter.posts")
api libs.microutils.koin
}
}
}
}

View File

@ -0,0 +1 @@
package dev.inmo.plaguposter.posts.panel

View File

@ -0,0 +1,8 @@
package dev.inmo.plaguposter.posts.panel
import dev.inmo.plaguposter.posts.models.RegisteredPost
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.InlineKeyboardButton
fun interface PanelButtonBuilder {
suspend fun buildButton(post: RegisteredPost): InlineKeyboardButton?
}

View File

@ -0,0 +1,35 @@
package dev.inmo.plaguposter.posts.panel
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import kotlinx.coroutines.flow.MutableSharedFlow
class PanelButtonsAPI(
private val preset: List<PanelButtonBuilder>,
private val rootPanelButtonText: String
) {
private val _buttons = mutableSetOf<PanelButtonBuilder>().also {
it.addAll(preset)
}
internal val buttonsBuilders: List<PanelButtonBuilder>
get() = _buttons.toList()
internal val forceRefreshFlow = MutableSharedFlow<PostId>()
val RootPanelButtonBuilder = PanelButtonBuilder {
CallbackDataInlineKeyboardButton(
rootPanelButtonText,
"$openGlobalMenuDataPrefix${it.id.string}"
)
}
fun add(button: PanelButtonBuilder) = _buttons.add(button)
fun remove(button: PanelButtonBuilder) = _buttons.remove(button)
suspend fun forceRefresh(postId: PostId) {
forceRefreshFlow.emit(postId)
}
companion object {
internal const val openGlobalMenuData = "force_refresh_panel"
internal const val openGlobalMenuDataPrefix = "$openGlobalMenuData "
}
}

View File

@ -0,0 +1,158 @@
package dev.inmo.plaguposter.posts.panel
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.koin.getAllDistinct
import dev.inmo.micro_utils.repos.deleteById
import dev.inmo.micro_utils.repos.set
import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.ChatConfig
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.repos.PostsMessages
import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.tgbotapi.extensions.api.delete
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.content.TextContent
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
object Plugin : Plugin {
@Serializable
internal data class Config (
val text: String = "Post settings:",
val parseMode: ParseMode? = null,
val buttonsPerRow: Int = 4,
val deleteButtonText: String? = null,
val rootButtonText: String = "Return to panel"
)
override fun Module.setupDI(database: Database, params: JsonObject) {
params["panel"] ?.let { element ->
single { get<Json>().decodeFromJsonElement(Config.serializer(), element) }
}
single {
val config = getOrNull<Config>() ?: Config()
val builtInButtons = listOfNotNull(
config.deleteButtonText ?.let { text ->
PanelButtonBuilder {
CallbackDataInlineKeyboardButton(
text,
"delete ${it.id.string}"
)
}
}
)
PanelButtonsAPI(
getAllDistinct<PanelButtonBuilder>() + builtInButtons,
config.rootButtonText
)
}
}
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
val postsRepo = koin.get<PostsRepo>()
val chatsConfig = koin.get<ChatConfig>()
val config = koin.getOrNull<Config>() ?: Config()
val api = koin.get<PanelButtonsAPI>()
val postsMessages = PostsMessages(koin.get(), koin.get())
postsRepo.newObjectsFlow.subscribeSafelyWithoutExceptions(this) {
val firstContent = it.content.first()
val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row ->
row.mapNotNull { builder ->
builder.buildButton(it)
}.takeIf { it.isNotEmpty() }
}
send(
firstContent.chatId,
text = config.text,
parseMode = config.parseMode,
replyToMessageId = firstContent.messageId,
replyMarkup = InlineKeyboardMarkup(buttons),
disableNotification = true
).also { sentMessage ->
postsMessages.set(it.id, sentMessage.chat.id to sentMessage.messageId)
}
}
postsRepo.deletedObjectsIdsFlow.subscribeSafelyWithoutExceptions(this) {
val (chatId, messageId) = postsMessages.get(it) ?: return@subscribeSafelyWithoutExceptions
delete(chatId, messageId)
}
suspend fun updatePost(
postId: PostId,
chatId: ChatId,
messageId: MessageIdentifier
) {
val post = postsRepo.getById(postId) ?: return
val buttons = api.buttonsBuilders.chunked(config.buttonsPerRow).mapNotNull { row ->
row.mapNotNull { builder ->
builder.buildButton(post)
}.takeIf { it.isNotEmpty() }
}
edit(
chatId,
messageId,
replyMarkup = InlineKeyboardMarkup(buttons)
)
}
onMessageDataCallbackQuery (
initialFilter = {
it.data.startsWith(PanelButtonsAPI.openGlobalMenuDataPrefix) && it.message.chat.id == chatsConfig.sourceChatId
}
) {
val postId = it.data.removePrefix(PanelButtonsAPI.openGlobalMenuDataPrefix).let(::PostId)
updatePost(postId, it.message.chat.id, it.message.messageId)
}
onMessageDataCallbackQuery(
initialFilter = {
it.data.startsWith("delete ") && it.message.chat.id == chatsConfig.sourceChatId
}
) { query ->
val postId = query.data.removePrefix("delete ").let(::PostId)
val post = postsRepo.getById(postId) ?: return@onMessageDataCallbackQuery
val approveData = uuid4().toString()
edit(
query.message,
replyMarkup = flatInlineKeyboard {
dataButton("\uD83D\uDDD1", approveData)
api.RootPanelButtonBuilder.buildButton(post) ?.let(::add)
}
)
val pushedButton = waitMessageDataCallbackQuery().first {
it.message.sameMessage(query.message)
}
if (pushedButton.data == approveData) {
postsRepo.deleteById(postId)
}
}
api.forceRefreshFlow.subscribeSafelyWithoutExceptions(this) {
val (chatId, messageId) = postsMessages.get(it) ?: return@subscribeSafelyWithoutExceptions
updatePost(it, chatId, messageId)
}
}
}

View File

@ -0,0 +1,29 @@
package dev.inmo.plaguposter.posts.panel.repos
import dev.inmo.micro_utils.repos.KeyValueRepo
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.sql.Database
private val ChatIdToMessageSerializer = PairSerializer(ChatId.serializer(), MessageIdentifier.serializer())
fun PostsMessages(
database: Database,
json: Json
): KeyValueRepo<PostId, Pair<ChatId, MessageIdentifier>> = ExposedKeyValueRepo<String, String>(
database,
{ text("postId") },
{ text("chatToMessage") },
"panel_messages_info"
).withMapper(
{ string },
{ json.encodeToString(ChatIdToMessageSerializer, this) },
{ PostId(this) },
{ json.decodeFromString(ChatIdToMessageSerializer, this) }
)

View File

@ -0,0 +1 @@
<manifest package="dev.inmo.plaguposter.posts.panel"/>

View File

@ -103,11 +103,6 @@ object Plugin : Plugin {
NewPost( NewPost(
state.messages state.messages
) )
).firstOrNull() ?.let {
send(state.context, "Ok, you have registered ${it.content.size} messages as new post")
} ?: send(
state.context,
"Sorry, for some reason I was unable to register your post"
) )
null null
} }

View File

@ -12,6 +12,7 @@ kotlin {
dependencies { dependencies {
api project(":plaguposter.common") api project(":plaguposter.common")
api project(":plaguposter.ratings") api project(":plaguposter.ratings")
api project(":plaguposter.posts.panel")
} }
} }
jvmMain { jvmMain {

View File

@ -1,5 +1,6 @@
package dev.inmo.plaguposter.ratings.source package dev.inmo.plaguposter.ratings.source
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.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.runCatchingSafely
@ -14,18 +15,28 @@ import dev.inmo.plaguposter.inlines.models.Format
import dev.inmo.plaguposter.inlines.models.OfferTemplate import dev.inmo.plaguposter.inlines.models.OfferTemplate
import dev.inmo.plaguposter.inlines.repos.InlineTemplatesRepo import dev.inmo.plaguposter.inlines.repos.InlineTemplatesRepo
import dev.inmo.plaguposter.posts.models.PostId import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder
import dev.inmo.plaguposter.posts.panel.PanelButtonsAPI
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.plaguposter.ratings.source.models.* import dev.inmo.plaguposter.ratings.source.models.*
import dev.inmo.plaguposter.ratings.source.repos.* import dev.inmo.plaguposter.ratings.source.repos.*
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.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
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.message.textsources.regular import dev.inmo.tgbotapi.types.message.textsources.regular
import kotlinx.coroutines.flow.filter
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.jetbrains.exposed.sql.Database
@ -41,7 +52,8 @@ object Plugin : Plugin {
@Serializable(RatingsVariantsSerializer::class) @Serializable(RatingsVariantsSerializer::class)
val variants: RatingsVariants, val variants: RatingsVariants,
val autoAttach: Boolean, val autoAttach: Boolean,
val ratingOfferText: String val ratingOfferText: String,
val panelButtonText: String = "Ratings"
) )
override fun Module.setupDI(database: Database, params: JsonObject) { override fun Module.setupDI(database: Database, params: JsonObject) {
@ -66,6 +78,7 @@ object Plugin : Plugin {
val ratingsRepo = koin.get<RatingsRepo>() val ratingsRepo = koin.get<RatingsRepo>()
val postsRepo = koin.get<PostsRepo>() val postsRepo = koin.get<PostsRepo>()
val config = koin.get<Config>() val config = koin.get<Config>()
val panelApi = koin.getOrNull<PanelButtonsAPI>()
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
@ -92,6 +105,7 @@ object Plugin : Plugin {
pollsToPostsIdsRepo.set(sent.content.poll.id, postId) pollsToPostsIdsRepo.set(sent.content.poll.id, postId)
pollsToMessageInfoRepo.set(sent.content.poll.id, sent.short()) pollsToMessageInfoRepo.set(sent.content.poll.id, sent.short())
}.getOrNull() ?: continue }.getOrNull() ?: continue
panelApi ?.forceRefresh(postId)
return true return true
} }
return false return false
@ -105,6 +119,8 @@ object Plugin : Plugin {
delete(messageInfo.chatId, messageInfo.messageId) delete(messageInfo.chatId, messageInfo.messageId)
}.onFailure { }.onFailure {
this@Plugin.logger.e(it) { "Something went wrong when trying to remove ratings message ($messageInfo) for post $postId" } this@Plugin.logger.e(it) { "Something went wrong when trying to remove ratings message ($messageInfo) for post $postId" }
}.onSuccess {
panelApi ?.forceRefresh(postId)
}.isSuccess }.isSuccess
}.any().also { }.any().also {
if (it) { if (it) {
@ -209,5 +225,55 @@ object Plugin : Plugin {
) )
) )
} }
panelApi ?.apply {
add(
PanelButtonBuilder {
CallbackDataInlineKeyboardButton(
config.panelButtonText + if (pollsToPostsIdsRepo.keys(it.id, firstPageWithOneElementPagination).results.any()) {
SuccessfulSymbol
} else {
UnsuccessfulSymbol
},
"toggle_ratings ${it.id.string}"
)
}
)
onMessageDataCallbackQuery(
initialFilter = {
it.data.startsWith("toggle_ratings ")
}
) {
val postId = it.data.removePrefix("toggle_ratings ").let(::PostId)
val post = postsRepo.getById(postId) ?: return@onMessageDataCallbackQuery
val ratingPollAttached = pollsToPostsIdsRepo.keys(postId, firstPageWithOneElementPagination).results.any()
val toggleData = uuid4().toString()
val editedMessage = edit(
it.message,
replyMarkup = flatInlineKeyboard {
dataButton(
if (ratingPollAttached) {
UnsuccessfulSymbol
} else {
SuccessfulSymbol
},
toggleData
)
panelApi.RootPanelButtonBuilder.buildButton(post) ?.let(::add)
}
)
val pushedButton = waitMessageDataCallbackQuery().first { it.message.sameMessage(editedMessage) }
if (pushedButton.data == toggleData) {
if (pollsToPostsIdsRepo.keys(postId, firstPageWithOneElementPagination).results.any()) {
detachPoll(postId)
} else {
attachPoll(postId)
}
}
}
}
} }
} }

View File

@ -11,6 +11,7 @@ dependencies {
api libs.plagubot.bot api libs.plagubot.bot
api project(":plaguposter.posts") api project(":plaguposter.posts")
api project(":plaguposter.posts.panel")
api project(":plaguposter.posts_registrar") api project(":plaguposter.posts_registrar")
api project(":plaguposter.triggers.command") api project(":plaguposter.triggers.command")
api project(":plaguposter.triggers.selector_with_timer") api project(":plaguposter.triggers.selector_with_timer")

View File

@ -14,7 +14,7 @@ function assert_success() {
} }
app=plaguposter app=plaguposter
version=0.0.1 version="`grep ../gradle.properties -e "^version=" | grep -e "[0-9.]*" -o`"
server=docker.io/insanusmokrassar server=docker.io/insanusmokrassar
assert_success ../gradlew build assert_success ../gradlew build

View File

@ -3,6 +3,7 @@ rootProject.name = 'plaguposter'
String[] includes = [ String[] includes = [
":common", ":common",
":posts", ":posts",
":posts:panel",
":posts_registrar", ":posts_registrar",
":ratings", ":ratings",
":ratings:source", ":ratings:source",

View File

@ -13,6 +13,7 @@ kotlin {
api project(":plaguposter.common") api project(":plaguposter.common")
api project(":plaguposter.posts") api project(":plaguposter.posts")
api project(":plaguposter.ratings.selector") api project(":plaguposter.ratings.selector")
api project(":plaguposter.posts.panel")
} }
} }
jvmMain { jvmMain {

View File

@ -1,28 +1,42 @@
package dev.inmo.plaguposter.triggers.command package dev.inmo.plaguposter.triggers.command
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions 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.pagination.firstPageWithOneElementPagination
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plaguposter.common.SuccessfulSymbol import dev.inmo.plaguposter.common.SuccessfulSymbol
import dev.inmo.plaguposter.common.UnsuccessfulSymbol
import dev.inmo.plaguposter.inlines.models.Format import dev.inmo.plaguposter.inlines.models.Format
import dev.inmo.plaguposter.inlines.models.OfferTemplate import dev.inmo.plaguposter.inlines.models.OfferTemplate
import dev.inmo.plaguposter.inlines.repos.InlineTemplatesRepo import dev.inmo.plaguposter.inlines.repos.InlineTemplatesRepo
import dev.inmo.plaguposter.posts.models.PostId
import dev.inmo.plaguposter.posts.panel.PanelButtonBuilder
import dev.inmo.plaguposter.posts.panel.PanelButtonsAPI
import dev.inmo.plaguposter.posts.repo.PostsRepo import dev.inmo.plaguposter.posts.repo.PostsRepo
import dev.inmo.plaguposter.posts.sending.PostPublisher import dev.inmo.plaguposter.posts.sending.PostPublisher
import dev.inmo.plaguposter.ratings.selector.Selector import dev.inmo.plaguposter.ratings.selector.Selector
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.edit.edit import dev.inmo.tgbotapi.extensions.api.edit.edit
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.BehaviourContextWithFSM import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextWithFSM
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitTextMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitTextMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.strictlyOn import dev.inmo.tgbotapi.extensions.behaviour_builder.strictlyOn
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.botCommandTextSourceOrNull import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.utils.contentMessageOrNull import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.message.textsources.regular import dev.inmo.tgbotapi.types.message.textsources.regular
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
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.jetbrains.exposed.sql.Database
@ -36,13 +50,22 @@ object Plugin : Plugin {
val sourceMessageId: MessageIdentifier, val sourceMessageId: MessageIdentifier,
val messageInReply: MessageIdentifier val messageInReply: MessageIdentifier
) : State ) : State
@Serializable
internal data class Config(
val panelButtonText: String? = "Publish"
)
override fun Module.setupDI(database: Database, params: JsonObject) { override fun Module.setupDI(database: Database, params: JsonObject) {
params["publish_command"] ?.let { configJson ->
single { get<Json>().decodeFromJsonElement(Config.serializer(), configJson) }
}
} }
override suspend fun BehaviourContextWithFSM<State>.setupBotPlugin(koin: Koin) { override suspend fun BehaviourContextWithFSM<State>.setupBotPlugin(koin: Koin) {
val postsRepo = koin.get<PostsRepo>() val postsRepo = koin.get<PostsRepo>()
val publisher = koin.get<PostPublisher>() val publisher = koin.get<PostPublisher>()
val selector = koin.getOrNull<Selector>() val selector = koin.getOrNull<Selector>()
val config = koin.getOrNull<Config>()
val panelApi = koin.getOrNull<PanelButtonsAPI>()
onCommand("publish_post") { onCommand("publish_post") {
val messageInReply = it.replyTo ?.contentMessageOrNull() ?: run { val messageInReply = it.replyTo ?.contentMessageOrNull() ?: run {
@ -87,5 +110,44 @@ object Plugin : Plugin {
) )
) )
} }
panelApi ?.apply {
config ?.panelButtonText ?.let { text ->
add(
PanelButtonBuilder {
CallbackDataInlineKeyboardButton(
text,
"publish ${it.id.string}"
)
}
)
onMessageDataCallbackQuery(
initialFilter = {
it.data.startsWith("publish ")
}
) {
val postId = it.data.removePrefix("publish ").let(::PostId)
val post = postsRepo.getById(postId) ?: return@onMessageDataCallbackQuery
val publishData = uuid4().toString()
val edited = edit(
it.message,
replyMarkup = flatInlineKeyboard {
dataButton(SuccessfulSymbol, publishData)
RootPanelButtonBuilder.buildButton(post) ?.let(::add)
}
)
val pushedButton = waitMessageDataCallbackQuery().first {
it.message.sameMessage(edited)
}
if (pushedButton.data == publishData) {
publisher.publish(postId)
}
}
}
}
} }
} }