Compare commits

...

6 Commits

6 changed files with 160 additions and 81 deletions

View File

@ -51,4 +51,5 @@ dependencies {
api "dev.inmo:plagubot.plugin:$plagubot_version"
api "dev.inmo:micro_utils.repos.exposed:$micro_utils_version"
api "dev.inmo:tgbotapi.libraries.cache.admins.plagubot:$tgbotapi_libraries_version"
api "dev.inmo:plagubot.plugins.commands:$commands_version"
}

View File

@ -7,10 +7,11 @@ kotlin.incremental=true
kotlin_version=1.6.21
kotlin_coroutines_version=1.6.4
kotlin_serialisation_runtime_version=1.3.3
plagubot_version=1.3.1
plagubot_version=1.4.0
micro_utils_version=0.11.13
tgbotapi_libraries_version=0.3.1
tgbotapi_libraries_version=0.4.1
commands_version=0.2.0
project_group=dev.inmo
project_version=0.1.6

View File

@ -1,13 +1,16 @@
package dev.inmo.plagubot.plugins.captcha
import com.benasher44.uuid.uuid4
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.provider.*
import dev.inmo.plagubot.plugins.captcha.settings.ChatSettings
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.plagubot.plugins.commands.BotCommandFullInfo
import dev.inmo.plagubot.plugins.commands.CommandsKeeperKey
import dev.inmo.tgbotapi.extensions.api.chat.members.*
import dev.inmo.tgbotapi.extensions.api.delete
import dev.inmo.tgbotapi.extensions.api.deleteMessage
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
@ -16,13 +19,16 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onNewCha
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.libraries.cache.admins.*
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import kotlinx.coroutines.*
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.core.qualifier.named
private const val enableAutoDeleteCommands = "captcha_auto_delete_commands_on"
private const val disableAutoDeleteCommands = "captcha_auto_delete_commands_off"
@ -45,55 +51,76 @@ private val changeCaptchaMethodCommandRegex = Regex(
@Serializable
class CaptchaBotPlugin : Plugin {
// override suspend fun getCommands(): List<BotCommand> = listOf(
// BotCommand(
// enableAutoDeleteCommands,
// "Enable auto removing of commands addressed to captcha plugin"
// ),
// BotCommand(
// disableAutoDeleteCommands,
// "Disable auto removing of commands addressed to captcha plugin"
// ),
// BotCommand(
// enableAutoDeleteServiceMessages,
// "Enable auto removing of users joined messages"
// ),
// BotCommand(
// disableAutoDeleteServiceMessages,
// "Disable auto removing of users joined messages"
// ),
// BotCommand(
// enableSlotMachineCaptcha,
// "Change captcha method to slot machine"
// ),
// BotCommand(
// enableSimpleCaptcha,
// "Change captcha method to simple button"
// ),
// BotCommand(
// disableCaptcha,
// "Disable captcha for chat"
// ),
// BotCommand(
// enableCaptcha,
// "Enable captcha for chat"
// ),
// BotCommand(
// enableExpressionCaptcha,
// "Change captcha method to expressions"
// ),
// BotCommand(
// enableKickOnUnsuccess,
// "Not solved captcha users will be kicked from the chat"
// ),
// BotCommand(
// disableKickOnUnsuccess,
// "Not solved captcha users will NOT be kicked from the chat"
// )
// )
override fun Module.setupDI(database: Database, params: JsonObject) {
single { CaptchaChatsSettingsRepo(database) }
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableAutoDeleteCommands, "Enable auto removing of commands addressed to captcha plugin")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(disableAutoDeleteCommands, "Disable auto removing of commands addressed to captcha plugin")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableAutoDeleteServiceMessages, "Enable auto removing of users joined messages")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(disableAutoDeleteServiceMessages, "Disable auto removing of users joined messages")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableSlotMachineCaptcha, "Change captcha method to slot machine")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableSimpleCaptcha, "Change captcha method to simple button")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(disableCaptcha, "Disable captcha for chat")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableCaptcha, "Enable captcha for chat")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableExpressionCaptcha, "Change captcha method to expressions")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(enableKickOnUnsuccess, "Not solved captcha users will be kicked from the chat")
)
}
single(named(uuid4().toString())) {
BotCommandFullInfo(
CommandsKeeperKey(BotCommandScope.AllChatAdministrators),
BotCommand(disableKickOnUnsuccess, "Not solved captcha users will NOT be kicked from the chat")
)
}
}
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
@ -124,10 +151,10 @@ class CaptchaBotPlugin : Plugin {
permissions = RestrictionsChatPermissions
)
}
val defaultChatPermissions = (getChat(it.chat) as ExtendedGroupChat).permissions
val defaultChatPermissions = LeftRestrictionsChatPermissions
with (settings.captchaProvider) {
doAction(it.date, chat, newUsers, defaultChatPermissions, adminsAPI)
doAction(it.date, chat, newUsers, defaultChatPermissions, adminsAPI, settings.kickOnUnsuccess)
}
}
@ -262,6 +289,50 @@ class CaptchaBotPlugin : Plugin {
}
}
}
onCommand(enableKickOnUnsuccess) { message ->
message.doAfterVerification(adminsAPI) {
val settings = message.chat.settings()
repo.update(
message.chat.id,
settings.copy(kickOnUnsuccess = true)
)
reply(message, "Ok, new users didn't passed captcha will be kicked").apply {
launchSafelyWithoutExceptions {
delay(5000L)
delete(this@apply)
}
}
if (settings.autoRemoveCommands) {
deleteMessage(message)
}
}
}
onCommand(disableKickOnUnsuccess) { message ->
message.doAfterVerification(adminsAPI) {
val settings = message.chat.settings()
repo.update(
message.chat.id,
settings.copy(kickOnUnsuccess = false)
)
reply(message, "Ok, new users didn't passed captcha will NOT be kicked").apply {
launchSafelyWithoutExceptions {
delay(5000L)
delete(this@apply)
}
}
if (settings.autoRemoveCommands) {
deleteMessage(message)
}
}
}
}
}
}

View File

@ -1,6 +1,7 @@
package dev.inmo.plagubot.plugins.captcha
import dev.inmo.tgbotapi.extensions.utils.SlotMachineReelImage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.CallbackDataInlineKeyboardButton
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
@ -23,7 +24,10 @@ fun slotMachineReplyMarkup(
}
else -> listOf(CallbackDataInlineKeyboardButton("$first$second$third", "$first$second$third"))
}
return InlineKeyboardMarkup(
texts.chunked(2)
)
return inlineKeyboard {
texts.chunked(2).forEach { add(it) }
// row {
// dataButton("Cancel (Admins only)", "cancel")
// }
}
}

View File

@ -43,7 +43,8 @@ sealed class CaptchaProvider {
chat: GroupChat,
newUsers: List<User>,
leftRestrictionsPermissions: ChatPermissions,
adminsApi: AdminsCacheAPI?
adminsApi: AdminsCacheAPI?,
kickOnUnsuccess: Boolean
)
}
@ -113,7 +114,8 @@ data class SlotMachineCaptchaProvider(
chat: GroupChat,
newUsers: List<User>,
leftRestrictionsPermissions: ChatPermissions,
adminsApi: AdminsCacheAPI?
adminsApi: AdminsCacheAPI?,
kickOnUnsuccess: Boolean
) {
val userBanDateTime = eventDateTime + checkTimeSpan
val authorized = Channel<User>(newUsers.size)
@ -144,18 +146,20 @@ data class SlotMachineCaptchaProvider(
val clicked = arrayOf<String?>(null, null, null)
while (leftToClick.isNotEmpty()) {
val userClicked = waitMessageDataCallbackQuery().filter { it.user.id == user.id && it.message.messageId == sentDice.messageId }.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) }
when {
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") }
else -> safelyWithoutExceptions { answerCallbackQuery(userClicked, "Nope") }
}
}
authorized.send(user)
@ -175,7 +179,7 @@ data class SlotMachineCaptchaProvider(
subContexts.forEach { (context, user) ->
if (user !in authorizedUsers) {
context.stop()
if (kick) {
if (kickOnUnsuccess) {
banUser(chat, user, leftRestrictionsPermissions)
}
}
@ -191,8 +195,7 @@ data class SlotMachineCaptchaProvider(
data class SimpleCaptchaProvider(
val checkTimeSeconds: Seconds = 60,
val captchaText: String = "press this button to pass captcha:",
val buttonText: String = "Press me\uD83D\uDE0A",
val kick: Boolean = true
val buttonText: String = "Press me\uD83D\uDE0A"
) : CaptchaProvider() {
@Transient
private val checkTimeSpan = checkTimeSeconds.seconds
@ -202,7 +205,8 @@ data class SimpleCaptchaProvider(
chat: GroupChat,
newUsers: List<User>,
leftRestrictionsPermissions: ChatPermissions,
adminsApi: AdminsCacheAPI?
adminsApi: AdminsCacheAPI?,
kickOnUnsuccess: Boolean
) {
val userBanDateTime = eventDateTime + checkTimeSpan
newUsers.map { user ->
@ -257,7 +261,7 @@ data class SimpleCaptchaProvider(
if (job.isActive) {
job.cancel()
if (kick) {
if (kickOnUnsuccess) {
banUser(chat, user, leftRestrictionsPermissions)
}
}
@ -316,8 +320,7 @@ data class ExpressionCaptchaProvider(
val maxPerNumber: Int = 10,
val operations: Int = 2,
val answers: Int = 6,
val attempts: Int = 3,
val kick: Boolean = true
val attempts: Int = 3
) : CaptchaProvider() {
@Transient
private val checkTimeSpan = checkTimeSeconds.seconds
@ -327,7 +330,8 @@ data class ExpressionCaptchaProvider(
chat: GroupChat,
newUsers: List<User>,
leftRestrictionsPermissions: ChatPermissions,
adminsApi: AdminsCacheAPI?
adminsApi: AdminsCacheAPI?,
kickOnUnsuccess: Boolean
) {
val userBanDateTime = eventDateTime + checkTimeSpan
newUsers.map { user ->
@ -376,12 +380,9 @@ data class ExpressionCaptchaProvider(
if (passed == null) {
removeRedundantMessages()
passed = it
if (it) {
safelyWithoutExceptions { restrictChatMember(chat, user, permissions = leftRestrictionsPermissions) }
} else {
if (kick) {
banUser(chat, user, leftRestrictionsPermissions)
}
when {
it -> safelyWithoutExceptions { restrictChatMember(chat, user, permissions = leftRestrictionsPermissions) }
kickOnUnsuccess -> banUser(chat, user, leftRestrictionsPermissions)
}
}
}

View File

@ -11,5 +11,6 @@ data class ChatSettings(
val captchaProvider: CaptchaProvider = SimpleCaptchaProvider(),
val autoRemoveCommands: Boolean = false,
val autoRemoveEvents: Boolean = true,
val kickOnUnsuccess: Boolean = true,
val enabled: Boolean = true
)