Compare commits

...

6 Commits

Author SHA1 Message Date
9981e82a10 small improvement of keyboards bot 2026-02-17 12:45:22 +06:00
bef86042f9 add support of styling for buttons 2026-02-16 23:31:59 +06:00
d1791b3058 add drafts bot 2026-02-16 18:57:43 +06:00
0432611f85 improve test bots 2026-02-16 17:40:23 +06:00
6b27aa01fb fix of topics according to 9.3 2026-02-15 23:34:37 +06:00
523e428bcb update dependencies 2026-02-15 15:52:53 +06:00
8 changed files with 269 additions and 66 deletions

9
DraftsBot/README.md Normal file
View File

@@ -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"
```

21
DraftsBot/build.gradle Normal file
View File

@@ -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"
}

View File

@@ -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<String> {
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()
}

View File

@@ -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 {
@@ -161,7 +163,7 @@ suspend fun activateKeyboardsBot(
it,
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
row {
simpleButton("/inline")
simpleButton("/inline", style = KeyboardButtonStyle.Primary)
}
}
) {

View File

@@ -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<TextContent>()?.content?.quote
ReplyParameters(

View File

@@ -1,15 +1,34 @@
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.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
@@ -20,13 +39,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 +83,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 +109,111 @@ 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)
}
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) {
println(it)
}
}.second.join()
}

View File

@@ -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

View File

@@ -63,3 +63,5 @@ include ":WebHooks"
include ":SuggestedPosts"
include ":ChecklistsBot"
include ":DraftsBot"