diff --git a/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt b/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt
index 2623cfc..9b9e1f6 100644
--- a/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt
+++ b/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt
@@ -2,24 +2,68 @@ 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.common.Percentage
+import dev.inmo.tgbotapi.extensions.api.answers.answer
 import dev.inmo.tgbotapi.extensions.api.bot.getMe
+import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountStarBalance
+import dev.inmo.tgbotapi.extensions.api.business.deleteBusinessMessages
+import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGifts
+import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGiftsFlow
+import dev.inmo.tgbotapi.extensions.api.business.readBusinessMessage
+import dev.inmo.tgbotapi.extensions.api.business.removeBusinessAccountProfilePhoto
+import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountBio
+import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountName
+import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountProfilePhoto
+import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountUsername
+import dev.inmo.tgbotapi.extensions.api.business.transferBusinessAccountStars
+import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
 import dev.inmo.tgbotapi.extensions.api.chat.modify.pinChatMessage
 import dev.inmo.tgbotapi.extensions.api.chat.modify.unpinChatMessage
+import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
 import dev.inmo.tgbotapi.extensions.api.get.getBusinessConnection
 import dev.inmo.tgbotapi.extensions.api.send.reply
 import dev.inmo.tgbotapi.extensions.api.send.send
+import dev.inmo.tgbotapi.extensions.api.stories.deleteStory
+import dev.inmo.tgbotapi.extensions.api.stories.postStory
 import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
 import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
-import dev.inmo.tgbotapi.extensions.utils.accessibleMessageOrNull
+import dev.inmo.tgbotapi.extensions.utils.commonMessageOrNull
+import dev.inmo.tgbotapi.extensions.utils.extendedPrivateChatOrThrow
 import dev.inmo.tgbotapi.extensions.utils.ifAccessibleMessage
 import dev.inmo.tgbotapi.extensions.utils.ifBusinessContentMessage
 import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
+import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
+import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
+import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
+import dev.inmo.tgbotapi.requests.abstracts.multipartFile
+import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto
+import dev.inmo.tgbotapi.requests.stories.PostStory
 import dev.inmo.tgbotapi.types.ChatId
+import dev.inmo.tgbotapi.types.MessageId
+import dev.inmo.tgbotapi.types.RawChatId
 import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
+import dev.inmo.tgbotapi.types.chat.PrivateChat
+import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
+import dev.inmo.tgbotapi.types.message.content.PhotoContent
+import dev.inmo.tgbotapi.types.message.content.StoryContent
+import dev.inmo.tgbotapi.types.message.content.TextContent
+import dev.inmo.tgbotapi.types.message.content.VideoContent
+import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupPartContent
+import dev.inmo.tgbotapi.types.stories.InputStoryContent
+import dev.inmo.tgbotapi.types.stories.StoryArea
+import dev.inmo.tgbotapi.types.stories.StoryAreaPosition
+import dev.inmo.tgbotapi.types.stories.StoryAreaType
+import dev.inmo.tgbotapi.utils.botCommand
+import dev.inmo.tgbotapi.utils.code
+import dev.inmo.tgbotapi.utils.extensions.splitForText
+import dev.inmo.tgbotapi.utils.row
+import korlibs.time.seconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
+import kotlinx.serialization.json.Json
 
 suspend fun main(args: Array<String>) {
     val botToken = args.first()
@@ -34,6 +78,7 @@ suspend fun main(args: Array<String>) {
     }
 
     val businessConnectionsChats = mutableMapOf<BusinessConnectionId, ChatId>()
+    val chatsBusinessConnections = mutableMapOf<ChatId, BusinessConnectionId>()
     val businessConnectionsChatsMutex = Mutex()
 
     telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
@@ -43,12 +88,14 @@ suspend fun main(args: Array<String>) {
         onBusinessConnectionEnabled {
             businessConnectionsChatsMutex.withLock {
                 businessConnectionsChats[it.id] = it.userChatId
+                chatsBusinessConnections[it.userChatId] = it.id
             }
             send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been enabled")
         }
         onBusinessConnectionDisabled {
             businessConnectionsChatsMutex.withLock {
                 businessConnectionsChats.remove(it.id)
+                chatsBusinessConnections.remove(it.userChatId)
             }
             send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been disabled")
         }
@@ -71,7 +118,20 @@ suspend fun main(args: Array<String>) {
                 if (businessContentMessage.sentByBusinessConnectionOwner) {
                     reply(sent, "You have sent this message to the ${businessContentMessage.businessConnectionId.string} related chat")
                 } else {
-                    reply(sent, "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat")
+                    reply(
+                        to = sent,
+                        text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
+                    )
+                    send(
+                        chatId = businessConnectionsChats[it.businessConnectionId] ?: return@ifBusinessContentMessage,
+                        text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
+                        replyMarkup = inlineKeyboard {
+                            row {
+                                dataButton("Read message", "read ${it.chat.id.chatId.long} ${it.messageId.long}")
+                                dataButton("Delete message", "delete ${it.chat.id.chatId.long} ${it.messageId.long}")
+                            }
+                        }
+                    )
                 }
             }
         }
@@ -98,5 +158,318 @@ suspend fun main(args: Array<String>) {
             }
             send(businessConnectionOwnerChat, "There are several removed messages in chat ${it.chat.id}: ${it.messageIds}")
         }
+        onCommand("get_business_account_info", initialFilter = { it.chat is PrivateChat }) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id]
+            val businessConnectionInfo = businessConnectionId ?.let { getBusinessConnection(it) }
+            reply(it) {
+                if (businessConnectionInfo == null) {
+                    +"There is no business connection for current chat"
+                } else {
+                    +(Json { prettyPrint = true; encodeDefaults = true }.encodeToString(businessConnectionInfo))
+                }
+            }
+        }
+        onMessageDataCallbackQuery(Regex("read \\d+ \\d+")) {
+            val (_, chatIdString, messageIdString) = it.data.split(" ")
+            val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
+            val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
+            val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
+
+            val readResponse = businessConnectionId ?.let { readBusinessMessage(it, chatId, messageId) }
+            answer(
+                it,
+                if (readResponse == null) {
+                    "There is no business connection for current chat"
+                } else {
+                    "Message has been read"
+                }
+            )
+        }
+        onMessageDataCallbackQuery(Regex("delete \\d+ \\d+")) {
+            val (_, chatIdString, messageIdString) = it.data.split(" ")
+            val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
+            val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
+            val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
+
+            val readResponse = businessConnectionId ?.let { deleteBusinessMessages(it, listOf(messageId)) }
+            answer(
+                it,
+                if (readResponse == null) {
+                    "There is no business connection for current chat"
+                } else {
+                    "Message has been deleted"
+                }
+            )
+        }
+        onCommandWithArgs("set_business_account_name", initialFilter = { it.chat is PrivateChat }) { it, args ->
+            val firstName = args[0]
+            val secondName = args.getOrNull(1)
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
+            val set = runCatching {
+                setBusinessAccountName(
+                    businessConnectionId,
+                    firstName,
+                    secondName
+                )
+            }.getOrElse { false }
+            reply(it) {
+                if (set) {
+                    +"Account name has been set"
+                } else {
+                    +"Account name has not been set"
+                }
+            }
+        }
+        onCommandWithArgs("set_business_account_username", initialFilter = { it.chat is PrivateChat }) { it, args ->
+            val username = args[0]
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
+            val set = runCatching {
+                setBusinessAccountUsername(
+                    businessConnectionId,
+                    username
+                )
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (set) {
+                    +"Account username has been set"
+                } else {
+                    +"Account username has not been set"
+                }
+            }
+        }
+        onCommand("get_business_account_star_balance", initialFilter = { it.chat is PrivateChat }) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
+            val starAmount = runCatching {
+                getBusinessAccountStarBalance(businessConnectionId)
+            }.getOrElse {
+                it.printStackTrace()
+                null
+            }
+            reply(it) {
+                if (starAmount != null) {
+                    +"Account stars amount: $starAmount"
+                } else {
+                    +"Account stars amount has not been got"
+                }
+            }
+        }
+        onCommandWithArgs("transfer_business_account_stars", initialFilter = { it.chat is PrivateChat }) { it, args ->
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
+            val count = args.firstOrNull() ?.toIntOrNull() ?: reply(it) {
+                "Pass amount of stars to transfer to bot with command"
+            }.let {
+                return@onCommandWithArgs
+            }
+            val transferred = runCatching {
+                transferBusinessAccountStars(businessConnectionId, count)
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (transferred) {
+                    +"Stars have been transferred"
+                } else {
+                    +"Stars have not been transferred"
+                }
+            }
+        }
+        onCommand("get_business_account_gifts", initialFilter = { it.chat is PrivateChat }) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
+            val giftsFlow = runCatching {
+                getBusinessAccountGiftsFlow(businessConnectionId)
+            }.getOrElse {
+                it.printStackTrace()
+                null
+            }
+            if (giftsFlow == null) {
+                reply(it) {
+                    +"Error in receiving of gifts"
+                }
+            } else {
+                giftsFlow.collect { giftsPage ->
+                    giftsPage.gifts.joinToString {
+                        it.toString()
+                    }.splitForText().forEach { message ->
+                        reply(it, message)
+                    }
+                }
+            }
+        }
+        onCommand("set_business_account_bio", requireOnlyCommandInMessage = false, initialFilter = { it.chat is PrivateChat }) {
+            val initialBio = getChat(it.chat).extendedPrivateChatOrThrow().bio
+            val bio = it.content.text.removePrefix("/set_business_account_bio").trim()
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
+            val set = runCatching {
+                setBusinessAccountBio(
+                    businessConnectionId,
+                    bio
+                )
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (set) {
+                    +"Account bio has been set. It will be reset within 15 seconds.\n\nInitial bio: " + code(initialBio)
+                } else {
+                    +"Account bio has not been set"
+                }
+            }
+            delay(15.seconds)
+            val reset = runCatching {
+                setBusinessAccountBio(
+                    businessConnectionId,
+                    initialBio
+                )
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (reset) {
+                    +"Account bio has been reset"
+                } else {
+                    +"Account bio has not been set. Set it manually: " + code(initialBio)
+                }
+            }
+        }
+        suspend fun handleSetProfilePhoto(it: CommonMessage<TextContent>, isPublic: Boolean) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@handleSetProfilePhoto
+            val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<PhotoContent>()
+            if (replyTo == null) {
+                reply(it) {
+                    +"Reply to photo for using of this command"
+                }
+                return@handleSetProfilePhoto
+            }
+
+            val set = runCatching {
+                val file = downloadFileToTemp(replyTo.content)
+                setBusinessAccountProfilePhoto(
+                    businessConnectionId,
+                    InputProfilePhoto.Static(
+                        file.multipartFile()
+                    ),
+                    isPublic = isPublic
+                )
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (set) {
+                    +"Account profile photo has been set. It will be reset within 15 seconds"
+                } else {
+                    +"Account profile photo has not been set"
+                }
+            }
+            if (set == false) { return@handleSetProfilePhoto }
+            delay(15.seconds)
+            val reset = runCatching {
+                removeBusinessAccountProfilePhoto(
+                    businessConnectionId,
+                    isPublic = isPublic
+                )
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (reset) {
+                    +"Account profile photo has been reset"
+                } else {
+                    +"Account profile photo has not been set. Set it manually"
+                }
+            }
+        }
+        onCommand("set_business_account_profile_photo", initialFilter = { it.chat is PrivateChat }) {
+            handleSetProfilePhoto(it, false)
+        }
+        onCommand("set_business_account_profile_photo_public", initialFilter = { it.chat is PrivateChat }) {
+            handleSetProfilePhoto(it, true)
+        }
+
+        onCommand("post_story", initialFilter = { it.chat is PrivateChat }) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
+            val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<VisualMediaGroupPartContent>()
+            if (replyTo == null) {
+                reply(it) {
+                    +"Reply to photo or video for using of this command"
+                }
+                return@onCommand
+            }
+
+            val posted = runCatching {
+                val file = downloadFileToTemp(replyTo.content)
+                postStory(
+                    businessConnectionId,
+                    when (replyTo.content) {
+                        is PhotoContent -> InputStoryContent.Photo(
+                            file.multipartFile()
+                        )
+                        is VideoContent -> InputStoryContent.Video(
+                            file.multipartFile()
+                        )
+                    },
+                    activePeriod = PostStory.ACTIVE_PERIOD_6_HOURS,
+                    areas = listOf(
+                        StoryArea(
+                            StoryAreaPosition(
+                                x = Percentage.of100(50.0),
+                                y = Percentage.of100(50.0),
+                                width = Percentage.of100(8.0),
+                                height = Percentage.of100(8.0),
+                                rotationAngle = 45.0,
+                                cornerRadius = Percentage.of100(4.0),
+                            ),
+                            StoryAreaType.Link(
+                                "https://github.com/InsanusMokrassar/TelegramBotAPI-examples/blob/master/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt"
+                            )
+                        )
+                    )
+                ) {
+                    +"It is test of postStory :)"
+                }
+            }.getOrElse {
+                it.printStackTrace()
+                null
+            }
+            reply(it) {
+                if (posted != null) {
+                    +"Story has been posted. You may unpost it with " + botCommand("remove_story")
+                } else {
+                    +"Story has not been posted"
+                }
+            }
+        }
+
+        onCommand("delete_story", initialFilter = { it.chat is PrivateChat }) {
+            val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
+            val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<StoryContent>()
+            if (replyTo == null) {
+                reply(it) {
+                    +"Reply to photo or video for using of this command"
+                }
+                return@onCommand
+            }
+
+            val deleted = runCatching {
+                deleteStory(businessConnectionId, replyTo.content.story.id)
+            }.getOrElse {
+                it.printStackTrace()
+                false
+            }
+            reply(it) {
+                if (deleted) {
+                    +"Story has been deleted"
+                } else {
+                    +"Story has not been deleted"
+                }
+            }
+        }
     }.second.join()
 }
\ No newline at end of file
diff --git a/CustomBot/src/main/kotlin/CustomBot.kt b/CustomBot/src/main/kotlin/CustomBot.kt
index 6cf9334..4bf65a2 100644
--- a/CustomBot/src/main/kotlin/CustomBot.kt
+++ b/CustomBot/src/main/kotlin/CustomBot.kt
@@ -4,6 +4,7 @@ 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.behaviour_builder.BehaviourContextData
 import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
 import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
@@ -65,6 +66,7 @@ suspend fun main(vararg args: String) {
         onCommand("start") {
             println(data.update)
             println(data.commonMessage)
+            println(getChat(it.chat))
         }
 
         onCommand(
diff --git a/InlineQueriesBot/build.gradle b/InlineQueriesBot/build.gradle
index 7131a21..49b7e81 100644
--- a/InlineQueriesBot/build.gradle
+++ b/InlineQueriesBot/build.gradle
@@ -12,14 +12,16 @@ plugins {
     id "org.jetbrains.kotlin.multiplatform"
 }
 
-apply plugin: 'application'
-
-mainClassName="InlineQueriesBotKt"
-
 apply from: "$nativePartTemplate"
 
 kotlin {
-    jvm()
+    jvm {
+        binaries {
+            executable {
+                mainClass.set("InlineQueriesBotKt")
+            }
+        }
+    }
 
     sourceSets {
         commonMain {
@@ -27,12 +29,9 @@ kotlin {
                 implementation kotlin('stdlib')
 
                 api "dev.inmo:tgbotapi:$telegram_bot_api_version"
+                api "io.ktor:ktor-client-logging:$ktor_version"
             }
         }
     }
 }
 
-dependencies {
-    implementation 'io.ktor:ktor-client-logging-jvm:3.1.0'
-}
-
diff --git a/InlineQueriesBot/src/commonMain/kotlin/Bot.kt b/InlineQueriesBot/src/commonMain/kotlin/Bot.kt
index 18019d8..c89df0f 100644
--- a/InlineQueriesBot/src/commonMain/kotlin/Bot.kt
+++ b/InlineQueriesBot/src/commonMain/kotlin/Bot.kt
@@ -1,4 +1,4 @@
-import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
+import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
 import dev.inmo.tgbotapi.extensions.api.answers.answer
 import dev.inmo.tgbotapi.extensions.api.bot.getMe
 import dev.inmo.tgbotapi.extensions.api.send.reply
@@ -59,7 +59,7 @@ suspend fun doInlineQueriesBot(token: String) {
             reply(message, deepLink)
         }
 
-        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
+        allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
             println(it)
         }
 
diff --git a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt
index 9781465..1be39cf 100644
--- a/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt
+++ b/KeyboardsBot/KeyboardsBotLib/src/commonMain/kotlin/KeyboardsBot.kt
@@ -1,4 +1,4 @@
-import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
+import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
 import dev.inmo.tgbotapi.bot.ktor.telegramBot
 import dev.inmo.tgbotapi.extensions.api.answers.answer
 import dev.inmo.tgbotapi.extensions.api.bot.getMe
@@ -89,7 +89,7 @@ suspend fun activateKeyboardsBot(
         onCommandWithArgs("inline") { message, args ->
             val numberArgs = args.mapNotNull { it.toIntOrNull() }
             val numberOfPages = numberArgs.getOrNull(1) ?: numberArgs.firstOrNull() ?: 10
-            val page = numberArgs.firstOrNull() ?.takeIf { numberArgs.size > 1 } ?.coerceAtLeast(1) ?: 1
+            val page = numberArgs.firstOrNull()?.takeIf { numberArgs.size > 1 }?.coerceAtLeast(1) ?: 1
             reply(
                 message,
                 replyMarkup = inlineKeyboard {
@@ -138,7 +138,8 @@ suspend fun activateKeyboardsBot(
 
         onBaseInlineQuery {
             val page = it.query.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
-            val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
+            val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }
+                .toIntOrNull() ?: return@onBaseInlineQuery
 
             answer(
                 it,
@@ -170,7 +171,7 @@ suspend fun activateKeyboardsBot(
 
         setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))
 
-        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
+        allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
             println(it)
         }
     }.join()
diff --git a/RandomFileSenderBot/build.gradle b/RandomFileSenderBot/build.gradle
index d4e2f24..8626fe1 100644
--- a/RandomFileSenderBot/build.gradle
+++ b/RandomFileSenderBot/build.gradle
@@ -12,12 +12,14 @@ plugins {
     id "org.jetbrains.kotlin.multiplatform"
 }
 
-apply plugin: 'application'
-
-mainClassName="RandomFileSenderBotKt"
-
 kotlin {
-    jvm()
+    jvm {
+        binaries {
+            executable {
+                mainClass.set("RandomFileSenderBotKt")
+            }
+        }
+    }
 
     sourceSets {
         commonMain {
diff --git a/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt b/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt
index 69fb356..094ff67 100644
--- a/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt
+++ b/ResenderBot/ResenderBotLib/src/commonMain/kotlin/ResenderBot.kt
@@ -1,4 +1,4 @@
-import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
+import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
 import dev.inmo.tgbotapi.extensions.api.bot.getMe
 import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
 import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
@@ -31,15 +31,15 @@ suspend fun activateResenderBot(
                     it.content.createResend(
                         chat.id,
                         messageThreadId = it.threadIdOrNull,
-                        replyParameters = it.replyInfo ?.messageMeta ?.let { meta ->
-                            val quote = it.withContentOrNull<TextContent>() ?.content ?.quote
+                        replyParameters = it.replyInfo?.messageMeta?.let { meta ->
+                            val quote = it.withContentOrNull<TextContent>()?.content?.quote
                             ReplyParameters(
                                 meta,
-                                entities = quote ?.textSources ?: emptyList(),
-                                quotePosition = quote ?.position
+                                entities = quote?.textSources ?: emptyList(),
+                                quotePosition = quote?.position
                             )
                         },
-                        effectId = it.possiblyWithEffectMessageOrNull() ?.effectId
+                        effectId = it.possiblyWithEffectMessageOrNull()?.effectId
                     )
                 ) {
                     it.forEach(print)
@@ -49,7 +49,7 @@ suspend fun activateResenderBot(
             println("Answer info: $answer")
         }
 
-        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
+        allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
             println(it)
         }
         print(bot.getMe())
diff --git a/StickerInfoBot/StickerInfoBotLib/src/commonMain/kotlin/StickerInfoBot.kt b/StickerInfoBot/StickerInfoBotLib/src/commonMain/kotlin/StickerInfoBot.kt
index da0db26..19aefe1 100644
--- a/StickerInfoBot/StickerInfoBotLib/src/commonMain/kotlin/StickerInfoBot.kt
+++ b/StickerInfoBot/StickerInfoBotLib/src/commonMain/kotlin/StickerInfoBot.kt
@@ -1,5 +1,5 @@
 import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
-import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
+import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
 import dev.inmo.tgbotapi.bot.ktor.telegramBot
 import dev.inmo.tgbotapi.extensions.api.bot.getMe
 import dev.inmo.tgbotapi.extensions.api.get.getCustomEmojiStickerOrNull
@@ -55,7 +55,7 @@ suspend fun activateStickerInfoBot(
             withTypingAction(it.chat) {
                 it.content.textSources.mapNotNull {
                     if (it is CustomEmojiTextSource) {
-                        getCustomEmojiStickerOrNull(it.customEmojiId) ?.stickerSetName
+                        getCustomEmojiStickerOrNull(it.customEmojiId)?.stickerSetName
                     } else {
                         null
                     }
@@ -76,7 +76,7 @@ suspend fun activateStickerInfoBot(
             )
         }
 
-        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
+        allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
             println(it)
         }
     }.join()
diff --git a/WebApp/build.gradle b/WebApp/build.gradle
index 8e04927..22cad8d 100644
--- a/WebApp/build.gradle
+++ b/WebApp/build.gradle
@@ -16,10 +16,14 @@ plugins {
     id "org.jetbrains.compose" version "$compose_version"
 }
 
-apply plugin: 'application'
-
 kotlin {
-    jvm()
+    jvm {
+        binaries {
+            executable {
+                mainClass.set("WebAppServerKt")
+            }
+        }
+    }
     js(IR) {
         browser()
         binaries.executable()
@@ -53,10 +57,6 @@ kotlin {
     }
 }
 
-application {
-    mainClassName = "WebAppServerKt"
-}
-
 tasks.getByName("compileKotlinJvm")
         .dependsOn(jsBrowserDistribution)
 tasks.getByName("compileKotlinJvm").configure {
diff --git a/WebApp/src/jsMain/kotlin/main.kt b/WebApp/src/jsMain/kotlin/main.kt
index fa1c3d0..609850c 100644
--- a/WebApp/src/jsMain/kotlin/main.kt
+++ b/WebApp/src/jsMain/kotlin/main.kt
@@ -1,6 +1,5 @@
 import androidx.compose.runtime.*
-import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
-import dev.inmo.tgbotapi.types.CustomEmojiId
+import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions
 import dev.inmo.tgbotapi.types.userIdField
 import dev.inmo.tgbotapi.types.webAppQueryIdField
 import dev.inmo.tgbotapi.webapps.*
@@ -23,10 +22,12 @@ import kotlinx.dom.appendElement
 import kotlinx.dom.appendText
 import kotlinx.serialization.json.Json
 import org.jetbrains.compose.web.attributes.InputType
-import org.jetbrains.compose.web.css.DisplayStyle
+import org.jetbrains.compose.web.attributes.placeholder
+import org.jetbrains.compose.web.css.Style
+import org.jetbrains.compose.web.css.StyleSheet
 import org.jetbrains.compose.web.css.Color as ComposeColor
 import org.jetbrains.compose.web.css.backgroundColor
-import org.jetbrains.compose.web.css.display
+import org.jetbrains.compose.web.css.color
 import org.jetbrains.compose.web.dom.*
 import org.jetbrains.compose.web.dom.Text
 import org.jetbrains.compose.web.renderComposable
@@ -39,6 +40,13 @@ fun HTMLElement.log(text: String) {
     appendElement("p", {})
 }
 
+private object RootStyleSheet : StyleSheet() {
+    val rootClass by style {
+        color(ComposeColor("var(--tg-theme-text-color)"))
+        backgroundColor(ComposeColor("var(--tg-theme-bg-color)"))
+    }
+}
+
 @OptIn(ExperimentalUnsignedTypes::class)
 fun main() {
     console.log("Web app started")
@@ -46,6 +54,14 @@ fun main() {
     val baseUrl = window.location.origin.removeSuffix("/")
 
     renderComposable("root") {
+        Style(RootStyleSheet)
+        DisposableEffect(null) {
+            scopeElement.classList.add(RootStyleSheet.rootClass)
+
+            onDispose {
+                scopeElement.classList.remove(RootStyleSheet.rootClass)
+            }
+        }
         val scope = rememberCoroutineScope()
         val isSafeState = remember { mutableStateOf<Boolean?>(null) }
         val logsState = remember { mutableStateListOf<Any?>() }
@@ -87,6 +103,7 @@ fun main() {
         P()
         Text("Chat from WebAppInitData: ${webApp.initDataUnsafe.chat}")
 
+        H3 { Text("Emoji status management") }
         val emojiStatusAccessState = remember { mutableStateOf(false) }
         webApp.onEmojiStatusAccessRequested {
             emojiStatusAccessState.value = it.isAllowed
@@ -110,7 +127,7 @@ fun main() {
             userId ?.let { userId ->
                 Button({
                     onClick {
-                        scope.launchSafelyWithoutExceptions {
+                        scope.launchLoggingDropExceptions {
                             client.post("$baseUrl/setCustomEmoji") {
                                 parameter(userIdField, userId.long)
                                 setBody(
@@ -127,10 +144,12 @@ fun main() {
                 }
             }
         }
+        P()
 
+        H3 { Text("Call server method with webAppQueryIdField") }
         Button({
             onClick {
-                scope.launchSafelyWithoutExceptions {
+                scope.launchLoggingDropExceptions {
                     handleResult({ "Clicked" }) {
                         client.post("${window.location.origin.removeSuffix("/")}/inline") {
                             parameter(webAppQueryIdField, it)
@@ -145,10 +164,11 @@ fun main() {
         }
 
         P()
+        H3 { Text("User info") }
         Text("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
 
         P()
-        Text("Alerts:")
+        H3 { Text("Alerts") }
         Button({
             onClick {
                 webApp.showPopup(
@@ -186,8 +206,22 @@ fun main() {
         }) {
             Text("Alert")
         }
+        Button({
+            onClick {
+                webApp.showConfirm(
+                    "This is confirm message"
+                ) {
+                    logsState.add(
+                        "You have pressed \"${if (it) "Ok" else "Cancel"}\" in confirm"
+                    )
+                }
+            }
+        }) {
+            Text("Confirm")
+        }
 
         P()
+        H3 { Text("Write access callbacks") }
         Button({
             onClick {
                 webApp.requestWriteAccess()
@@ -206,6 +240,7 @@ fun main() {
         }
 
         P()
+        H3 { Text("Request contact") }
         Button({
             onClick {
                 webApp.requestContact()
@@ -220,24 +255,9 @@ fun main() {
         }) {
             Text("Request contact with callback")
         }
-        P()
-
-        Button({
-            onClick {
-                webApp.showConfirm(
-                    "This is confirm message"
-                ) {
-                    logsState.add(
-                        "You have pressed \"${if (it) "Ok" else "Cancel"}\" in confirm"
-                    )
-                }
-            }
-        }) {
-            Text("Confirm")
-        }
 
         P()
-
+        H3 { Text("Closing confirmation") }
         val isClosingConfirmationEnabledState = remember { mutableStateOf(webApp.isClosingConfirmationEnabled) }
         Button({
             onClick {
@@ -255,7 +275,7 @@ fun main() {
         }
 
         P()
-
+        H3 { Text("Colors") }
         val headerColor = remember { mutableStateOf<Color.Hex>(Color.Hex("#000000")) }
         fun updateHeaderColor() {
             val (r, g, b) = Random.nextUBytes(3)
@@ -280,7 +300,6 @@ fun main() {
         }
 
         P()
-
         val backgroundColor = remember { mutableStateOf<Color.Hex>(Color.Hex("#000000")) }
         fun updateBackgroundColor() {
             val (r, g, b) = Random.nextUBytes(3)
@@ -305,7 +324,6 @@ fun main() {
         }
 
         P()
-
         val bottomBarColor = remember { mutableStateOf<Color.Hex>(Color.Hex("#000000")) }
         fun updateBottomBarColor() {
             val (r, g, b) = Random.nextUBytes(3)
@@ -329,60 +347,6 @@ fun main() {
             }
         }
 
-        P()
-
-        val storageTrigger = remember { mutableStateOf<List<Pair<CloudStorageKey, CloudStorageValue>>>(emptyList()) }
-        fun updateCloudStorage() {
-            webApp.cloudStorage.getAll {
-                it.onSuccess {
-                    storageTrigger.value = it.toList().sortedBy { it.first.key }
-                }
-            }
-        }
-        key(storageTrigger.value) {
-            storageTrigger.value.forEach { (key, value) ->
-                val keyState = remember { mutableStateOf(key.key) }
-                val valueState = remember { mutableStateOf(value.value) }
-                Input(InputType.Text) {
-                    value(key.key)
-                    onInput { keyState.value = it.value }
-                }
-                Input(InputType.Text) {
-                    value(value.value)
-                    onInput { valueState.value = it.value }
-                }
-                Button({
-                    onClick {
-                        if (key.key != keyState.value) {
-                            webApp.cloudStorage.remove(key)
-                        }
-                        webApp.cloudStorage.set(keyState.value, valueState.value)
-                        updateCloudStorage()
-                    }
-                }) {
-                    Text("Save")
-                }
-            }
-            let { // new element adding
-                val keyState = remember { mutableStateOf("") }
-                val valueState = remember { mutableStateOf("") }
-                Input(InputType.Text) {
-                    onInput { keyState.value = it.value }
-                }
-                Input(InputType.Text) {
-                    onInput { valueState.value = it.value }
-                }
-                Button({
-                    onClick {
-                        webApp.cloudStorage.set(keyState.value, valueState.value)
-                        updateCloudStorage()
-                    }
-                }) {
-                    Text("Save")
-                }
-            }
-        }
-
         remember {
             webApp.apply {
 
@@ -432,9 +396,10 @@ fun main() {
                 }
             }
         }
-        P()
 
-        let { // Accelerometer
+        P()
+        let {
+            H3 { Text("Accelerometer") }
             val enabledState = remember { mutableStateOf(webApp.accelerometer.isStarted) }
             webApp.onAccelerometerStarted { enabledState.value = true }
             webApp.onAccelerometerStopped { enabledState.value = false }
@@ -475,7 +440,8 @@ fun main() {
         }
         P()
 
-        let { // Gyroscope
+        let {
+            H3 { Text("Gyroscope") }
             val enabledState = remember { mutableStateOf(webApp.gyroscope.isStarted) }
             webApp.onGyroscopeStarted { enabledState.value = true }
             webApp.onGyroscopeStopped { enabledState.value = false }
@@ -514,9 +480,10 @@ fun main() {
                 Text("z: ${zState.value}")
             }
         }
-        P()
 
-        let { // DeviceOrientation
+        P()
+        let {
+            H3 { Text("Device Orientation") }
             val enabledState = remember { mutableStateOf(webApp.deviceOrientation.isStarted) }
             webApp.onDeviceOrientationStarted { enabledState.value = true }
             webApp.onDeviceOrientationStopped { enabledState.value = false }
@@ -555,8 +522,181 @@ fun main() {
                 Text("gamma: ${gammaState.value}")
             }
         }
+
+        P()
+        H3 { Text("Cloud storage") }
+        val storageTrigger = remember { mutableStateOf<List<Pair<CloudStorageKey, CloudStorageValue>>>(emptyList()) }
+        fun updateCloudStorage() {
+            webApp.cloudStorage.getAll {
+                it.onSuccess {
+                    storageTrigger.value = it.toList().sortedBy { it.first.key }
+                }
+            }
+        }
+        key(storageTrigger.value) {
+            storageTrigger.value.forEach { (key, value) ->
+                val keyState = remember { mutableStateOf(key.key) }
+                val valueState = remember { mutableStateOf(value.value) }
+                Input(InputType.Text) {
+                    value(key.key)
+                    onInput { keyState.value = it.value }
+                }
+                Input(InputType.Text) {
+                    value(value.value)
+                    onInput { valueState.value = it.value }
+                }
+                Button({
+                    onClick {
+                        if (key.key != keyState.value) {
+                            webApp.cloudStorage.remove(key)
+                        }
+                        webApp.cloudStorage.set(keyState.value, valueState.value)
+                        updateCloudStorage()
+                    }
+                }) {
+                    Text("Save")
+                }
+            }
+            let { // new element adding
+                val keyState = remember { mutableStateOf("") }
+                val valueState = remember { mutableStateOf("") }
+                Input(InputType.Text) {
+                    onInput { keyState.value = it.value }
+                }
+                Input(InputType.Text) {
+                    onInput { valueState.value = it.value }
+                }
+                Button({
+                    onClick {
+                        webApp.cloudStorage.set(keyState.value, valueState.value)
+                        updateCloudStorage()
+                    }
+                }) {
+                    Text("Save")
+                }
+            }
+        }
+
+        P()
+        let { // DeviceStorage
+            H3 { Text("Device storage") }
+            val fieldKey = remember { mutableStateOf("") }
+            val fieldValue = remember { mutableStateOf("") }
+            val message = remember { mutableStateOf("") }
+            Div {
+                Text("Start type title of key. If value will be found in device storage, it will be shown in value input")
+            }
+
+            Input(InputType.Text) {
+                placeholder("Key")
+                value(fieldKey.value)
+                onInput {
+                    fieldKey.value = it.value
+                    webApp.deviceStorage.getItem(it.value) { e, v ->
+                        fieldValue.value = v ?: ""
+                        if (v == null) {
+                            message.value = "Value for key \"${it.value}\" has not been found"
+                        } else {
+                            message.value = "Value for key \"${it.value}\" has been found: \"$v\""
+                        }
+                    }
+                }
+            }
+            Div {
+                Text("If you want to change value if typed key - just put it here")
+            }
+            Input(InputType.Text) {
+                placeholder("Value")
+                value(fieldValue.value)
+                onInput {
+                    fieldValue.value = it.value
+                    webApp.deviceStorage.setItem(fieldKey.value, it.value) { e, v ->
+                        if (v == true) {
+                            fieldValue.value = it.value
+                            message.value = "Value \"${it.value}\" has been saved"
+                        }
+                    }
+                }
+            }
+
+            if (message.value.isNotEmpty()) {
+                Div { Text(message.value) }
+            }
+        }
         P()
 
+        let { // DeviceStorage
+            H3 { Text("Secure storage") }
+            val fieldKey = remember { mutableStateOf("") }
+            val fieldValue = remember { mutableStateOf("") }
+            val message = remember { mutableStateOf("") }
+            val restorableState = remember { mutableStateOf(false) }
+            Div {
+                Text("Start type title of key. If value will be found in device storage, it will be shown in value input")
+            }
+
+            Input(InputType.Text) {
+                placeholder("Key")
+                value(fieldKey.value)
+                onInput {
+                    fieldKey.value = it.value
+                    webApp.secureStorage.getItem(it.value) { e, v, restorable ->
+                        fieldValue.value = v ?: ""
+                        restorableState.value = restorable == true
+                        if (v == null) {
+                            if (restorable == true) {
+                                message.value = "Value for key \"${it.value}\" has not been found, but can be restored"
+                            } else {
+                                message.value = "Value for key \"${it.value}\" has not been found. Error: $e"
+                            }
+                        } else {
+                            message.value = "Value for key \"${it.value}\" has been found: \"$v\""
+                        }
+                    }
+                }
+            }
+            if (restorableState.value) {
+                Button({
+                    onClick {
+                        webApp.secureStorage.restoreItem(fieldKey.value) { e, v ->
+                            fieldValue.value = v ?: ""
+                            if (v == null) {
+                                message.value = "Value for key \"${fieldKey.value}\" has not been restored. Error: $e"
+                            } else {
+                                message.value = "Value for key \"${fieldKey.value}\" has been restored: \"$v\""
+                            }
+                        }
+                    }
+                }) {
+                    Text("Restore")
+                }
+            }
+            Div {
+                Text("If you want to change value if typed key - just put it here")
+            }
+            Input(InputType.Text) {
+                placeholder("Value")
+                value(fieldValue.value)
+                onInput {
+                    fieldValue.value = it.value
+                    webApp.secureStorage.setItem(fieldKey.value, it.value) { e, v ->
+                        if (v) {
+                            fieldValue.value = it.value
+                            message.value = "Value \"${it.value}\" has been saved"
+                        } else {
+                            message.value = "Value \"${it.value}\" has not been saved. Error: $e"
+                        }
+                    }
+                }
+            }
+
+            if (message.value.isNotEmpty()) {
+                Div { Text(message.value) }
+            }
+        }
+        P()
+
+        H3 { Text("Events") }
         EventType.values().forEach { eventType ->
             when (eventType) {
                 EventType.AccelerometerChanged -> webApp.onAccelerometerChanged { /*logsState.add("AccelerometerChanged") /* see accelerometer block */ */ }
diff --git a/WebApp/src/jvmMain/kotlin/WebAppServer.kt b/WebApp/src/jvmMain/kotlin/WebAppServer.kt
index d6ed35f..11b1f8a 100644
--- a/WebApp/src/jvmMain/kotlin/WebAppServer.kt
+++ b/WebApp/src/jvmMain/kotlin/WebAppServer.kt
@@ -1,4 +1,5 @@
 import dev.inmo.kslog.common.*
+import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
 import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
 import dev.inmo.micro_utils.ktor.server.createKtorServer
 import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery
@@ -195,7 +196,7 @@ suspend fun main(vararg args: String) {
             BotCommand("reply_markup", "Use to get reply markup keyboard with web app trigger"),
             BotCommand("inline", "Use to get inline keyboard with web app trigger"),
         )
-        allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
+        allUpdatesFlow.subscribeLoggingDropExceptions(this) {
             println(it)
         }
         println(getMe())
diff --git a/build.gradle b/build.gradle
index ddbaec4..491d1cc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,8 +14,8 @@ allprojects {
         nativePartTemplate = "${rootProject.projectDir.absolutePath}/native_template.gradle"
     }
     repositories {
-        mavenLocal()
         mavenCentral()
+        google()
         if (project.hasProperty("GITHUB_USER") && project.hasProperty("GITHUB_TOKEN")) {
             maven {
                 url "https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"
@@ -26,7 +26,8 @@ allprojects {
             }
         }
 
-        maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
+        maven { url "https://proxy.nexus.inmo.dev/repository/maven-releases/" }
+        mavenLocal()
     }
 }
 
diff --git a/gradle.properties b/gradle.properties
index dc5e890..f2a00ce 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,9 +5,9 @@ org.gradle.jvmargs=-Xmx3148m
 kotlin.daemon.jvmargs=-Xmx3g -Xms500m
 
 
-kotlin_version=2.1.10
-telegram_bot_api_version=23.2.0
-micro_utils_version=0.24.6
+kotlin_version=2.1.20
+telegram_bot_api_version=25.0.0
+micro_utils_version=0.25.3
 serialization_version=1.8.0
-ktor_version=3.1.0
+ktor_version=3.1.1
 compose_version=1.7.3