From 523e428bcba40ee5719413d2d6f21753d16215a6 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 15 Feb 2026 15:52:53 +0600 Subject: [PATCH 01/12] update dependencies --- gradle.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9982181..476adb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.jvmargs=-Xmx3148m kotlin.daemon.jvmargs=-Xmx3g -Xms500m -kotlin_version=2.2.10 -telegram_bot_api_version=29.0.0 -micro_utils_version=0.26.3 +kotlin_version=2.2.21 +telegram_bot_api_version=31.0.0 +micro_utils_version=0.26.8 serialization_version=1.9.0 -ktor_version=3.2.3 +ktor_version=3.3.2 compose_version=1.8.2 From 6b27aa01fb3e02014fcda2a18dd731328cb68048 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 15 Feb 2026 23:34:37 +0600 Subject: [PATCH 02/12] fix of topics according to 9.3 --- .../src/main/kotlin/TopicsHandling.kt | 157 +++++++++++------- 1 file changed, 101 insertions(+), 56 deletions(-) diff --git a/TopicsHandling/src/main/kotlin/TopicsHandling.kt b/TopicsHandling/src/main/kotlin/TopicsHandling.kt index 5b04a82..49c86ea 100644 --- a/TopicsHandling/src/main/kotlin/TopicsHandling.kt +++ b/TopicsHandling/src/main/kotlin/TopicsHandling.kt @@ -1,15 +1,26 @@ import com.benasher44.uuid.uuid4 +import dev.inmo.kslog.common.w +import dev.inmo.micro_utils.coroutines.runCatchingLogging import dev.inmo.micro_utils.coroutines.runCatchingSafely +import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.chat.forum.* 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.utils.forumChatOrNull +import dev.inmo.tgbotapi.extensions.utils.forumContentMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.privateChatOrNull +import dev.inmo.tgbotapi.extensions.utils.privateForumChatOrNull import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.ForumTopic +import dev.inmo.tgbotapi.types.chat.PrivateChat import dev.inmo.tgbotapi.types.commands.BotCommandScope +import io.ktor.client.plugins.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -20,13 +31,33 @@ suspend fun main(vararg args: String) { CoroutineScope(Dispatchers.Default), defaultExceptionsHandler = { it.printStackTrace() + }, + builder = { + client = client.config { + install(HttpTimeout) { + requestTimeoutMillis = 30000 + socketTimeoutMillis = 30000 + connectTimeoutMillis = 30000 + } + } } ) { + suspend fun TelegramBot.isPrivateForumsEnabled(): Boolean { + val me = getMe() + if (me.hasTopicsEnabled == false) { + Log.w("private forums are disabled. That means that they will not work in private chats") + } + return me.hasTopicsEnabled + } + println() flushAccumulatedUpdates() - allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { + allUpdatesFlow.subscribeLoggingDropExceptions(this) { println(it) } onCommand("start_test_topics") { + if (it.chat is PrivateChat && isPrivateForumsEnabled() == false) { + return@onCommand + } val forumTopic = createForumTopic( it.chat, "Test", @@ -44,21 +75,23 @@ suspend fun main(vararg args: String) { reply(it, "Test topic has changed its name to Test 01") - delay(1000L) - closeForumTopic( - it.chat.id, - forumTopic.messageThreadId, - ) + if (it.chat.privateChatOrNull() == null) { // For private forums it is prohibited to close or reopen topics + delay(1000L) + closeForumTopic( + it.chat.id, + forumTopic.messageThreadId, + ) - reply(it, "Test topic has been closed") + reply(it, "Test topic has been closed") - delay(1000L) - reopenForumTopic( - it.chat.id, - forumTopic.messageThreadId, - ) + delay(1000L) + reopenForumTopic( + it.chat.id, + forumTopic.messageThreadId, + ) - reply(it, "Test topic has been reopened") + reply(it, "Test topic has been reopened") + } delay(1000L) deleteForumTopic( @@ -68,68 +101,80 @@ suspend fun main(vararg args: String) { reply(it, "Test topic has been deleted") - delay(1000L) - hideGeneralForumTopic( - it.chat.id, - ) + if (it.chat.privateChatOrNull() == null) { // For private forums it is prohibited to close or reopen topics + delay(1000L) + hideGeneralForumTopic( + it.chat.id, + ) - reply(it, "General topic has been hidden") + reply(it, "General topic has been hidden") - delay(1000L) - unhideGeneralForumTopic( - it.chat.id - ) + delay(1000L) + unhideGeneralForumTopic( + it.chat.id + ) - reply(it, "General topic has been shown") + reply(it, "General topic has been shown") - delay(1000L) - runCatchingSafely( - { _ -> - reopenGeneralForumTopic( - it.chat.id - ) + delay(1000L) + runCatchingSafely( + { _ -> + reopenGeneralForumTopic( + it.chat.id + ) + closeGeneralForumTopic( + it.chat.id + ) + } + ) { closeGeneralForumTopic( it.chat.id ) } - ) { - closeGeneralForumTopic( + + reply(it, "General topic has been closed") + + delay(1000L) + reopenGeneralForumTopic( it.chat.id ) + + reply(it, "General topic has been opened") + + delay(1000L) + editGeneralForumTopic( + it.chat.id, + uuid4().toString().take(10) + ) + + reply(it, "General topic has been renamed") + + delay(1000L) + editGeneralForumTopic( + it.chat.id, + "Main topic" + ) + + reply(it, "General topic has been renamed") } - reply(it, "General topic has been closed") - - delay(1000L) - reopenGeneralForumTopic( - it.chat.id - ) - - reply(it, "General topic has been opened") - - delay(1000L) - editGeneralForumTopic( - it.chat.id, - uuid4().toString().take(10) - ) - - reply(it, "General topic has been renamed") - - delay(1000L) - editGeneralForumTopic( - it.chat.id, - "Main topic" - ) - - reply(it, "General topic has been renamed") - delay(1000L) } + onCommand("delete_topic") { + val chat = it.chat.forumChatOrNull() ?: return@onCommand + + deleteForumTopic(chat, chat.id.threadId ?: return@onCommand) + } + setMyCommands( BotCommand("start_test_topics", "start test topics"), + BotCommand("delete_topic", "delete topic where message have been sent"), scope = BotCommandScope.AllGroupChats ) + allUpdatesFlow.subscribeLoggingDropExceptions(this) { + println(it) + } }.second.join() } From 0432611f851b611876677e6db8d11f82532ca40e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 16 Feb 2026 17:40:23 +0600 Subject: [PATCH 03/12] improve test bots --- .../src/commonMain/kotlin/ResenderBot.kt | 1 - .../src/main/kotlin/TopicsHandling.kt | 39 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt b/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt index e5ebe67..a4e3177 100644 --- a/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt +++ b/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt @@ -38,7 +38,6 @@ suspend fun activateResenderBot( executeUnsafe( it.content.createResend( chat.id, - messageThreadId = it.threadIdOrNull, replyParameters = it.replyInfo?.messageMeta?.let { meta -> val quote = it.withContentOrNull()?.content?.quote ReplyParameters( diff --git a/TopicsHandling/src/main/kotlin/TopicsHandling.kt b/TopicsHandling/src/main/kotlin/TopicsHandling.kt index 49c86ea..e31f406 100644 --- a/TopicsHandling/src/main/kotlin/TopicsHandling.kt +++ b/TopicsHandling/src/main/kotlin/TopicsHandling.kt @@ -11,6 +11,14 @@ import dev.inmo.tgbotapi.extensions.api.chat.forum.* 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.onForumTopicClosed +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicEdited +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicReopened +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicHidden +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicUnhidden +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicEdited import dev.inmo.tgbotapi.extensions.utils.forumChatOrNull import dev.inmo.tgbotapi.extensions.utils.forumContentMessageOrNull import dev.inmo.tgbotapi.extensions.utils.privateChatOrNull @@ -168,9 +176,40 @@ suspend fun main(vararg args: String) { deleteForumTopic(chat, chat.id.threadId ?: return@onCommand) } + onCommand("unpin_all_forum_topic_messages") { + val chat = it.chat.forumChatOrNull() ?: return@onCommand + + unpinAllForumTopicMessages(chat, chat.id.threadId ?: return@onCommand) + } + + onForumTopicCreated { + reply(it, "Topic has been created") + } + onPrivateForumTopicCreated { + reply(it, "Private topic has been created") + } + + onForumTopicEdited { + reply(it, "Topic has been edited") + } + onPrivateForumTopicEdited { + reply(it, "Private topic has been edited") + } + + onForumTopicReopened { + reply(it, "Topic has been reopened") + } + onGeneralForumTopicHidden { + reply(it, "General topic has been hidden") + } + onGeneralForumTopicUnhidden { + reply(it, "General topic has been unhidden") + } + setMyCommands( BotCommand("start_test_topics", "start test topics"), BotCommand("delete_topic", "delete topic where message have been sent"), + BotCommand("unpin_all_forum_topic_messages", "delete topic where message have been sent"), scope = BotCommandScope.AllGroupChats ) allUpdatesFlow.subscribeLoggingDropExceptions(this) { From d1791b3058575b231799372212d72253ae561c6d Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 16 Feb 2026 18:57:43 +0600 Subject: [PATCH 04/12] add drafts bot --- DraftsBot/README.md | 9 +++ DraftsBot/build.gradle | 21 +++++++ DraftsBot/src/main/kotlin/DraftsBot.kt | 86 ++++++++++++++++++++++++++ settings.gradle | 2 + 4 files changed, 118 insertions(+) create mode 100644 DraftsBot/README.md create mode 100644 DraftsBot/build.gradle create mode 100644 DraftsBot/src/main/kotlin/DraftsBot.kt diff --git a/DraftsBot/README.md b/DraftsBot/README.md new file mode 100644 index 0000000..c49f1d8 --- /dev/null +++ b/DraftsBot/README.md @@ -0,0 +1,9 @@ +# Drafts bot + +The main purpose of this bot is just to answer "Oh, hi, " and add user mention here + +## Launch + +```bash +../gradlew run --args="BOT_TOKEN" +``` diff --git a/DraftsBot/build.gradle b/DraftsBot/build.gradle new file mode 100644 index 0000000..2006fb3 --- /dev/null +++ b/DraftsBot/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="TopicsHandlingKt" + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} diff --git a/DraftsBot/src/main/kotlin/DraftsBot.kt b/DraftsBot/src/main/kotlin/DraftsBot.kt new file mode 100644 index 0000000..8da1208 --- /dev/null +++ b/DraftsBot/src/main/kotlin/DraftsBot.kt @@ -0,0 +1,86 @@ +import com.benasher44.uuid.uuid4 +import dev.inmo.kslog.common.w +import dev.inmo.micro_utils.coroutines.runCatchingLogging +import dev.inmo.micro_utils.coroutines.runCatchingSafely +import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions +import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands +import dev.inmo.tgbotapi.extensions.api.chat.forum.* +import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.send.send +import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlow +import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlowWithTexts +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.onForumTopicClosed +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicEdited +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicReopened +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicHidden +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicUnhidden +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicEdited +import dev.inmo.tgbotapi.extensions.utils.forumChatOrNull +import dev.inmo.tgbotapi.extensions.utils.forumContentMessageOrNull +import dev.inmo.tgbotapi.extensions.utils.privateChatOrNull +import dev.inmo.tgbotapi.extensions.utils.privateForumChatOrNull +import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates +import dev.inmo.tgbotapi.types.BotCommand +import dev.inmo.tgbotapi.types.ForumTopic +import dev.inmo.tgbotapi.types.chat.PrivateChat +import dev.inmo.tgbotapi.types.commands.BotCommandScope +import io.ktor.client.plugins.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.isActive + +const val testText = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +""" + +suspend fun main(vararg args: String) { + telegramBotWithBehaviourAndLongPolling( + args.first(), + CoroutineScope(Dispatchers.Default), + defaultExceptionsHandler = { + it.printStackTrace() + }, + builder = { + client = client.config { + install(HttpTimeout) { + requestTimeoutMillis = 30000 + socketTimeoutMillis = 30000 + connectTimeoutMillis = 30000 + } + } + } + ) { + onCommand("test_draft_flow") { + sendMessageDraftFlowWithTexts( + it.chat.id, + flow { + val step = 50 + var currentLength = step + while (isActive && testText.length > currentLength) { + delay(500L) + emit(testText.take(currentLength)) + currentLength += step + } + }, + ) + send(it.chat, testText) + } + + setMyCommands( + BotCommand("test_draft_flow", "Start draft testing with flow"), + scope = BotCommandScope.AllGroupChats + ) + allUpdatesFlow.subscribeLoggingDropExceptions(this) { + println(it) + } + }.second.join() +} diff --git a/settings.gradle b/settings.gradle index 63cf651..3790358 100644 --- a/settings.gradle +++ b/settings.gradle @@ -63,3 +63,5 @@ include ":WebHooks" include ":SuggestedPosts" include ":ChecklistsBot" + +include ":DraftsBot" From bef86042f99325e2005b82116acab515f06b3a15 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Mon, 16 Feb 2026 23:31:59 +0600 Subject: [PATCH 05/12] add support of styling for buttons --- .../src/commonMain/kotlin/KeyboardsBot.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt index 1be39cf..ba8546f 100644 --- a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt +++ b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt @@ -11,9 +11,11 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.withContent import dev.inmo.tgbotapi.types.BotCommand +import dev.inmo.tgbotapi.types.CustomEmojiId import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent import dev.inmo.tgbotapi.types.InlineQueryId +import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.utils.PreviewFeature import dev.inmo.tgbotapi.utils.botCommand @@ -51,17 +53,17 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) { row { if (page - 1 > 2) { - dataButton("<<", "1 $count") + dataButton("<<", "1 $count", style = KeyboardButtonStyle.Danger) } if (page - 1 > 1) { - dataButton("<", "${page - 2} $count") + dataButton("<", "${page - 2} $count", style = KeyboardButtonStyle.Primary) } if (page + 1 < count) { - dataButton(">", "${page + 2} $count") + dataButton(">", "${page + 2} $count", style = KeyboardButtonStyle.Success) } if (page + 2 < count) { - dataButton(">>", "$count $count") + dataButton(">>", "$count $count", style = KeyboardButtonStyle.Danger) } } row { From 9981e82a108d6ef097b7d5a2b68e5ce94514fdce Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 17 Feb 2026 12:45:22 +0600 Subject: [PATCH 06/12] small improvement of keyboards bot --- .../KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt index ba8546f..77d75d3 100644 --- a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt +++ b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt @@ -163,7 +163,7 @@ suspend fun activateKeyboardsBot( it, replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) { row { - simpleButton("/inline") + simpleButton("/inline", style = KeyboardButtonStyle.Primary) } } ) { From fb6ed8b7ae85272aa9d794a945c0b285a274005c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 18 Feb 2026 19:59:34 +0600 Subject: [PATCH 07/12] rename GetMe to My bot --- CustomBot/src/main/kotlin/CustomBot.kt | 2 + GetMeBot/src/main/kotlin/GetMeBot.kt | 29 -------- {GetMeBot => MyBot}/README.md | 0 {GetMeBot => MyBot}/build.gradle | 0 MyBot/src/main/kotlin/MyBot.kt | 94 ++++++++++++++++++++++++++ settings.gradle | 2 +- 6 files changed, 97 insertions(+), 30 deletions(-) delete mode 100644 GetMeBot/src/main/kotlin/GetMeBot.kt rename {GetMeBot => MyBot}/README.md (100%) rename {GetMeBot => MyBot}/build.gradle (100%) create mode 100644 MyBot/src/main/kotlin/MyBot.kt diff --git a/CustomBot/src/main/kotlin/CustomBot.kt b/CustomBot/src/main/kotlin/CustomBot.kt index d75ace7..72e5ac3 100644 --- a/CustomBot/src/main/kotlin/CustomBot.kt +++ b/CustomBot/src/main/kotlin/CustomBot.kt @@ -11,6 +11,8 @@ 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.onChannelDirectMessagesConfigurationChanged +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerChanged +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerLeft 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 diff --git a/GetMeBot/src/main/kotlin/GetMeBot.kt b/GetMeBot/src/main/kotlin/GetMeBot.kt deleted file mode 100644 index eec6881..0000000 --- a/GetMeBot/src/main/kotlin/GetMeBot.kt +++ /dev/null @@ -1,29 +0,0 @@ -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.tgbotapi.bot.ktor.telegramBot -import dev.inmo.tgbotapi.extensions.api.bot.getMe -import dev.inmo.tgbotapi.extensions.api.chat.get.getChat - -/** - * This is one of the easiest bots - it will just print information about itself - */ -suspend fun main(vararg args: String) { - val botToken = args.first() - val isDebug = args.getOrNull(1) == "debug" - - if (isDebug) { - setDefaultKSLog( - KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? -> - println(defaultMessageFormatter(level, tag, message, throwable)) - } - ) - } - - val bot = telegramBot(botToken) - - val me = bot.getMe() - println(me) - println(bot.getChat(me)) -} diff --git a/GetMeBot/README.md b/MyBot/README.md similarity index 100% rename from GetMeBot/README.md rename to MyBot/README.md diff --git a/GetMeBot/build.gradle b/MyBot/build.gradle similarity index 100% rename from GetMeBot/build.gradle rename to MyBot/build.gradle diff --git a/MyBot/src/main/kotlin/MyBot.kt b/MyBot/src/main/kotlin/MyBot.kt new file mode 100644 index 0000000..a6f5150 --- /dev/null +++ b/MyBot/src/main/kotlin/MyBot.kt @@ -0,0 +1,94 @@ +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.launchLoggingDropExceptions +import dev.inmo.micro_utils.coroutines.runCatchingLogging +import dev.inmo.tgbotapi.bot.ktor.telegramBot +import dev.inmo.tgbotapi.extensions.api.bot.getMe +import dev.inmo.tgbotapi.extensions.api.bot.removeMyProfilePhoto +import dev.inmo.tgbotapi.extensions.api.bot.setMyProfilePhoto +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp +import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlowWithTexts +import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitPhotoMessage +import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat +import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile +import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first + +/** + * This is one of the easiest bots - it will just print information about itself + */ +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)) + } + ) + } + + val bot = telegramBot(botToken) + + telegramBotWithBehaviourAndLongPolling( + botToken, + CoroutineScope(Dispatchers.Default), + testServer = isTestServer, + ) { + val me = bot.getMe() + println(me) + println(bot.getChat(me)) + + onCommand("setMyProfilePhoto") { commandMessage -> + reply(commandMessage, "ok, send me new photo") + val newPhotoMessage = waitPhotoMessage().filter { potentialPhotoMessage -> + potentialPhotoMessage.sameChat(commandMessage) + }.first() + val draftMessagesChannel = Channel(capacity = 1) + + launchLoggingDropExceptions { + sendMessageDraftFlowWithTexts(commandMessage.chat.id, draftMessagesChannel.consumeAsFlow()) + }.invokeOnCompletion { + draftMessagesChannel.close(it) + } + + draftMessagesChannel.send("Start downloading photo") + val photoFile = downloadFileToTemp(newPhotoMessage.content) + + draftMessagesChannel.send("Photo file have been downloaded. Start set my profile photo") + + val setResult = setMyProfilePhoto( + InputProfilePhoto.Static( + photoFile.asMultipartFile() + ) + ) + if (setResult) { + reply(commandMessage, "New photo have been set") + } + } + + onCommand("removeMyProfilePhoto") { + runCatchingLogging { + if (removeMyProfilePhoto()) { + reply(it, "Photo have been removed") + } + }.onFailure { e -> + e.printStackTrace() + reply(it, "Something web wrong. See logs for details.") + } + } + }.second.join() +} diff --git a/settings.gradle b/settings.gradle index 3790358..dded845 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ include ":HelloBot" include ":PollsBot" -include ":GetMeBot" +include ":MyBot" include ":DeepLinksBot" From e660f06edfdb7f8ef2f7efa2066babfde5c69c8c Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Wed, 18 Feb 2026 22:24:32 +0600 Subject: [PATCH 08/12] add showing of user audios and update telegram bot api version onto temporal version --- CustomBot/src/main/kotlin/CustomBot.kt | 31 ++++++++++++++++++++++++++ gradle.properties | 4 ++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CustomBot/src/main/kotlin/CustomBot.kt b/CustomBot/src/main/kotlin/CustomBot.kt index 72e5ac3..1189e43 100644 --- a/CustomBot/src/main/kotlin/CustomBot.kt +++ b/CustomBot/src/main/kotlin/CustomBot.kt @@ -6,7 +6,10 @@ import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.get.getUserProfileAudios import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio +import dev.inmo.tgbotapi.extensions.api.send.replyWithPlaylist import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling @@ -14,6 +17,8 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChanne import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerChanged import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerLeft import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.types.media.AudioMediaGroupMemberTelegramMedia +import dev.inmo.tgbotapi.types.media.toTelegramMediaAudio import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.update.abstracts.Update import kotlinx.coroutines.CoroutineScope @@ -72,6 +77,32 @@ suspend fun main(vararg args: String) { println(data.update) println(data.commonMessage) println(getChat(it.chat)) + var currentOffset = 0 + val pageSize = 2 + do { + val userAudios = getUserProfileAudios(userId = it.chat.id, offset = currentOffset, limit = pageSize) + currentOffset += pageSize + println(userAudios) + when (userAudios.audios.size) { + 1 -> { + replyWithAudio( + it, + userAudios.audios.first().fileId + ) + } + 0 -> { + // do nothing + } + else -> { + replyWithPlaylist( + it, + userAudios.audios.map { + it.toTelegramMediaAudio() + } + ) + } + } + } while (currentOffset < userAudios.totalCount && userAudios.audios.isNotEmpty()) } onCommand( diff --git a/gradle.properties b/gradle.properties index 476adb7..4312fce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,8 +6,8 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.2.21 -telegram_bot_api_version=31.0.0 -micro_utils_version=0.26.8 +telegram_bot_api_version=31.0.0-branch_31.0.0-build3109 +micro_utils_version=0.26.9 serialization_version=1.9.0 ktor_version=3.3.2 compose_version=1.8.2 From 186a0f7abf9b9820fd09d84483e57ab4c7c19950 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 20 Feb 2026 18:58:24 +0600 Subject: [PATCH 09/12] update telegram bot api version --- CustomBot/src/main/kotlin/CustomBot.kt | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CustomBot/src/main/kotlin/CustomBot.kt b/CustomBot/src/main/kotlin/CustomBot.kt index 1189e43..bf493d3 100644 --- a/CustomBot/src/main/kotlin/CustomBot.kt +++ b/CustomBot/src/main/kotlin/CustomBot.kt @@ -7,6 +7,7 @@ import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance import dev.inmo.tgbotapi.extensions.api.chat.get.getChat import dev.inmo.tgbotapi.extensions.api.get.getUserProfileAudios +import dev.inmo.tgbotapi.extensions.api.send.media.sendPaidMedia import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio import dev.inmo.tgbotapi.extensions.api.send.replyWithPlaylist @@ -17,8 +18,10 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChanne import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerChanged import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerLeft import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto import dev.inmo.tgbotapi.types.media.AudioMediaGroupMemberTelegramMedia import dev.inmo.tgbotapi.types.media.toTelegramMediaAudio +import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaPhoto import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.update.abstracts.Update import kotlinx.coroutines.CoroutineScope diff --git a/gradle.properties b/gradle.properties index 4312fce..f818cfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.2.21 -telegram_bot_api_version=31.0.0-branch_31.0.0-build3109 +telegram_bot_api_version=31.0.0-branch_31.0.0-build3116 micro_utils_version=0.26.9 serialization_version=1.9.0 ktor_version=3.3.2 From e1f5e40143997c8fce8fd115a90be87779907529 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Fri, 20 Feb 2026 19:21:23 +0600 Subject: [PATCH 10/12] fix of build --- StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt b/StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt index e964cc3..1258851 100644 --- a/StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt +++ b/StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt @@ -15,6 +15,7 @@ import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.withContentOrNull import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile +import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.RawChatId import dev.inmo.tgbotapi.types.UserId import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup @@ -40,7 +41,7 @@ import kotlinx.coroutines.Dispatchers */ suspend fun main(vararg args: String) { val botToken = args.first() - val adminUserId = args.getOrNull(1) ?.toLongOrNull() ?.let(::RawChatId) ?.let(::UserId) ?: error("Pass user-admin for full access to the bot") + val adminUserId = args.getOrNull(1) ?.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: error("Pass user-admin for full access to the bot") val isDebug = args.any { it == "debug" } val isTestServer = args.any { it == "testServer" } From 9d4b7b5a500863771c0b40b94a3a5816f16cd85e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 14:00:26 +0600 Subject: [PATCH 11/12] add gifts bot --- GiftsBot/build.gradle | 21 +++++ GiftsBot/src/main/kotlin/GiftsBot.kt | 112 +++++++++++++++++++++++++++ gradle.properties | 2 +- settings.gradle | 2 + 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 GiftsBot/build.gradle create mode 100644 GiftsBot/src/main/kotlin/GiftsBot.kt diff --git a/GiftsBot/build.gradle b/GiftsBot/build.gradle new file mode 100644 index 0000000..ccb78a7 --- /dev/null +++ b/GiftsBot/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="GiftsBotKt" + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + + implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" +} diff --git a/GiftsBot/src/main/kotlin/GiftsBot.kt b/GiftsBot/src/main/kotlin/GiftsBot.kt new file mode 100644 index 0000000..efb3678 --- /dev/null +++ b/GiftsBot/src/main/kotlin/GiftsBot.kt @@ -0,0 +1,112 @@ +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.business.getBusinessAccountGiftsFlow +import dev.inmo.tgbotapi.extensions.api.gifts.getChatGiftsFlow +import dev.inmo.tgbotapi.extensions.api.gifts.getUserGiftsFlow +import dev.inmo.tgbotapi.extensions.api.send.reply +import dev.inmo.tgbotapi.extensions.api.send.withTypingAction +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.onGiveawayCompleted +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayContent +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCreated +import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayWinners +import dev.inmo.tgbotapi.types.chat.BusinessChat +import dev.inmo.tgbotapi.types.chat.PrivateChat +import dev.inmo.tgbotapi.types.chat.PublicChat +import dev.inmo.tgbotapi.types.chat.UnknownChatType +import dev.inmo.tgbotapi.types.gifts.OwnedGift +import dev.inmo.tgbotapi.types.message.textsources.splitForText +import dev.inmo.tgbotapi.utils.bold +import dev.inmo.tgbotapi.utils.buildEntities +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +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, testServer = isTestServer) { + // start here!! + val me = getMe() + println(me) + + onCommand("start") { + val giftsFlow = when (val chat = it.chat) { + is BusinessChat -> { + getBusinessAccountGiftsFlow( + chat.id.businessConnectionId + ) + } + is PrivateChat -> { + getUserGiftsFlow(it.chat.id) + } + is UnknownChatType, + is PublicChat -> { + getChatGiftsFlow(it.chat.id) + } + } + + withTypingAction(it.chat) { + val texts = buildEntities { + giftsFlow.collect { ownedGifts -> + ownedGifts.gifts.forEach { + when (it) { + is OwnedGift.Regular.Common -> { + bold("Type") + ": Regular common\n" + bold("Id") + ": ${it.gift.id.string}\n" + bold("Text") + ": ${it.text ?: "(None)"}\n" + bold("Stars cost") + ": ${it.gift.starCount}\n" + } + is OwnedGift.Unique.Common -> { + bold("Type") + ": Unique common\n" + bold("Id") + ": ${it.gift.id ?.string ?: "(None)"}\n" + bold("Name") + ": ${it.gift.name.value}\n" + bold("Model") + ": ${it.gift.model.name}\n" + bold("Number") + ": ${it.gift.number}\n" + } + is OwnedGift.Regular.OwnedByBusinessAccount -> { + bold("Type") + ": Regular owned by business\n" + bold("Id") + ": ${it.gift.id.string}\n" + bold("Text") + ": ${it.text ?: "(None)"}\n" + bold("Stars cost") + ": ${it.gift.starCount}\n" + } + is OwnedGift.Unique.OwnedByBusinessAccount -> { + bold("Type") + ": Unique owned by business\n" + bold("Id") + ": ${it.gift.id ?.string ?: "(None)"}\n" + bold("Name") + ": ${it.gift.name.value}\n" + bold("Model") + ": ${it.gift.model.name}\n" + bold("Number") + ": ${it.gift.number}\n" + } + } + } + } + } + val preparedTexts = texts.splitForText() + if (preparedTexts.isEmpty()) { + reply(it, "This chat have no any gifts") + } else { + preparedTexts.forEach { preparedText -> reply(it, preparedText) } + } + } + } + +// allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { +// println(it) +// } + }.second.join() +} diff --git a/gradle.properties b/gradle.properties index f818cfb..74a052d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.2.21 -telegram_bot_api_version=31.0.0-branch_31.0.0-build3116 +telegram_bot_api_version=31.0.0-branch_31.0.0-build3119 micro_utils_version=0.26.9 serialization_version=1.9.0 ktor_version=3.3.2 diff --git a/settings.gradle b/settings.gradle index dded845..0589ba6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -65,3 +65,5 @@ include ":SuggestedPosts" include ":ChecklistsBot" include ":DraftsBot" + +include ":GiftsBot" From 07403546f4e3b355e74f368bb19df9a699826ec4 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 24 Feb 2026 15:07:13 +0600 Subject: [PATCH 12/12] update telegram bot api version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 74a052d..ef92a94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ kotlin.daemon.jvmargs=-Xmx3g -Xms500m kotlin_version=2.2.21 -telegram_bot_api_version=31.0.0-branch_31.0.0-build3119 +telegram_bot_api_version=31.0.0 micro_utils_version=0.26.9 serialization_version=1.9.0 ktor_version=3.3.2