diff --git a/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt b/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt index 193aa57..58dbd1a 100644 --- a/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt +++ b/FilesLoaderBot/src/main/kotlin/FilesLoaderBot.kt @@ -2,6 +2,7 @@ import dev.inmo.tgbotapi.extensions.api.files.downloadFile import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia import dev.inmo.tgbotapi.utils.filenameFromUrl @@ -18,6 +19,9 @@ suspend fun main(args: Array) { directoryOrFile.mkdirs() telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) { + onCommand("start") { + reply(it, "Send me any media (like photo or video) to download it") + } onMedia(initialFilter = null) { val pathedFile = bot.getFileAdditionalInfo(it.content.media) val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl) diff --git a/RightsChangerBot/README.md b/RightsChangerBot/README.md new file mode 100644 index 0000000..50b3a00 --- /dev/null +++ b/RightsChangerBot/README.md @@ -0,0 +1,12 @@ +# RightsChanger + +All the commands should be called with reply to some common user. + +* Use `/simple` with bot to get request buttons for non-independent permissions change +* Use `/granular` with bot to get request buttons for independent permissions change + +## Launch + +```bash +../gradlew run --args="BOT_TOKEN allowed_user_id_long" +``` diff --git a/RightsChangerBot/build.gradle b/RightsChangerBot/build.gradle new file mode 100644 index 0000000..1a34bb6 --- /dev/null +++ b/RightsChangerBot/build.gradle @@ -0,0 +1,21 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'application' + +mainClassName="RightsChangerKt" + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} diff --git a/RightsChangerBot/src/main/kotlin/RightsChanger.kt b/RightsChangerBot/src/main/kotlin/RightsChanger.kt new file mode 100644 index 0000000..aa172c9 --- /dev/null +++ b/RightsChangerBot/src/main/kotlin/RightsChanger.kt @@ -0,0 +1,251 @@ +import dev.inmo.tgbotapi.bot.ktor.telegramBot +import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember +import dev.inmo.tgbotapi.extensions.api.chat.members.restrictChatMember +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.buildBehaviourWithLongPolling +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery +import dev.inmo.tgbotapi.extensions.utils.asContentMessage +import dev.inmo.tgbotapi.extensions.utils.asPossiblyReplyMessage +import dev.inmo.tgbotapi.extensions.utils.commonMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.contentMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.extendedGroupChatOrNull +import dev.inmo.tgbotapi.extensions.utils.fromUserMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.restrictedChatMemberOrNull +import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard +import dev.inmo.tgbotapi.extensions.utils.whenMemberChatMember +import dev.inmo.tgbotapi.types.BotCommand +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup +import dev.inmo.tgbotapi.types.chat.ChatPermissions +import dev.inmo.tgbotapi.types.chat.PublicChat +import dev.inmo.tgbotapi.types.commands.BotCommandScope +import dev.inmo.tgbotapi.types.toChatId +import dev.inmo.tgbotapi.utils.row + +suspend fun main(args: Array) { + val botToken = args.first() + + val bot = telegramBot(botToken) + + val allowedAdmin = ChatId(args[1].toLong()) + + fun Boolean?.allowedSymbol() = when (this) { + true -> "✅" + false -> "❌" + null -> "" + } + + val granularDataPrefix = "granular" + val messagesToggleGranularData = "$granularDataPrefix messages" + val otherMessagesToggleGranularData = "$granularDataPrefix other messages" + val audiosToggleGranularData = "$granularDataPrefix audios" + val voicesToggleGranularData = "$granularDataPrefix voices" + val videosToggleGranularData = "$granularDataPrefix videos" + val videoNotesToggleGranularData = "$granularDataPrefix video notes" + val photosToggleGranularData = "$granularDataPrefix photos" + val webPagePreviewToggleGranularData = "$granularDataPrefix web page preview" + val pollsToggleGranularData = "$granularDataPrefix polls" + val documentsToggleGranularData = "$granularDataPrefix documents" + + val commonDataPrefix = "common" + val pollsToggleCommonData = "$commonDataPrefix polls" + val otherMessagesToggleCommonData = "$commonDataPrefix other messages" + val webPagePreviewToggleCommonData = "$commonDataPrefix web page preview" + + suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? { + val chatMember = getChatMember(chatId, userId) + return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember { + getChat(chatId).extendedGroupChatOrNull() ?.permissions + } + } + + suspend fun BehaviourContext.buildGranularKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? { + val permissions = getUserChatPermissions(chatId, userId) ?: return null + + return inlineKeyboard { + row { + dataButton("Send messages${permissions.canSendMessages.allowedSymbol()}", messagesToggleGranularData) + dataButton("Send other messages${permissions.canSendOtherMessages.allowedSymbol()}", otherMessagesToggleGranularData) + } + row { + dataButton("Send audios${permissions.canSendAudios.allowedSymbol()}", audiosToggleGranularData) + dataButton("Send voices${permissions.canSendVoiceNotes.allowedSymbol()}", voicesToggleGranularData) + } + row { + dataButton("Send videos${permissions.canSendVideos.allowedSymbol()}", videosToggleGranularData) + dataButton("Send video notes${permissions.canSendVideoNotes.allowedSymbol()}", videoNotesToggleGranularData) + } + row { + dataButton("Send photos${permissions.canSendPhotos.allowedSymbol()}", photosToggleGranularData) + dataButton("Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}", webPagePreviewToggleGranularData) + } + row { + dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleGranularData) + dataButton("Send documents${permissions.canSendDocuments.allowedSymbol()}", documentsToggleGranularData) + } + } + } + + suspend fun BehaviourContext.buildCommonKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? { + val permissions = getUserChatPermissions(chatId, userId) ?: return null + + return inlineKeyboard { + row { + dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleCommonData) + } + row { + dataButton("Send other messages${permissions.canSendOtherMessages.allowedSymbol()}", otherMessagesToggleCommonData) + } + row { + dataButton("Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}", webPagePreviewToggleCommonData) + } + } + } + + bot.buildBehaviourWithLongPolling { + onCommand("simple", initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull() ?.user ?.id == allowedAdmin }) { + val replyMessage = it.replyTo + val userInReply = replyMessage ?.fromUserMessageOrNull() ?.user ?.id ?: return@onCommand + reply( + replyMessage, + "Manage keyboard:", + replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand + ) + } + onCommand("granular", initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull() ?.user ?.id == allowedAdmin }) { + val replyMessage = it.replyTo + val userInReply = replyMessage ?.fromUserMessageOrNull() ?.user ?.id ?: return@onCommand + reply( + replyMessage, + "Manage keyboard:", + replyMarkup = buildGranularKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand + ) + } + + onMessageDataCallbackQuery( + Regex("^${granularDataPrefix}.*"), + initialFilter = { it.user.id == allowedAdmin } + ) { + val messageReply = it.message.commonMessageOrNull() ?.replyTo ?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery + val userId = messageReply.user.id + val permissions = getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery + val newPermission = when (it.data) { + messagesToggleGranularData -> { + permissions.copyGranular( + canSendMessages = permissions.canSendMessages ?.let { !it } ?: false + ) + } + otherMessagesToggleGranularData -> { + permissions.copyGranular( + canSendOtherMessages = permissions.canSendOtherMessages ?.let { !it } ?: false + ) + } + audiosToggleGranularData -> { + permissions.copyGranular( + canSendAudios = permissions.canSendAudios ?.let { !it } ?: false + ) + } + voicesToggleGranularData -> { + permissions.copyGranular( + canSendVoiceNotes = permissions.canSendVoiceNotes ?.let { !it } ?: false + ) + } + videosToggleGranularData -> { + permissions.copyGranular( + canSendVideos = permissions.canSendVideos ?.let { !it } ?: false + ) + } + videoNotesToggleGranularData -> { + permissions.copyGranular( + canSendVideoNotes = permissions.canSendVideoNotes ?.let { !it } ?: false + ) + } + photosToggleGranularData -> { + permissions.copyGranular( + canSendPhotos = permissions.canSendPhotos ?.let { !it } ?: false + ) + } + webPagePreviewToggleGranularData -> { + permissions.copyGranular( + canAddWebPagePreviews = permissions.canAddWebPagePreviews ?.let { !it } ?: false + ) + } + pollsToggleGranularData -> { + permissions.copyGranular( + canSendPolls = permissions.canSendPolls ?.let { !it } ?: false + ) + } + documentsToggleGranularData -> { + permissions.copyGranular( + canSendDocuments = permissions.canSendDocuments ?.let { !it } ?: false + ) + } + else -> permissions.copyGranular() + } + + restrictChatMember( + it.message.chat.id, + userId, + permissions = newPermission, + useIndependentChatPermissions = true + ) + + edit( + it.message, + replyMarkup = buildGranularKeyboard(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery + ) + } + + onMessageDataCallbackQuery( + Regex("^${commonDataPrefix}.*"), + initialFilter = { it.user.id == allowedAdmin } + ) { + val messageReply = it.message.commonMessageOrNull() ?.replyTo ?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery + val userId = messageReply.user.id + val permissions = getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery + val newPermission = when (it.data) { + pollsToggleCommonData -> { + permissions.copyCommon( + canSendPolls = permissions.canSendPolls ?.let { !it } ?: false + ) + } + otherMessagesToggleCommonData -> { + permissions.copyCommon( + canSendOtherMessages = permissions.canSendOtherMessages ?.let { !it } ?: false + ) + } + webPagePreviewToggleCommonData -> { + permissions.copyCommon( + canAddWebPagePreviews = permissions.canAddWebPagePreviews ?.let { !it } ?: false + ) + } + else -> permissions.copyCommon() + } + + restrictChatMember( + it.message.chat.id, + userId, + permissions = newPermission, + useIndependentChatPermissions = false + ) + + edit( + it.message, + replyMarkup = buildCommonKeyboard(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery + ) + } + + setMyCommands( + BotCommand("simple", "Trigger simple keyboard. Use with reply to user"), + BotCommand("granular", "Trigger granular keyboard. Use with reply to user"), + scope = BotCommandScope.AllGroupChats + ) + }.join() +} diff --git a/UserChatShared/README.md b/UserChatShared/README.md new file mode 100644 index 0000000..8972e2f --- /dev/null +++ b/UserChatShared/README.md @@ -0,0 +1,9 @@ +# UserChatShared + +Use `/start` with bot to get request buttons. Bot will ask you to choose user/chat from your list and send it to him. + +## Launch + +```bash +../gradlew run --args="BOT_TOKEN" +``` diff --git a/UserChatShared/build.gradle b/UserChatShared/build.gradle new file mode 100644 index 0000000..227d7b0 --- /dev/null +++ b/UserChatShared/build.gradle @@ -0,0 +1,21 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin' +apply plugin: 'application' + +mainClassName="UserChatSharedKt" + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} diff --git a/UserChatShared/src/main/kotlin/UserChatShared.kt b/UserChatShared/src/main/kotlin/UserChatShared.kt new file mode 100644 index 0000000..b4c0377 --- /dev/null +++ b/UserChatShared/src/main/kotlin/UserChatShared.kt @@ -0,0 +1,218 @@ +import dev.inmo.micro_utils.coroutines.runCatchingSafely +import dev.inmo.tgbotapi.bot.ktor.telegramBot +import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.send.* +import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared +import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestBotButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestChatButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestGroupButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestUserButton +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestUserOrBotButton +import dev.inmo.tgbotapi.types.BotCommand +import dev.inmo.tgbotapi.types.chat.PrivateChat +import dev.inmo.tgbotapi.types.message.textsources.mention +import dev.inmo.tgbotapi.types.request.RequestId +import dev.inmo.tgbotapi.utils.row + +suspend fun main(args: Array) { + val botToken = args.first() + + val bot = telegramBot(botToken) + + val requestIdUserOrBot = RequestId(0) + val requestIdUserNonPremium = RequestId(1) + val requestIdUserAny = RequestId(2) + val requestIdUserPremium = RequestId(3) + val requestIdBot = RequestId(4) + + val requestIdAnyChat = RequestId(5) + val requestIdChannel = RequestId(6) + val requestIdPublicChannel = RequestId(7) + val requestIdPrivateChannel = RequestId(8) + val requestIdChannelUserOwner = RequestId(9) + + val requestIdGroup = RequestId(10) + val requestIdPublicGroup = RequestId(11) + val requestIdPrivateGroup = RequestId(12) + val requestIdGroupUserOwner = RequestId(13) + + val requestIdForum = RequestId(14) + val requestIdPublicForum = RequestId(15) + val requestIdPrivateForum = RequestId(16) + val requestIdForumUserOwner = RequestId(17) + + val keyboard = replyKeyboard( + resizeKeyboard = true, + ) { + row { + requestUserOrBotButton( + "\uD83D\uDC64/\uD83E\uDD16", + requestIdUserOrBot + ) + } + row { + requestUserButton( + "\uD83D\uDC64☆", + requestIdUserNonPremium, + premiumUser = false + ) + requestUserButton( + "\uD83D\uDC64", + requestIdUserAny, + premiumUser = null + ) + requestUserButton( + "\uD83D\uDC64★", + requestIdUserPremium, + premiumUser = true + ) + requestBotButton( + "\uD83E\uDD16", + requestIdBot + ) + } + row { + requestChatButton( + "\uD83D\uDDE3/\uD83D\uDC65", + requestIdAnyChat + ) + } + row { + requestChatButton( + "\uD83D\uDDE3", + requestIdChannel, + isChannel = true + ) + requestChatButton( + "\uD83D\uDDE3\uD83D\uDD17", + requestIdPublicChannel, + isChannel = true, + isPublic = true + ) + requestChatButton( + "\uD83D\uDDE3❌\uD83D\uDD17", + requestIdPrivateChannel, + isChannel = true, + isPublic = false + ) + requestChatButton( + "\uD83D\uDDE3\uD83D\uDC6E", + requestIdChannelUserOwner, + isChannel = true, + isOwnedBy = true + ) + } + row { + requestGroupButton( + "👥", + requestIdGroup + ) + requestGroupButton( + "👥\uD83D\uDD17", + requestIdPublicGroup, + isPublic = true + ) + requestGroupButton( + "👥❌\uD83D\uDD17", + requestIdPrivateGroup, + isPublic = false + ) + requestGroupButton( + "👥\uD83D\uDC6E", + requestIdGroupUserOwner, + isOwnedBy = true + ) + } + row { + requestGroupButton( + "🏛", + requestIdForum, + isForum = true + ) + requestGroupButton( + "🏛\uD83D\uDD17", + requestIdPublicForum, + isPublic = true, + isForum = true + ) + requestGroupButton( + "🏛❌\uD83D\uDD17", + requestIdPrivateForum, + isPublic = false, + isForum = true + ) + requestGroupButton( + "🏛\uD83D\uDC6E", + requestIdForumUserOwner, + isOwnedBy = true, + isForum = true + ) + } + } + + bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) { + onCommand("start", initialFilter = { it.chat is PrivateChat }) { + reply( + it, + "Here possible requests buttons:", + replyMarkup = keyboard + ) + } + + onUserShared { + val userId = it.chatEvent.userId + val userInfo = runCatchingSafely { getChat(userId) }.getOrNull() + reply( + it, + ) { + +"You have shared " + +mention( + when (it.chatEvent.requestId) { + requestIdUserOrBot -> "user or bot" + requestIdUserNonPremium -> "non premium user" + requestIdUserAny -> "any user" + requestIdUserPremium -> "premium user" + requestIdBot -> "bot" + else -> "somebody O.o" + }, + userId + ) + +" (user info: $userInfo; user id: $userId)" + } + } + + onChatShared { + val chatId = it.chatEvent.chatId + val chatInfo = runCatchingSafely { getChat(chatId) }.getOrNull() + reply( + it, + ) { + +"You have shared " + +when (it.chatEvent.requestId) { + requestIdAnyChat -> "some chat" + requestIdChannel -> "any channel" + requestIdPublicChannel -> "public channel" + requestIdPrivateChannel -> "private channel" + requestIdChannelUserOwner -> "channel owned by you" + requestIdGroup -> "any group" + requestIdPublicGroup -> "public group" + requestIdPrivateGroup -> "private group" + requestIdGroupUserOwner -> "group owned by you" + requestIdForum -> "any forum" + requestIdPublicForum -> "public forum" + requestIdPrivateForum -> "private forum" + requestIdForumUserOwner -> "forum owned by you" + else -> "some chat O.o" + } + +" (chat info: $chatInfo; chat id: $chatId)" + } + } + + setMyCommands(BotCommand("start", "Trigger buttons")) + }.join() +} diff --git a/gradle.properties b/gradle.properties index 3a54f45..088dac6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx768m kotlin_version=1.7.22 -telegram_bot_api_version=5.0.1 -micro_utils_version=0.16.6 +telegram_bot_api_version=5.1.0 +micro_utils_version=0.16.8 serialization_version=1.4.1 -ktor_version=2.2.2 +ktor_version=2.2.3 diff --git a/settings.gradle b/settings.gradle index 5bad889..262e635 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,3 +28,7 @@ include ":WebApp" include ":FSMBot" include ":TopicsHandling" + +include ":UserChatShared" + +include ":RightsChangerBot"