From 20b2ae81759e29c3786c96ff7a4dd83ecb3a0074 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 13 Apr 2026 16:01:06 +0600 Subject: [PATCH 1/8] update dependencies --- gradle.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index ef92a94..0041c82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.jvmargs=-Xmx3148m kotlin.daemon.jvmargs=-Xmx3g -Xms500m -kotlin_version=2.2.21 -telegram_bot_api_version=31.0.0 -micro_utils_version=0.26.9 -serialization_version=1.9.0 -ktor_version=3.3.2 -compose_version=1.8.2 +kotlin_version=2.3.20 +telegram_bot_api_version=33.0.0 +micro_utils_version=0.29.1 +serialization_version=1.10.0 +ktor_version=3.4.1 +compose_version=1.10.2 From b4e2d52e7e10877834c3798669093f2a4b784c4a Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 14 Apr 2026 16:31:26 +0600 Subject: [PATCH 2/8] add managedbotsbot sample --- ManagedBotsBot/build.gradle | 21 +++++ .../src/main/kotlin/ManagedBotsBot.kt | 82 +++++++++++++++++++ settings.gradle | 2 + 3 files changed, 105 insertions(+) create mode 100644 ManagedBotsBot/build.gradle create mode 100644 ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt diff --git a/ManagedBotsBot/build.gradle b/ManagedBotsBot/build.gradle new file mode 100644 index 0000000..fdbd23c --- /dev/null +++ b/ManagedBotsBot/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="CustomBotKt" + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} diff --git a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt new file mode 100644 index 0000000..7dfaed3 --- /dev/null +++ b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt @@ -0,0 +1,82 @@ +import dev.inmo.kslog.common.KSLog +import dev.inmo.kslog.common.LogLevel +import dev.inmo.kslog.common.defaultMessageFormatter +import dev.inmo.kslog.common.setDefaultKSLog +import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData +import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction +import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +import dev.inmo.tgbotapi.types.update.abstracts.Update +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +private var BehaviourContextData.update: Update? + get() = get("update") as? Update + set(value) = set("update", value) + +private var BehaviourContextData.commonMessage: CommonMessage<*>? + get() = get("commonMessage") as? CommonMessage<*> + set(value) = set("commonMessage", value) + +/** + * This place can be the playground for your code. + */ +suspend fun main(vararg args: String) { + val botToken = args.first() + + val isDebug = args.any { it == "debug" } + val isTestServer = args.any { it == "testServer" } + + if (isDebug) { + setDefaultKSLog( + KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? -> + println(defaultMessageFormatter(level, tag, message, throwable)) + } + ) + } + + telegramBotWithBehaviourAndLongPolling( + botToken, + CoroutineScope(Dispatchers.IO), + testServer = isTestServer, + builder = { + includeMiddlewares { + addMiddleware { + doOnRequestReturnResult { result, request, _ -> + println("Result of $request:\n\n$result") + null + } + } + } + }, + subcontextInitialAction = buildSubcontextInitialAction { + add { + data.update = it + } + } + ) { + // start here!! + val me = getMe() + println(me) + + onCommand("start") { + println(data.update) + println(data.commonMessage) + println(getChat(it.chat)) + } + + onCommand("canManageBots") { + val me = getMe() + reply(it, if (me.canManageBots) "Yes" else "No") + } + + allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { + println(it) + } + }.second.join() +} diff --git a/settings.gradle b/settings.gradle index a967762..5309208 100644 --- a/settings.gradle +++ b/settings.gradle @@ -69,3 +69,5 @@ include ":DraftsBot" include ":GiftsBot" include ":TagsBot" + +include ":ManagedBotsBot" From 8268cd9bf4a5a6afdaf6e1ad3f58ff4e5efa385e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 14 Apr 2026 18:12:25 +0600 Subject: [PATCH 3/8] add showing of request managed bot --- .../src/main/kotlin/ManagedBotsBot.kt | 28 +++++++++++++++++++ gradle.properties | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt index 7dfaed3..2e8d197 100644 --- a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt +++ b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt @@ -10,7 +10,14 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatReplyKeyboard +import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard +import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestManagedBotButton +import dev.inmo.tgbotapi.types.Username +import dev.inmo.tgbotapi.types.buttons.KeyboardButtonRequestManagedBot +import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButtonId import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage +import dev.inmo.tgbotapi.types.request.RequestId import dev.inmo.tgbotapi.types.update.abstracts.Update import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -75,6 +82,27 @@ suspend fun main(vararg args: String) { reply(it, if (me.canManageBots) "Yes" else "No") } + val requestId = RequestId(0) + onCommand("keyboard") { + reply( + it, + "Keyboard", + replyMarkup = flatReplyKeyboard( + resizeKeyboard = true, + oneTimeKeyboard = true, + ) { + requestManagedBotButton( + "Add managed bot", + KeyboardButtonRequestManagedBot( + requestId = requestId, + suggestedName = "SampleName", + suggestedUsername = Username("@some_sample_bot") + ) + ) + } + ) + } + allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) } diff --git a/gradle.properties b/gradle.properties index 0041c82..94816ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.3.20 -telegram_bot_api_version=33.0.0 +telegram_bot_api_version=33.0.0-t1 micro_utils_version=0.29.1 serialization_version=1.10.0 ktor_version=3.4.1 From 9903e0e32314f24adeb6f5bd1d32f749035e7e3d Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 14 Apr 2026 18:19:57 +0600 Subject: [PATCH 4/8] add more infos --- .../src/main/kotlin/ManagedBotsBot.kt | 25 ++++++++++++++++++- gradle.properties | 2 +- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt index 2e8d197..f2fb161 100644 --- a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt +++ b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt @@ -2,14 +2,21 @@ import dev.inmo.kslog.common.KSLog import dev.inmo.kslog.common.LogLevel import dev.inmo.kslog.common.defaultMessageFormatter import dev.inmo.kslog.common.setDefaultKSLog +import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.managed_bots.getManagedBotToken +import dev.inmo.tgbotapi.extensions.api.managed_bots.replaceManagedBotToken import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction 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.onManagedBotCreated +import dev.inmo.tgbotapi.extensions.utils.chatEventMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.groupContentMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.managedBotCreatedOrNull import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatReplyKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestManagedBotButton @@ -18,6 +25,7 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardButtonRequestManagedBot import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButtonId import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.request.RequestId +import dev.inmo.tgbotapi.types.toChatId import dev.inmo.tgbotapi.types.update.abstracts.Update import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -103,7 +111,22 @@ suspend fun main(vararg args: String) { ) } - allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { + onManagedBotCreated { + reply(it, "Managed bot created successfully: ${it.chatEvent.bot}") + val token = getManagedBotToken( + it.chatEvent.bot.id.toChatId() + ) + reply(it, "Token: $token") + } + + onCommand("replaceToken") { + val reply = it.replyTo ?.chatEventMessageOrNull() ?: return@onCommand + val managedBotCreated = reply.chatEvent.managedBotCreatedOrNull() ?: return@onCommand + + reply(it, "Token: ${replaceManagedBotToken(managedBotCreated.bot.id.toChatId())}") + } + + allUpdatesFlow.subscribeLoggingDropExceptions(this) { println(it) } }.second.join() diff --git a/gradle.properties b/gradle.properties index 94816ec..0041c82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.3.20 -telegram_bot_api_version=33.0.0-t1 +telegram_bot_api_version=33.0.0 micro_utils_version=0.29.1 serialization_version=1.10.0 ktor_version=3.4.1 From 9746a068b7f4282dfc72ab8698c4b634774ab4af Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 14 Apr 2026 19:58:42 +0600 Subject: [PATCH 5/8] update ManagedBotsBot --- ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt index f2fb161..191a850 100644 --- a/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt +++ b/ManagedBotsBot/src/main/kotlin/ManagedBotsBot.kt @@ -9,11 +9,13 @@ import dev.inmo.tgbotapi.extensions.api.chat.get.getChat import dev.inmo.tgbotapi.extensions.api.managed_bots.getManagedBotToken import dev.inmo.tgbotapi.extensions.api.managed_bots.replaceManagedBotToken import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction 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.onManagedBotCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onManagedBotUpdated import dev.inmo.tgbotapi.extensions.utils.chatEventMessageOrNull import dev.inmo.tgbotapi.extensions.utils.groupContentMessageOrNull import dev.inmo.tgbotapi.extensions.utils.managedBotCreatedOrNull @@ -119,11 +121,19 @@ suspend fun main(vararg args: String) { reply(it, "Token: $token") } + onManagedBotUpdated { + send(it.user, "Managed bot has been updated: ${it.bot}") + val token = getManagedBotToken( + it.bot.id.toChatId() + ) + send(it.user, "Token: $token") + } + onCommand("replaceToken") { val reply = it.replyTo ?.chatEventMessageOrNull() ?: return@onCommand val managedBotCreated = reply.chatEvent.managedBotCreatedOrNull() ?: return@onCommand - reply(it, "Token: ${replaceManagedBotToken(managedBotCreated.bot.id.toChatId())}") + reply(it, "Token in replace update: ${replaceManagedBotToken(managedBotCreated.bot.id.toChatId())}") } allUpdatesFlow.subscribeLoggingDropExceptions(this) { From 514d9d68b8efb2e4c8663c3825687fd65a4d359c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 15 Apr 2026 15:37:50 +0600 Subject: [PATCH 6/8] add checks of save button and other fixes --- .../commonMain/kotlin/PreparedSampleData.kt | 6 ++ WebApp/src/jsMain/kotlin/main.kt | 74 ++++++++++++++++--- WebApp/src/jvmMain/kotlin/WebAppServer.kt | 38 ++++++++++ 3 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 WebApp/src/commonMain/kotlin/PreparedSampleData.kt diff --git a/WebApp/src/commonMain/kotlin/PreparedSampleData.kt b/WebApp/src/commonMain/kotlin/PreparedSampleData.kt new file mode 100644 index 0000000..16d53d2 --- /dev/null +++ b/WebApp/src/commonMain/kotlin/PreparedSampleData.kt @@ -0,0 +1,6 @@ +import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButtonId +import dev.inmo.tgbotapi.types.request.RequestId +import kotlin.random.Random +import kotlin.random.nextUInt + +val preparedSampleKeyboardRequestId = RequestId(Random.nextUInt().toUShort()) diff --git a/WebApp/src/jsMain/kotlin/main.kt b/WebApp/src/jsMain/kotlin/main.kt index c312cb3..533dd5d 100644 --- a/WebApp/src/jsMain/kotlin/main.kt +++ b/WebApp/src/jsMain/kotlin/main.kt @@ -1,6 +1,7 @@ import androidx.compose.runtime.* import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions import dev.inmo.tgbotapi.types.CustomEmojiId +import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButtonId import dev.inmo.tgbotapi.types.userIdField import dev.inmo.tgbotapi.types.webAppQueryIdField import dev.inmo.tgbotapi.webapps.* @@ -17,6 +18,7 @@ import io.ktor.client.request.* import io.ktor.client.statement.bodyAsText import io.ktor.http.* import io.ktor.http.content.TextContent +import kotlinx.browser.document import kotlinx.browser.window import kotlinx.coroutines.* import kotlinx.dom.appendElement @@ -65,7 +67,12 @@ fun main() { } val scope = rememberCoroutineScope() val isSafeState = remember { mutableStateOf(null) } - val logsState = remember { mutableStateListOf() } + val logsState = remember { + mutableStateListOf( + window.location.href, + ) + } + val buttonIdState = remember { mutableStateOf(null) } // Text(window.location.href) // P() @@ -94,6 +101,30 @@ fun main() { ) } + LaunchedEffect(baseUrl) { + val response = client.post("$baseUrl/getPreparedKeyboardButtonId") { + setBody( + Json.encodeToString( + WebAppDataWrapper.serializer(), + WebAppDataWrapper(webApp.initData, webApp.initDataUnsafe.hash) + ) + ) + parameter(userIdField, webApp.initDataUnsafe.user ?.id ?.long ?: return@LaunchedEffect) + } + when (response.status) { + HttpStatusCode.OK -> { + val buttonId = response.bodyAsText() + buttonIdState.value = PreparedKeyboardButtonId(buttonId) + } + HttpStatusCode.NoContent -> { + buttonIdState.value = null + } + else -> { + logsState.add("Error while getting prepared keyboard button id: ${response.status}") + } + } + } + Text( when (isSafeState.value) { null -> "Checking safe state..." @@ -249,6 +280,23 @@ fun main() { Text("Confirm") } + P() + H3 { Text("Prepared keyboard button") } + val buttonIdValue = buttonIdState.value + if (buttonIdValue == null) { + Text("Ensure that you have called /prepareKeyboard in bot. If you did it, check logs of server") + } else { + Button({ + onClick { + webApp.requestChat(buttonIdValue) { + logsState.add("Chat have been received: $it") + } + } + }) { + Text("Prepared keyboard button") + } + } + P() H3 { Text("Write access callbacks") } Button({ @@ -396,11 +444,15 @@ fun main() { } mainButton.apply { setText("Main button") - setParams( - BottomButtonParams( - iconCustomEmojiId = CustomEmojiId("5370976574969486150") // 😏 + runCatching { + setParams( + BottomButtonParams( + iconCustomEmojiId = CustomEmojiId("5370976574969486150") // 😏 + ) ) - ) + }.onFailure { + logsState.add("Can't set params for main button: $it") + } onClick { logsState.add("Main button clicked") hapticFeedback.notificationOccurred( @@ -411,11 +463,15 @@ fun main() { } secondaryButton.apply { setText("Secondary button") - setParams( - BottomButtonParams( - iconCustomEmojiId = CustomEmojiId("5370763368497944736") // 😒 + runCatching { + setParams( + BottomButtonParams( + iconCustomEmojiId = CustomEmojiId("5370763368497944736") // 😒 + ) ) - ) + }.onFailure { + logsState.add("Can't set params for secondary button: $it") + } onClick { logsState.add("Secondary button clicked") hapticFeedback.notificationOccurred( diff --git a/WebApp/src/jvmMain/kotlin/WebAppServer.kt b/WebApp/src/jvmMain/kotlin/WebAppServer.kt index 11b1f8a..e89b6eb 100644 --- a/WebApp/src/jvmMain/kotlin/WebAppServer.kt +++ b/WebApp/src/jvmMain/kotlin/WebAppServer.kt @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.ktor.server.createKtorServer import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands +import dev.inmo.tgbotapi.extensions.api.savePreparedKeyboardButton import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.api.set.setUserEmojiStatus @@ -21,6 +22,10 @@ import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent +import dev.inmo.tgbotapi.types.buttons.KeyboardButtonRequestManagedBot +import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButton +import dev.inmo.tgbotapi.types.buttons.PreparedKeyboardButtonId +import dev.inmo.tgbotapi.types.buttons.reply.requestManagedBotReplyButton import dev.inmo.tgbotapi.types.webapps.WebAppInfo import dev.inmo.tgbotapi.utils.* import io.ktor.http.* @@ -59,6 +64,7 @@ suspend fun main(vararg args: String) { val initiationLogger = KSLog("Initialization") val bot = telegramBot(telegramBotAPIUrlsKeeper) + val usersToButtonsMap = mutableMapOf() createKtorServer( "0.0.0.0", args.getOrNull(2) ?.toIntOrNull() ?: 8080 @@ -123,6 +129,24 @@ suspend fun main(vararg args: String) { call.respond(HttpStatusCode.OK, set.toString()) } + post("getPreparedKeyboardButtonId") { + val requestBody = call.receiveText() + val webAppCheckData = Json.decodeFromString(WebAppDataWrapper.serializer(), requestBody) + + val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash) + val rawUserId = call.parameters[userIdField] ?.toLongOrNull() ?.let(::RawChatId) ?: error("$userIdField should be presented as long value") + + if (isSafe) { + val buttonId = usersToButtonsMap[UserId(rawUserId)] + if (buttonId == null) { + call.respond(HttpStatusCode.NoContent) + } else { + call.respond(HttpStatusCode.OK, buttonId.string) + } + } else { + call.respond(HttpStatusCode.Forbidden) + } + } } }.start(false) @@ -171,6 +195,20 @@ suspend fun main(vararg args: String) { ) ) } + onCommand("prepareKeyboard") { + val preparedKeyboardButton = savePreparedKeyboardButton( + userId = it.chat.id.toChatId(), + button = requestManagedBotReplyButton( + text = "Saved sample button", + requestManagedBot = KeyboardButtonRequestManagedBot( + requestId = preparedSampleKeyboardRequestId, + suggestedName = "Saved sample button bot", + suggestedUsername = Username.prepare("saved_sample_button_bot") + ) + ) + ) + usersToButtonsMap[it.chat.id.toChatId()] = preparedKeyboardButton.id + } onBaseInlineQuery { answerInlineQuery( it, From bd71e642b2f235815707dfbcb7e7ae340f6fab46 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 15 Apr 2026 16:52:54 +0600 Subject: [PATCH 7/8] upfill polls bot --- PollsBot/src/main/kotlin/PollsBot.kt | 57 ++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/PollsBot/src/main/kotlin/PollsBot.kt b/PollsBot/src/main/kotlin/PollsBot.kt index c15675c..58eb602 100644 --- a/PollsBot/src/main/kotlin/PollsBot.kt +++ b/PollsBot/src/main/kotlin/PollsBot.kt @@ -6,11 +6,16 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.send.polls.sendQuizPoll import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll +import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.send 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.onPollAnswer +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollOptionAdded +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollOptionDeleted import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollUpdates +import dev.inmo.tgbotapi.extensions.utils.accessibleMessageOrNull import dev.inmo.tgbotapi.extensions.utils.customEmojiTextSourceOrNull import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources import dev.inmo.tgbotapi.types.BotCommand @@ -105,7 +110,9 @@ suspend fun main(vararg args: String) { } }, isAnonymous = false, - replyParameters = ReplyParameters(it) + replyParameters = ReplyParameters(it), + allowAddingOptions = true, + hideResultsUntilCloses = true, ) pollToChatMutex.withLock { pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id @@ -118,7 +125,12 @@ suspend fun main(vararg args: String) { .firstOrNull { it.first.command == "quiz" } ?.second ?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() } - val correctAnswer = Random.nextInt(10) + val correctAnswer = mutableListOf() + (1 until Random.nextInt(9)).forEach { + val option = Random.nextInt(10) + if (correctAnswer.contains(option)) return@forEach + correctAnswer.add(option) + } val sentPoll = sendQuizPoll( it.chat.id, questionEntities = buildEntities { @@ -127,7 +139,13 @@ suspend fun main(vararg args: String) { customEmoji(customEmoji.customEmojiId, customEmoji.subsources) } }, - (1 .. 10).map { + descriptionTextSources = buildEntities { + regular("Test quiz poll description:") + if (customEmoji != null) { + customEmoji(customEmoji.customEmojiId, customEmoji.subsources) + } + }, + options = (1 .. 10).map { InputPollOption { regular(it.toString()) + " " if (customEmoji != null) { @@ -137,7 +155,11 @@ suspend fun main(vararg args: String) { }, isAnonymous = false, replyParameters = ReplyParameters(it), - correctOptionId = correctAnswer, + correctOptionIds = correctAnswer.sorted(), + allowMultipleAnswers = correctAnswer.size > 1, + allowsRevoting = true, + shuffleOptions = true, + hideResultsUntilCloses = true, explanationTextSources = buildEntities { regular("Random solved it to be ") + underline((correctAnswer + 1).toString()) + " " if (customEmoji != null) { @@ -145,6 +167,7 @@ suspend fun main(vararg args: String) { } } ) + println("Sent poll data: $sentPoll") pollToChatMutex.withLock { pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id } @@ -168,6 +191,32 @@ suspend fun main(vararg args: String) { } } + onPollOptionAdded { + it.chatEvent.pollMessage ?.accessibleMessageOrNull() ?.let { pollMessage -> + reply(pollMessage) { + +"Poll option added: \n" + +it.chatEvent.optionTextSources + } + } + } + onPollOptionDeleted { + it.chatEvent.pollMessage ?.accessibleMessageOrNull() ?.let { pollMessage -> + reply(pollMessage) { + +"Poll option deleted: \n" + +it.chatEvent.optionTextSources + } + } + } + + onContentMessage { + val replyPollOptionId = it.replyInfo ?.pollOptionId ?: return@onContentMessage + it.replyTo ?.accessibleMessageOrNull() ?.let { replied -> + reply(replied, pollOptionId = replyPollOptionId) { + +"Reply to poll option" + } + } + } + setMyCommands( BotCommand("anonymous", "Create anonymous regular poll"), BotCommand("public", "Create non anonymous regular poll"), From 00ab078891a1284f5ae6ab78a6a262bd1f916435 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sat, 18 Apr 2026 18:40:16 +0600 Subject: [PATCH 8/8] build fix --- .../src/main/kotlin/BusinessConnectionsBot.kt | 16 ++++++++++++++++ .../src/main/kotlin/ChatAvatarSetter.kt | 11 ++++------- MyBot/src/main/kotlin/MyBot.kt | 11 ++++------- PollsBot/src/main/kotlin/PollsBot.kt | 2 +- .../src/main/kotlin/StickerSetHandlerBot.kt | 6 +++++- 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt b/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt index 41a9710..24ef4cf 100644 --- a/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt +++ b/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt @@ -213,6 +213,8 @@ suspend fun main(args: Array) { firstName, secondName ) + }.map { + true }.getOrElse { false } reply(it) { if (set) { @@ -230,6 +232,8 @@ suspend fun main(args: Array) { businessConnectionId, username ) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -267,6 +271,8 @@ suspend fun main(args: Array) { } val transferred = runCatching { transferBusinessAccountStars(businessConnectionId, count) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -310,6 +316,8 @@ suspend fun main(args: Array) { businessConnectionId, bio ) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -327,6 +335,8 @@ suspend fun main(args: Array) { businessConnectionId, initialBio ) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -358,6 +368,8 @@ suspend fun main(args: Array) { ), isPublic = isPublic ) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -376,6 +388,8 @@ suspend fun main(args: Array) { businessConnectionId, isPublic = isPublic ) + }.map { + true }.getOrElse { it.printStackTrace() false @@ -461,6 +475,8 @@ suspend fun main(args: Array) { val deleted = runCatching { deleteStory(businessConnectionId, replyTo.content.story.id) + }.map { + true }.getOrElse { it.printStackTrace() false diff --git a/ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt b/ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt index 0c0a4f6..7447349 100644 --- a/ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt +++ b/ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt @@ -1,3 +1,4 @@ +import dev.inmo.micro_utils.coroutines.runCatchingLogging import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.extensions.api.chat.modify.setChatPhoto @@ -15,17 +16,13 @@ suspend fun main(args: Array) { bot.buildBehaviourWithLongPolling(scope = CoroutineScope(Dispatchers.IO)) { onPhoto { val bytes = downloadFile(it.content) - runCatchingSafely { + runCatchingLogging { setChatPhoto( it.chat.id, bytes.asMultipartFile("sample.jpg") ) - }.onSuccess { b -> - if (b) { - reply(it, "Done") - } else { - reply(it, "Something went wrong") - } + }.onSuccess { _ -> + reply(it, "Done") }.onFailure { e -> e.printStackTrace() diff --git a/MyBot/src/main/kotlin/MyBot.kt b/MyBot/src/main/kotlin/MyBot.kt index a6f5150..eda28e7 100644 --- a/MyBot/src/main/kotlin/MyBot.kt +++ b/MyBot/src/main/kotlin/MyBot.kt @@ -70,21 +70,18 @@ suspend fun main(vararg args: String) { draftMessagesChannel.send("Photo file have been downloaded. Start set my profile photo") - val setResult = setMyProfilePhoto( + setMyProfilePhoto( InputProfilePhoto.Static( photoFile.asMultipartFile() ) ) - if (setResult) { - reply(commandMessage, "New photo have been set") - } + reply(commandMessage, "New photo have been set") } onCommand("removeMyProfilePhoto") { runCatchingLogging { - if (removeMyProfilePhoto()) { - reply(it, "Photo have been removed") - } + removeMyProfilePhoto() + reply(it, "Photo have been removed") }.onFailure { e -> e.printStackTrace() reply(it, "Something web wrong. See logs for details.") diff --git a/PollsBot/src/main/kotlin/PollsBot.kt b/PollsBot/src/main/kotlin/PollsBot.kt index 58eb602..e4e27db 100644 --- a/PollsBot/src/main/kotlin/PollsBot.kt +++ b/PollsBot/src/main/kotlin/PollsBot.kt @@ -156,7 +156,7 @@ suspend fun main(vararg args: String) { isAnonymous = false, replyParameters = ReplyParameters(it), correctOptionIds = correctAnswer.sorted(), - allowMultipleAnswers = correctAnswer.size > 1, + allowsMultipleAnswers = correctAnswer.size > 1, allowsRevoting = true, shuffleOptions = true, hideResultsUntilCloses = true, diff --git a/StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt b/StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt index 875a1a5..ed8bac8 100644 --- a/StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt +++ b/StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt @@ -44,7 +44,11 @@ suspend fun main(args: Array) { onCommand("delete") { val deleted = runCatchingSafely { deleteStickerSet(it.chat.stickerSetName()) - }.getOrElse { false } + }.map { + true + }.getOrElse { + false + } if (deleted) { reply(it, "Deleted")