From 5c3128e58ade47342cedd0dcfee72a137b3dac58 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 17 Feb 2021 19:24:28 +0600 Subject: [PATCH] first version --- build.gradle | 12 +- gradle.properties | 7 +- .../captcha/AuthorizedUserChatPermissions.kt | 14 +++ .../inmo/plagubot/plugins/captcha/Plugin.kt | 109 ++++++++++++++++++ .../captcha/SlotMachineAnswersMarkup.kt | 29 +++++ .../captcha/db/CaptchaChatsSettingsRepo.kt | 56 +++++++++ .../plugins/captcha/settings/ChatSettings.kt | 17 +++ src/main/kotlin/plagubot_plugin/Plugin.kt | 19 --- 8 files changed, 237 insertions(+), 26 deletions(-) create mode 100644 src/main/kotlin/dev/inmo/plagubot/plugins/captcha/AuthorizedUserChatPermissions.kt create mode 100644 src/main/kotlin/dev/inmo/plagubot/plugins/captcha/Plugin.kt create mode 100644 src/main/kotlin/dev/inmo/plagubot/plugins/captcha/SlotMachineAnswersMarkup.kt create mode 100644 src/main/kotlin/dev/inmo/plagubot/plugins/captcha/db/CaptchaChatsSettingsRepo.kt create mode 100644 src/main/kotlin/dev/inmo/plagubot/plugins/captcha/settings/ChatSettings.kt delete mode 100644 src/main/kotlin/plagubot_plugin/Plugin.kt diff --git a/build.gradle b/build.gradle index 5523253..6d04c9b 100644 --- a/build.gradle +++ b/build.gradle @@ -12,12 +12,15 @@ buildscript { plugins { id 'org.jetbrains.kotlin.jvm' version "$kotlin_version" id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" - id 'application' } +project.group = "$project_group" +project.version = "$project_version" + repositories { - jcenter() + mavenLocal() mavenCentral() + jcenter() } dependencies { @@ -26,8 +29,5 @@ dependencies { api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_runtime_version" api "dev.inmo:plagubot.plugin:$plagubot_version" -} - -application { - mainClassName = 'telegram_bot.AppKt' + api "dev.inmo:micro_utils.repos.exposed:$micro_utils_version" } diff --git a/gradle.properties b/gradle.properties index 674cb28..461e7bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,9 @@ kotlin.incremental=true kotlin_version=1.4.30 kotlin_coroutines_version=1.4.2 kotlin_serialisation_runtime_version=1.1.0-RC -plagubot_version=0.1.0 +plagubot_version=0.1.1 + +micro_utils_version=0.4.25 + +project_group=dev.inmo +project_version=0.1.1 diff --git a/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/AuthorizedUserChatPermissions.kt b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/AuthorizedUserChatPermissions.kt new file mode 100644 index 0000000..89ad611 --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/AuthorizedUserChatPermissions.kt @@ -0,0 +1,14 @@ +package dev.inmo.plagubot.plugins.captcha + +import dev.inmo.tgbotapi.types.chat.ChatPermissions + +val authorizedUserChatPermissions = ChatPermissions( + canSendMessages = true, + canSendMediaMessages = true, + canSendPolls = true, + canSendOtherMessages = true, + canAddWebPagePreviews = true, + canChangeInfo = true, + canInviteUsers = true, + canPinMessages = true, +) diff --git a/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/Plugin.kt b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/Plugin.kt new file mode 100644 index 0000000..438cd96 --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/Plugin.kt @@ -0,0 +1,109 @@ +package dev.inmo.plagubot.plugins.captcha + +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.micro_utils.repos.create +import dev.inmo.plagubot.Plugin +import dev.inmo.plagubot.plugins.captcha.db.CaptchaChatsSettingsRepo +import dev.inmo.plagubot.plugins.captcha.settings.ChatSettings +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.answers.answerCallbackQuery +import dev.inmo.tgbotapi.extensions.api.chat.members.* +import dev.inmo.tgbotapi.extensions.api.send.media.reply +import dev.inmo.tgbotapi.extensions.api.send.sendDice +import dev.inmo.tgbotapi.extensions.api.send.sendTextMessage +import dev.inmo.tgbotapi.extensions.behaviour_builder.* +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitBaseInlineQuery +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDataCallbackQuery +import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onNewChatMembers +import dev.inmo.tgbotapi.extensions.utils.* +import dev.inmo.tgbotapi.extensions.utils.formatting.buildEntities +import dev.inmo.tgbotapi.extensions.utils.formatting.regular +import dev.inmo.tgbotapi.types.MessageEntity.textsources.mention +import dev.inmo.tgbotapi.types.User +import dev.inmo.tgbotapi.types.chat.ChatPermissions +import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.toList +import kotlinx.serialization.Serializable +import org.jetbrains.exposed.sql.Database + +@Serializable +class CaptchaBotPlugin : Plugin { + override suspend fun BehaviourContext.invoke( + database: Database, + params: Map + ) { + val repo = CaptchaChatsSettingsRepo(database) + onNewChatMembers( + additionalFilter = { + it.chat.asPublicChat() != null + } + ) { + val eventDateTime = it.date + val chat = it.chat.requirePublicChat() + val newUsers = it.chatEvent.members + newUsers.forEach { user -> + restrictChatMember( + chat, + user, + permissions = ChatPermissions() + ) + } + val settings = repo.getById(it.chat.id) ?: repo.create(ChatSettings(it.chat.id)).firstOrNull() ?: return@onNewChatMembers + val userBanDateTime = eventDateTime + settings.checkTimeSpan + val authorized = Channel(newUsers.size) + val subContexts = newUsers.map { + doInSubContext { + val sentMessage = sendTextMessage( + chat, + buildEntities { + +it.mention(it.firstName) + regular(settings.captchaText) + } + ) + val sentDice = sendDice( + sentMessage.chat, + SlotMachineDiceAnimationType, + replyToMessageId = sentMessage.messageId, + replyMarkup = slotMachineReplyMarkup() + ) + val reels = sentDice.content.dice.calculateSlotMachineResult()!! + val leftToClick = mutableListOf( + reels.left.asSlotMachineReelImage.text, + reels.center.asSlotMachineReelImage.text, + reels.right.asSlotMachineReelImage.text + ) + + launch { + while (leftToClick.isNotEmpty()) { + val userClicked = waitDataCallbackQuery { if (user.id == it.id) this else null }.first() + answerCallbackQuery(userClicked, "⏳") + if (userClicked.data == leftToClick.first()) { + leftToClick.removeAt(0) + } + } + authorized.send(it) + safelyWithoutExceptions { restrictChatMember(chat, it, permissions = authorizedUserChatPermissions) } + stop() + } + + this to it + } + } + + delay((userBanDateTime - eventDateTime).millisecondsLong) + + val authorizedUsers = authorized.toList() + authorized.close() + + subContexts.forEach { (context, user) -> + if (user !in authorizedUsers) { + context.stop() + safelyWithoutExceptions { kickChatMember(chat, user) } + } + } + } + } +} diff --git a/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/SlotMachineAnswersMarkup.kt b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/SlotMachineAnswersMarkup.kt new file mode 100644 index 0000000..a0e9e6b --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/SlotMachineAnswersMarkup.kt @@ -0,0 +1,29 @@ +package dev.inmo.plagubot.plugins.captcha + +import dev.inmo.tgbotapi.extensions.utils.SlotMachineReelImage +import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton +import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup + +infix fun String.startingOf(target: String) = target.startsWith(this) + +fun slotMachineReplyMarkup( + first: String? = null, + second: String? = null, + third: String? = null, +): InlineKeyboardMarkup { + val texts = when { + first == null -> SlotMachineReelImage.values().map { + CallbackDataInlineKeyboardButton("${it.text}**", it.text) + } + second == null -> SlotMachineReelImage.values().map { + CallbackDataInlineKeyboardButton("$first${it.text}*", it.text) + } + third == null -> SlotMachineReelImage.values().map { + CallbackDataInlineKeyboardButton("$first$second${it.text}", it.text) + } + else -> listOf(CallbackDataInlineKeyboardButton("$first$second$third", "$first$second$third")) + } + return InlineKeyboardMarkup( + texts.chunked(2) + ) +} diff --git a/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/db/CaptchaChatsSettingsRepo.kt b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/db/CaptchaChatsSettingsRepo.kt new file mode 100644 index 0000000..3e1a894 --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/db/CaptchaChatsSettingsRepo.kt @@ -0,0 +1,56 @@ +package dev.inmo.plagubot.plugins.captcha.db + +import dev.inmo.micro_utils.repos.exposed.AbstractExposedCRUDRepo +import dev.inmo.micro_utils.repos.exposed.ExposedCRUDRepo +import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo +import dev.inmo.plagubot.plugins.captcha.settings.* +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.toChatId +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.statements.InsertStatement +import org.jetbrains.exposed.sql.statements.UpdateStatement + +class CaptchaChatsSettingsRepo( + override val database: Database +) : AbstractExposedCRUDRepo( + tableName = "CaptchaChatsSettingsRepo" +) { + private val chatIdColumn = long("chatId") + private val checkTimeSecondsColumn = integer("checkTime") + private val solveCaptchaTextColumn = text("checkTime") + + override val primaryKey = PrimaryKey(chatIdColumn) + + override val selectByIds: SqlExpressionBuilder.(List) -> Op = { + chatIdColumn.inList(it.map { it.chatId }) + } + override val InsertStatement.asObject: ChatSettings + get() = TODO("Not yet implemented") + + override fun insert(value: ChatSettings, it: InsertStatement) { + it[chatIdColumn] = value.chatId.chatId + it[checkTimeSecondsColumn] = value.checkTime + it[solveCaptchaTextColumn] = value.captchaText + } + + override fun update(id: ChatId, value: ChatSettings, it: UpdateStatement) { + if (id.chatId == value.chatId.chatId) { + it[checkTimeSecondsColumn] = value.checkTime + it[solveCaptchaTextColumn] = value.captchaText + } + } + + override fun InsertStatement.asObject(value: ChatSettings): ChatSettings = ChatSettings( + get(chatIdColumn).toChatId(), + get(checkTimeSecondsColumn), + get(solveCaptchaTextColumn) + ) + + override val selectById: SqlExpressionBuilder.(ChatId) -> Op = { chatIdColumn.eq(it.chatId) } + override val ResultRow.asObject: ChatSettings + get() = ChatSettings( + get(chatIdColumn).toChatId(), + get(checkTimeSecondsColumn), + get(solveCaptchaTextColumn) + ) +} diff --git a/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/settings/ChatSettings.kt b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/settings/ChatSettings.kt new file mode 100644 index 0000000..69d1334 --- /dev/null +++ b/src/main/kotlin/dev/inmo/plagubot/plugins/captcha/settings/ChatSettings.kt @@ -0,0 +1,17 @@ +package dev.inmo.plagubot.plugins.captcha.settings + +import com.soywiz.klock.TimeSpan +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.Seconds +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class ChatSettings( + val chatId: ChatId, + val checkTime: Seconds = 60, + val captchaText: String = "solve next captcha:" +) { + @Transient + val checkTimeSpan = TimeSpan(checkTime * 1000.0) +} diff --git a/src/main/kotlin/plagubot_plugin/Plugin.kt b/src/main/kotlin/plagubot_plugin/Plugin.kt deleted file mode 100644 index 90b7839..0000000 --- a/src/main/kotlin/plagubot_plugin/Plugin.kt +++ /dev/null @@ -1,19 +0,0 @@ -package plagubot_plugin - -import dev.inmo.plagubot.Plugin -import dev.inmo.tgbotapi.bot.TelegramBot -import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter -import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.serialization.Serializable -import org.jetbrains.exposed.sql.Database - -@Serializable -class PlaguBotPlugin : Plugin { - override suspend fun BehaviourContext.invoke( - database: Database, - params: Map - ) { - TODO("Not yet implemented") - } -}