package dev.inmo.plagubot.plugins.captcha import dev.inmo.micro_utils.coroutines.* 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.deleteMessage import dev.inmo.tgbotapi.extensions.api.edit.ReplyMarkup.editMessageReplyMarkup 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.extensions.utils.shortcuts.executeUnsafe import dev.inmo.tgbotapi.requests.DeleteMessage 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.chat.LeftRestrictionsChatPermissions import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType import dev.inmo.tgbotapi.types.message.abstracts.Message 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 }, includeFilterByChatInBehaviourSubContext = false ) { safelyWithoutExceptions { deleteMessage(it) } 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 messagesToDelete = Channel(Channel.UNLIMITED) val subContexts = newUsers.map { doInSubContext(stopOnCompletion = false) { val sentMessage = sendTextMessage( chat, buildEntities { +it.mention(it.firstName) regular(", ${settings.captchaText}") } ).also { messagesToDelete.send(it) } val sentDice = sendDice( sentMessage.chat, SlotMachineDiceAnimationType, replyToMessageId = sentMessage.messageId, replyMarkup = slotMachineReplyMarkup() ).also { messagesToDelete.send(it) } val reels = sentDice.content.dice.calculateSlotMachineResult()!! val leftToClick = mutableListOf( reels.left.asSlotMachineReelImage.text, reels.center.asSlotMachineReelImage.text, reels.right.asSlotMachineReelImage.text ) launch { val clicked = arrayOf(null, null, null) while (leftToClick.isNotEmpty()) { val userClicked = waitDataCallbackQuery { if (user.id == it.id) this else null }.first() if (userClicked.data == leftToClick.first()) { clicked[3 - leftToClick.size] = leftToClick.removeAt(0) if (clicked.contains(null)) { safelyWithoutExceptions { answerCallbackQuery(userClicked, "Ok, next one") } editMessageReplyMarkup(sentDice, slotMachineReplyMarkup(clicked[0], clicked[1], clicked[2])) } else { safelyWithoutExceptions { answerCallbackQuery(userClicked, "Thank you and welcome", showAlert = true) } safelyWithoutExceptions { deleteMessage(sentMessage) } safelyWithoutExceptions { deleteMessage(sentDice) } } } else { safelyWithoutExceptions { answerCallbackQuery(userClicked, "Nope") } } } authorized.send(it) safelyWithoutExceptions { restrictChatMember(chat, it, permissions = LeftRestrictionsChatPermissions) } stop() } this to it } } delay((userBanDateTime - eventDateTime).millisecondsLong) authorized.close() val authorizedUsers = authorized.toList() subContexts.forEach { (context, user) -> if (user !in authorizedUsers) { context.stop() safelyWithoutExceptions { kickChatMember(chat, user) } } } messagesToDelete.close() for (message in messagesToDelete) { executeUnsafe(DeleteMessage(message.chat.id, message.messageId), retries = 0) } } } }