Compare commits

...

53 Commits

Author SHA1 Message Date
renovate[bot]
f934a286a3 Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.10.0 2026-03-05 12:53:13 +00:00
29ad52b506 Merge pull request #352 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v31.2.0
2026-03-05 00:04:38 +06:00
renovate[bot]
b848c6bfad Update telegram_bot_api_version to v31.2.0 2026-03-04 17:50:36 +00:00
6642b95af2 Merge pull request #357 from InsanusMokrassar/31.1.0
31.1.0
2026-03-03 13:31:56 +06:00
828ab43317 add opportunity to set/unset tags rights in tags bot 2026-03-02 19:21:48 +06:00
1a4533221c add showing of custom emoji in button 2026-03-02 18:55:05 +06:00
e304a5ecab add TagsBot 2026-03-02 18:02:35 +06:00
600ac8ebbf Merge pull request #356 from InsanusMokrassar/31.0.0
31.0.0
2026-03-02 16:58:51 +06:00
07403546f4 update telegram bot api version 2026-02-24 15:07:13 +06:00
9d4b7b5a50 add gifts bot 2026-02-24 14:00:26 +06:00
e1f5e40143 fix of build 2026-02-20 19:21:23 +06:00
186a0f7abf update telegram bot api version 2026-02-20 18:58:24 +06:00
e660f06edf add showing of user audios and update telegram bot api version onto temporal version 2026-02-18 22:24:32 +06:00
fb6ed8b7ae rename GetMe to My bot 2026-02-18 19:59:34 +06:00
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
0e8714cf2b Merge pull request #351 from InsanusMokrassar/29.0.0
29.0.0
2025-09-28 10:54:27 +06:00
ea74f884bf change vrsion of telegram bot api 2025-09-25 14:05:44 +06:00
69cbc257b5 Merge branch 'master' into 29.0.0 2025-09-25 14:04:47 +06:00
d3e6014e06 fix dependencies 2025-09-23 18:29:42 +06:00
1d260f82e9 Merge branch 'master' into 29.0.0 2025-09-23 18:29:13 +06:00
5d0b48c4b7 update dependencies 2025-09-23 18:16:40 +06:00
34be1a25b2 update SuggestedPostsBot to include printing of paid post publishing 2025-09-23 16:34:57 +06:00
990614e257 add checklists tasks 2025-09-23 15:43:58 +06:00
0d1bcf05fd improve SuggestedPostsBot 2025-09-23 13:38:54 +06:00
7d5cb58a3f small improvement with suggested post approvement failes 2025-09-23 12:52:31 +06:00
261df14412 improvements in suggested posts bot 2025-09-23 12:49:11 +06:00
81ba5831c3 add suggested posts sample 2025-09-22 12:44:52 +06:00
0b9c715e25 start migration onto 29.0.0 2025-09-18 01:05:03 +06:00
0216919145 Merge pull request #345 from InsanusMokrassar/28.0.0
28.0.0
2025-08-23 16:14:17 +06:00
e2d56a4d80 finalize changes 2025-08-11 15:56:20 +06:00
70aca52960 update dependency 2025-08-11 15:22:21 +06:00
6c0d961339 temporal improvements for fsm 2025-08-11 15:22:21 +06:00
renovate[bot]
a3cdf693f2 Update ktor monorepo to v3.2.3 2025-08-11 15:22:02 +06:00
renovate[bot]
e378c6630c Update telegram_bot_api_version to v27.1.2 2025-08-07 10:58:59 +00:00
707ad9a160 Merge pull request #331 from InsanusMokrassar/renovate/compose_version
Update plugin org.jetbrains.compose to v1.8.2
2025-07-29 17:31:19 +06:00
68e9830a8f Merge pull request #341 from InsanusMokrassar/27.1.0
27.1.0
2025-07-29 17:28:01 +06:00
55ebdeadbc migration onto 27.1.0 2025-07-27 14:32:39 +06:00
renovate[bot]
d4f3d4bc68 Update plugin org.jetbrains.compose to v1.8.2 2025-07-25 17:23:59 +00:00
b3d06c9773 Merge pull request #340 from InsanusMokrassar/27.0.0
27.0.0
2025-07-25 23:22:44 +06:00
e6e3eabf97 update dependencies 2025-07-22 19:52:24 +06:00
47efedf311 start migration onto 27.0.0 2025-07-15 13:35:35 +06:00
8423b1377b Merge pull request #336 from InsanusMokrassar/26.1.0
26.1.0
2025-07-10 17:21:41 +06:00
d0029603ce improvements according to latest changes 2025-07-08 18:41:18 +06:00
8d8fa74779 start migration onto 26.1.0 2025-07-08 11:58:35 +06:00
459a70c47b Merge pull request #333 from InsanusMokrassar/26.0.0
26.0.0
2025-06-16 12:15:06 +06:00
88102f3afa update telegram bot api version up to last release one 2025-06-15 10:33:03 +06:00
a621058fdd update dependencies 2025-06-14 23:48:32 +06:00
56e072aabe Merge pull request #332 from InsanusMokrassar/25.0.0
25.0.0
2025-06-14 23:35:44 +06:00
30 changed files with 1096 additions and 114 deletions

View File

@@ -34,6 +34,7 @@ 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.updates.retrieving.flushAccumulatedUpdates
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.requests.abstracts.multipartFile
import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto
@@ -84,6 +85,7 @@ suspend fun main(args: Array<String>) {
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
println(me)
flushAccumulatedUpdates()
onBusinessConnectionEnabled {
businessConnectionsChatsMutex.withLock {
@@ -471,5 +473,15 @@ suspend fun main(args: Array<String>) {
}
}
}
// Will work when some premium user sending to some other user checklist
onChecklistContent {
execute(
it.content.createResend(
it.chat.id,
businessConnectionId = it.chat.id.businessConnectionId ?: chatsBusinessConnections[it.chat.id] ?: return@onChecklistContent
)
)
}
}.second.join()
}

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="ChecklistsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,120 @@
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.runCatchingLogging
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.resend
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.suggested.approveSuggestedPost
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostApproved
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostDeclined
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.onChecklistContent
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChecklistTasksAdded
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChecklistTasksDone
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApprovalFailed
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApproved
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostDeclined
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostPaid
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostRefunded
import dev.inmo.tgbotapi.extensions.utils.channelDirectMessagesContentMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.previewChannelDirectMessagesChatOrNull
import dev.inmo.tgbotapi.extensions.utils.suggestedChannelDirectMessagesContentMessageOrNull
import dev.inmo.tgbotapi.types.checklists.ChecklistTaskId
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.ChecklistContent
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.code
import dev.inmo.tgbotapi.utils.firstOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
suspend fun main(vararg args: String) {
val botToken = args.first()
val isDebug = args.any { it == "debug" }
val isTestServer = args.any { it == "testServer" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
telegramBotWithBehaviourAndLongPolling(
botToken,
CoroutineScope(Dispatchers.Default),
testServer = isTestServer,
) {
// start here!!
val me = getMe()
println(me)
fun ChecklistContent.textBuilderTextSources(): TextSourcesList {
return buildEntities {
+checklist.textSources + "\n\n"
checklist.tasks.forEach { task ->
+""
code(
if (task.completionDate != null) {
"[x] "
} else {
"[ ] "
}
)
bold(task.textSources) + "\n"
}
}
}
onChecklistContent { messageWithContent ->
reply(messageWithContent) {
+messageWithContent.content.textBuilderTextSources()
}
}
onChecklistTasksDone { eventMessage ->
reply(
eventMessage,
checklistTaskId = eventMessage.chatEvent.markedAsDone ?.firstOrNull()
) {
eventMessage.chatEvent.checklistMessage.content.checklist
+eventMessage.chatEvent.checklistMessage.content.textBuilderTextSources()
}
}
onChecklistTasksAdded { messageWithContent ->
reply(
messageWithContent.chatEvent.checklistMessage,
checklistTaskId = messageWithContent.chatEvent.tasks.firstOrNull() ?.id
) {
+messageWithContent.chatEvent.checklistMessage.content.textBuilderTextSources()
}
}
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
println(it)
}
}.second.join()
}

View File

@@ -4,11 +4,24 @@ 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.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
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.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
@@ -67,6 +80,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(
@@ -79,6 +118,17 @@ suspend fun main(vararg args: String) {
println(data.commonMessage)
}
onCommand("getMyStarBalance") {
reply(
to = it,
text = getMyStarBalance().toString()
)
}
onChannelDirectMessagesConfigurationChanged {
println(it.chatEvent)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}

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

@@ -1,10 +1,13 @@
import dev.inmo.micro_utils.coroutines.awaitFirst
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitCommandMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndFSMAndStartLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.command
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.containsCommand
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
@@ -13,10 +16,12 @@ import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.botCommand
import dev.inmo.tgbotapi.utils.firstOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
sealed interface BotState : State
data class ExpectContentOrStopState(override val context: IdChatIdentifier, val sourceMessage: CommonMessage<TextContent>) : BotState
@@ -48,19 +53,29 @@ suspend fun main(args: Array<String>) {
+"Send me some content or " + botCommand("stop") + " if you want to stop sending"
}
val contentMessage = waitAnyContentMessage().filter { message ->
message.sameThread(it.sourceMessage)
}.first()
val contentMessage = firstOf(
{
waitCommandMessage("stop").filter { message ->
message.sameThread(it.sourceMessage)
}.first()
null
},
{
waitAnyContentMessage().filter { message ->
message.sameThread(it.sourceMessage)
}.filter {
containsCommand(
"stop",
it.withContentOrNull<TextContent>() ?.content ?.textSources ?: return@filter false
) == false
}.first()
}
) ?: return@strictlyOn StopState(it.context)
val content = contentMessage.content
when {
content is TextContent && content.text == "/stop"
|| content is TextContent && content.parseCommandsWithArgs().keys.contains("stop") -> StopState(it.context)
else -> {
execute(content.createResend(it.context))
it
}
}
execute(content.createResend(it.context))
it
}
strictlyOn<StopState> {
send(it.context) { +"You have stopped sending of content" }

View File

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

21
GiftsBot/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="GiftsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

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

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

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

View File

@@ -1,3 +1,6 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.filter.filtered
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
@@ -10,7 +13,9 @@ import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.message.abstracts.BusinessContentMessage
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.currentCoroutineContext
@@ -19,7 +24,10 @@ suspend fun activateResenderBot(
token: String,
print: (Any) -> Unit
) {
telegramBotWithBehaviourAndLongPolling(token, scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
telegramBotWithBehaviourAndLongPolling(
token,
scope = CoroutineScope(currentCoroutineContext() + SupervisorJob()),
) {
onContentMessage(
subcontextUpdatesFilter = MessageFilterByChat,
initialFilter = { it !is BusinessContentMessage<*> || !it.sentByBusinessConnectionOwner }
@@ -30,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,4 +1,19 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
suspend fun main(args: Array<String>) {
val isDebug = args.getOrNull(1) == "debug"
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
activateResenderBot(args.first()) {
println(it)
}

View File

@@ -18,5 +18,5 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation 'io.ktor:ktor-client-logging-jvm:3.1.0'
implementation 'io.ktor:ktor-client-logging-jvm:3.2.3'
}

View File

@@ -1,4 +1,4 @@
# CustomBot
# StarTransactionsBot
This bot basically have no any useful behaviour, but you may customize it as a playground

View File

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

9
SuggestedPosts/README.md Normal file
View File

@@ -0,0 +1,9 @@
# StickerSetHandler
Send sticker to this bot to form your own stickers set. Send /delete to delete this sticker set
## How to run
```bash
./gradlew run --args="TOKEN"
```

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="SuggestedPostsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,140 @@
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.runCatchingLogging
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.resend
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.suggested.approveSuggestedPost
import dev.inmo.tgbotapi.extensions.api.suggested.declineSuggestedPost
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostApproved
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostDeclined
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.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApprovalFailed
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApproved
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostDeclined
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostPaid
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostRefunded
import dev.inmo.tgbotapi.extensions.utils.channelDirectMessagesContentMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.previewChannelDirectMessagesChatOrNull
import dev.inmo.tgbotapi.extensions.utils.suggestedChannelDirectMessagesContentMessageOrNull
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
import dev.inmo.tgbotapi.types.message.abstracts.ChannelPaidPost
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.utils.firstOf
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
/**
* This place can be the playground for your code.
*/
suspend fun main(vararg args: String) {
val botToken = args.first()
val isDebug = args.any { it == "debug" }
val isTestServer = args.any { it == "testServer" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
telegramBotWithBehaviourAndLongPolling(
botToken,
CoroutineScope(Dispatchers.Default),
testServer = isTestServer,
) {
// start here!!
val me = getMe()
println(me)
onCommand("start") {
println(getChat(it.chat))
}
onContentMessage {
val message = it.channelDirectMessagesContentMessageOrNull() ?: return@onContentMessage
val chat = getChat(it.chat)
println(chat)
resend(
message.chat.id,
message.content,
suggestedPostParameters = SuggestedPostParameters()
)
}
onContentMessage(
subcontextUpdatesFilter = { _, _ -> true } // important to not miss updates in channel for waitSuggestedPost events
) { message ->
val suggestedPost = message.suggestedChannelDirectMessagesContentMessageOrNull() ?: return@onContentMessage
firstOf(
{
waitSuggestedPostApproved().filter {
it.suggestedPostMessage ?.chat ?.id == message.chat.id
}.first()
},
{
waitSuggestedPostDeclined().filter {
it.suggestedPostMessage ?.chat ?.id == message.chat.id
}.first()
},
{
for (i in 0 until 3) {
delay(1000L)
send(suggestedPost.chat, "${3 - i}")
}
declineSuggestedPost(suggestedPost)
},
)
}
onContentMessage(initialFilter = { it is ChannelPaidPost<*> }) {
println(it)
}
onSuggestedPostPaid {
println(it)
reply(it, "Paid")
}
onSuggestedPostApproved {
println(it)
reply(it, "Approved")
}
onSuggestedPostDeclined {
println(it)
reply(it, "Declined")
}
onSuggestedPostRefunded {
println(it)
reply(it, "Refunded")
}
onSuggestedPostApprovalFailed {
println(it)
reply(it, "Approval failed")
}
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
println(it)
}
}.second.join()
}

21
TagsBot/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="TagsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,101 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.abstracts.FromUser
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGiftsFlow
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChatAdministrator
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChatMember
import dev.inmo.tgbotapi.extensions.api.chat.members.setChatMemberTag
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.onContentMessage
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.extensions.utils.extensions.raw.sender_chat
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_tag
import dev.inmo.tgbotapi.extensions.utils.fromUserOrNull
import dev.inmo.tgbotapi.extensions.utils.groupContentMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.idChatIdentifierOrNull
import dev.inmo.tgbotapi.extensions.utils.potentiallyFromUserGroupContentMessageOrNull
import dev.inmo.tgbotapi.types.UserTag
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.abstracts.OptionallyFromUserMessage
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("setChatMemberTag", requireOnlyCommandInMessage = false) {
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
val title = it.content.text.removePrefix("/setChatMemberTag").removePrefix(" ")
setChatMemberTag(
chatId = reply.chat.id,
userId = reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
tag = UserTag(title)
)
}
onCommand("setCanManageTags", requireOnlyCommandInMessage = false) {
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
val setOrUnset = it.content.text.removePrefix("/setCanManageTags").removePrefix(" ") == "true"
promoteChatAdministrator(
it.chat.id,
reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
canManageTags = setOrUnset
)
}
onCommand("removeChatMemberTag") {
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
setChatMemberTag(
chatId = reply.chat.id,
userId = reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
tag = null
)
}
onContentMessage {
val groupContentMessage = it.potentiallyFromUserGroupContentMessageOrNull() ?: return@onContentMessage
reply(it, "Tag after casting: ${groupContentMessage.senderTag}")
reply(it, "Tag by getting via risk API: ${it.sender_tag}")
}
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
println(it)
}
}.second.join()
}

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

@@ -1,5 +1,6 @@
import androidx.compose.runtime.*
import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions
import dev.inmo.tgbotapi.types.CustomEmojiId
import dev.inmo.tgbotapi.types.userIdField
import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.webapps.*
@@ -163,6 +164,34 @@ fun main() {
Text("Answer in chat button")
}
H3 { Text("Hide keyboard") }
val hideCountdown = remember { mutableStateOf<Int?>(null) }
Button({
onClick {
hideCountdown.value = 5
}
}) {
if (hideCountdown.value == null) {
Text("Hide")
} else {
Text("Hide in ${hideCountdown.value} seconds")
}
}
LaunchedEffect(hideCountdown.value) {
val value = hideCountdown.value
when {
value == null -> return@LaunchedEffect
value > 0 -> {
delay(1000)
hideCountdown.value = hideCountdown.value ?.minus(1)
}
else -> {
webApp.hideKeyboard()
hideCountdown.value = null
}
}
}
P()
H3 { Text("User info") }
Text("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
@@ -367,6 +396,11 @@ fun main() {
}
mainButton.apply {
setText("Main button")
setParams(
BottomButtonParams(
iconCustomEmojiId = CustomEmojiId("5370976574969486150") // 😏
)
)
onClick {
logsState.add("Main button clicked")
hapticFeedback.notificationOccurred(
@@ -377,6 +411,11 @@ fun main() {
}
secondaryButton.apply {
setText("Secondary button")
setParams(
BottomButtonParams(
iconCustomEmojiId = CustomEmojiId("5370763368497944736") // 😒
)
)
onClick {
logsState.add("Secondary button clicked")
hapticFeedback.notificationOccurred(

View File

@@ -5,9 +5,9 @@ org.gradle.jvmargs=-Xmx3148m
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
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.1
compose_version=1.7.3
kotlin_version=2.2.21
telegram_bot_api_version=31.2.0
micro_utils_version=0.26.9
serialization_version=1.10.0
ktor_version=3.3.2
compose_version=1.8.2

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip

View File

@@ -6,7 +6,7 @@ include ":HelloBot"
include ":PollsBot"
include ":GetMeBot"
include ":MyBot"
include ":DeepLinksBot"
@@ -59,3 +59,13 @@ include ":CustomBot"
include ":MemberUpdatedWatcherBot"
include ":WebHooks"
include ":SuggestedPosts"
include ":ChecklistsBot"
include ":DraftsBot"
include ":GiftsBot"
include ":TagsBot"