Compare commits

...

118 Commits

Author SHA1 Message Date
renovate[bot]
aadedaa413 Update plugin org.jetbrains.kotlin.plugin.compose to v2.2.21 2025-11-11 00:31:00 +00: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
73f05bbcd7 fix in repositories 2025-06-14 23:21:00 +06:00
f053013360 fix of build with adding google repository in webapp sample? 2025-06-14 22:28:14 +06:00
bc39279c6c update telegram bot api version 2025-06-01 22:18:08 +06:00
ad8fa92e87 protess on tests 2025-06-01 22:15:57 +06:00
b0554adb7f add delete_story test 2025-06-01 21:19:37 +06:00
ad90180def fixes 2025-06-01 20:23:19 +06:00
69eda92bc7 add set business account profile photo tests 2025-06-01 19:24:01 +06:00
aee070c6c6 add check of set business account bio 2025-05-25 19:42:47 +06:00
36163d5619 add sample for set business account username 2025-05-25 19:30:51 +06:00
92d1c7a402 add sections and update version of library 2025-05-25 13:49:02 +06:00
7ce784d0a2 add device storage sample and beckground/text color config in webapps 2025-05-25 13:01:22 +06:00
d203d48391 add support of setBusinessAccountName 2025-05-24 22:32:10 +06:00
9352bb0090 add tests for reading and removing business messages 2025-05-18 22:04:03 +06:00
b1bb11d826 update nexus.inmo.dev maven repo 2025-05-18 21:13:43 +06:00
349517462e start add tests for new business account features 2025-05-11 21:28:32 +06:00
1708cad654 start migration onto 25.0.0 2025-05-11 21:13:58 +06:00
f87a9c5c66 Merge pull request #316 from InsanusMokrassar/renovate/kotlin-monorepo
Update kotlin monorepo to v2.1.10
2025-02-15 16:46:54 +06:00
a7b54e4b63 Merge pull request #320 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.24.6
2025-02-15 16:45:39 +06:00
renovate[bot]
436213492d Update kotlin monorepo to v2.1.10 2025-02-15 10:45:34 +00:00
renovate[bot]
0c2110a71d Update dependency dev.inmo:micro_utils.ktor.server to v0.24.6 2025-02-15 10:45:29 +00:00
949fa1a429 Merge pull request #319 from InsanusMokrassar/renovate/ktor-monorepo
Update ktor monorepo to v3.1.0
2025-02-15 16:45:04 +06:00
97cdd5a95f Merge pull request #321 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v23.2.0
2025-02-15 16:44:49 +06:00
renovate[bot]
0cb116acef Update telegram_bot_api_version to v23.2.0 2025-02-15 10:32:03 +00:00
renovate[bot]
a0332c4efd Update ktor monorepo to v3.1.0 2025-02-11 19:14:32 +00:00
f6bce640da update dependencies 2025-02-01 09:26:06 +06:00
d22a99da19 Merge pull request #315 from InsanusMokrassar/23.1.1
23.1.1
2025-01-29 09:09:14 +06:00
467a3a1710 Update gradle.properties 2025-01-27 10:57:07 +06:00
5810bc5930 migration onto 23.1.1 2025-01-27 09:20:08 +06:00
2cf2c4264e Merge pull request #310 from InsanusMokrassar/renovate/ktor-monorepo
Update ktor monorepo to v3.0.3
2025-01-03 14:17:33 +06:00
renovate[bot]
3d5c2ee4b8 Update ktor monorepo to v3.0.3 2025-01-03 08:17:24 +00:00
360c6b4364 Merge pull request #312 from InsanusMokrassar/renovate/compose_version
Update plugin org.jetbrains.compose to v1.7.3
2025-01-03 14:16:48 +06:00
renovate[bot]
bb6a0a125a Update plugin org.jetbrains.compose to v1.7.3 2024-12-20 01:19:28 +00:00
6a61da2eb7 Merge pull request #311 from InsanusMokrassar/22.0.0
22.0.0
2024-12-09 08:54:48 +06:00
8cd75673f5 add opportunity to set custom emoji status from webapp 2024-12-08 13:17:39 +06:00
d294d0ef59 update events listeners 2024-12-08 11:58:47 +06:00
2ab8ccbfdf small refactor in webapp 2024-12-08 10:20:37 +06:00
d12e9aa032 rework to use compose 2024-12-08 10:14:42 +06:00
76f151586e start migration to compose in webapp 2024-12-07 11:36:39 +06:00
1c437690e4 migrate webapp 2024-12-06 16:32:47 +06:00
222c7ec8ee 22.0.0 2024-12-06 13:18:18 +06:00
59778a3add Merge pull request #309 from InsanusMokrassar/21.0.0
21.0.0
2024-11-30 17:58:05 +06:00
3e20835bc6 Update gradle.properties 2024-11-30 17:11:07 +06:00
c3ad2d4319 upgrade custom bot to include context data and additional context data 2024-11-30 14:42:21 +06:00
59fca968d7 update tgbotapi and include sample of context data in custom bot 2024-11-29 12:50:33 +06:00
f03ba5f177 Merge pull request #302 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v20.0.1
2024-11-13 13:53:07 +06:00
renovate[bot]
855d2c1296 Update telegram_bot_api_version to v20.0.1 2024-11-11 10:46:16 +00:00
280f5abce0 Merge pull request #294 from InsanusMokrassar/renovate/major-ktor-monorepo
Update dependency io.ktor:ktor-client-logging-jvm to v3
2024-11-04 01:39:22 +06:00
renovate[bot]
ed81e76ef8 Update dependency io.ktor:ktor-client-logging-jvm to v3 2024-11-03 19:39:16 +00:00
541b76b292 Merge pull request #301 from InsanusMokrassar/20.0.0
20.0.0
2024-11-02 00:34:52 +06:00
5b580b5a15 migration onto 20.0.0 2024-11-01 23:48:29 +06:00
86790ee414 add copyText sample button 2024-11-01 23:21:33 +06:00
0bbe430374 update ktgbotapi 2024-11-01 15:27:17 +06:00
b7d53a7410 Merge pull request #298 from InsanusMokrassar/19.0.0
19.0.0
2024-11-01 14:55:49 +06:00
73064db226 small adaptation 2024-10-30 18:09:23 +06:00
a50eda366d update dependencies and add webhooks sample 2024-10-30 14:38:46 +06:00
e34f0ec9d8 Update gradle-wrapper.properties 2024-10-22 17:36:59 +06:00
c2237f7e87 Merge pull request #297 from InsanusMokrassar/18.2.2
18.2.2
2024-10-22 17:35:39 +06:00
0bbc6a9555 Update gradle.properties 2024-10-22 17:22:41 +06:00
d4d8508abf add middlewares sample in custom bot 2024-10-15 13:52:26 +06:00
9acb64fda9 Merge pull request #291 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.22.4
2024-09-26 07:59:14 +06:00
renovate[bot]
760ae36207 Update dependency dev.inmo:micro_utils.ktor.server to v0.22.4 2024-09-26 01:59:00 +00:00
5c6b1b7171 Merge pull request #290 from InsanusMokrassar/renovate/serialization_version
Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.7.3
2024-09-26 07:58:43 +06:00
6e06357541 Merge pull request #265 from InsanusMokrassar/renovate/ktor-monorepo
Update dependency io.ktor:ktor-client-logging-jvm to v2.3.12
2024-09-26 07:58:28 +06:00
38f46dfa3b Merge pull request #292 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v18.2.1
2024-09-26 07:57:57 +06:00
renovate[bot]
e7f7ef16ac Update telegram_bot_api_version to v18.2.1 2024-09-25 21:35:02 +00:00
renovate[bot]
d100a5a336 Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.7.3 2024-09-19 18:27:24 +00:00
5f0f2ce76d Merge pull request #289 from InsanusMokrassar/18.2.0
18.2.0
2024-09-09 02:33:18 +06:00
14235e7bd4 update GiveawaysBot 2024-09-08 23:38:19 +06:00
6eafd89542 add println of giveaway content 2024-09-08 19:44:51 +06:00
ed2922045c add giveaways bot 2024-09-08 19:33:43 +06:00
21ec50c773 add opportunity to use test server in custom bot 2024-09-08 15:53:46 +06:00
ab362e8c3b start updating up to 18.2.0 2024-09-07 02:43:05 +06:00
346755b41c Merge pull request #282 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v18.1.0
2024-09-05 03:17:20 +06:00
renovate[bot]
a601674d71 Update telegram_bot_api_version to v18.1.0 2024-09-04 21:02:48 +00:00
renovate[bot]
cea610a0f8 Update dependency io.ktor:ktor-client-logging-jvm to v2.3.12 2024-09-02 02:00:00 +00:00
b6c92f754f Merge pull request #287 from InsanusMokrassar/18.0.0
18.0.0
2024-09-02 07:59:08 +06:00
023b810d07 update micro_utils dependency 2024-09-02 01:24:11 +06:00
0ec543d5c5 update sample of MemberUpdatedWatcher bot 2024-08-30 23:32:29 +06:00
777604e5a0 update new samples 2024-08-30 19:06:38 +06:00
999c33b2f5 Merge pull request #286 from Nik-mmzd/chatmemberupdated
Add MemberUpdatedWatcherBot example utilizing new 18.0.0 extensions
2024-08-30 18:42:18 +06:00
ca0427bfdd Merge branch '18.0.0' into chatmemberupdated 2024-08-30 18:41:59 +06:00
a62a14a599 migration onto 18.0.0 2024-08-30 18:40:39 +06:00
McModder
3efd3463a3 Add MemberUpdatedWatcherBot example utilizing new 18.0.0 extensions 2024-08-29 22:40:47 +03:00
590f9ec6d8 Merge pull request #280 from InsanusMokrassar/17.0.0
17.0.0
2024-08-15 19:17:08 +06:00
39 changed files with 2046 additions and 319 deletions

View File

@@ -2,24 +2,69 @@ 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.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
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,21 +79,25 @@ suspend fun main(args: Array<String>) {
}
val businessConnectionsChats = mutableMapOf<BusinessConnectionId, ChatId>()
val chatsBusinessConnections = mutableMapOf<ChatId, BusinessConnectionId>()
val businessConnectionsChatsMutex = Mutex()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
println(me)
flushAccumulatedUpdates()
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 +120,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 +160,328 @@ 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"
}
}
}
// 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,10 +4,27 @@ 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.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChannelDirectMessagesConfigurationChanged
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.update.abstracts.Update
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
private var BehaviourContextData.update: Update?
get() = get("update") as? Update
set(value) = set("update", value)
private var BehaviourContextData.commonMessage: CommonMessage<*>?
get() = get("commonMessage") as? CommonMessage<*>
set(value) = set("commonMessage", value)
/**
* This place can be the playground for your code.
*/
@@ -15,6 +32,7 @@ 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(
@@ -24,11 +42,59 @@ suspend fun main(vararg args: String) {
)
}
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
telegramBotWithBehaviourAndLongPolling(
botToken,
CoroutineScope(Dispatchers.IO),
testServer = isTestServer,
builder = {
includeMiddlewares {
addMiddleware {
doOnRequestReturnResult { result, request, _ ->
println("Result of $request:\n\n$result")
null
}
}
}
},
subcontextInitialAction = buildSubcontextInitialAction {
add {
data.update = it
}
}
) {
// start here!!
val me = getMe()
println(me)
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
onCommand("start") {
println(data.update)
println(data.commonMessage)
println(getChat(it.chat))
}
onCommand(
"additional_command",
additionalSubcontextInitialAction = { update, commonMessage ->
data.commonMessage = commonMessage
}
) {
println(data.update)
println(data.commonMessage)
}
onCommand("getMyStarBalance") {
reply(
to = it,
text = getMyStarBalance().toString()
)
}
onChannelDirectMessagesConfigurationChanged {
println(it.chatEvent)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(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" }

9
GiveawaysBot/README.md Normal file
View File

@@ -0,0 +1,9 @@
# CustomBot
Printing giveaways
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

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

View File

@@ -0,0 +1,57 @@
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.behaviour_builder.telegramBotWithBehaviourAndLongPolling
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
/**
* 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, testServer = isTestServer) {
// start here!!
val me = getMe()
println(me)
onGiveawayCreated {
println(it)
}
onGiveawayCompleted {
println(it)
}
onGiveawayWinners {
println(it)
}
onGiveawayContent {
println(it)
}
// allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
// println(it)
// }
}.second.join()
}

View File

@@ -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:2.3.7'
}

View File

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

View File

@@ -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
@@ -45,6 +45,9 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
}
}
}
row {
copyTextButton("Command copy button", "/inline $page $count")
}
row {
if (page - 1 > 2) {
@@ -84,11 +87,13 @@ suspend fun activateKeyboardsBot(
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onCommandWithArgs("inline") { message, args ->
val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10
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
reply(
message,
replyMarkup = inlineKeyboard {
includePageButtons(1, numberOfPages)
includePageButtons(page, numberOfPages)
}
) {
regular("Your inline keyboard with $numberOfPages pages")
@@ -133,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,
@@ -165,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()

View File

@@ -1,7 +1,21 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.setDefaultKSLog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun main(args: Array<String>) {
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
withContext(Dispatchers.IO) { // IO for inheriting of it in side of activateKeyboardsBot
activateKeyboardsBot(args.first()) {
println(it)

View File

@@ -0,0 +1,10 @@
# MemberUpdatedWatcherBot
This bot will watch for some ChatMemberUpdated events using new extensions from 18.0.0
## Launch
```bash
../gradlew run --args="BOT_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="MemberUpdatedWatcherKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,98 @@
import dev.inmo.kslog.common.*
import dev.inmo.tgbotapi.extensions.api.*
import dev.inmo.tgbotapi.extensions.api.bot.*
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictedFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictionsChangedFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.*
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.utils.*
@OptIn(PreviewFeature::class)
suspend fun main(args: Array<String>) {
val token = args.first()
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val internalLogger = KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag ?: "ChatMemberUpdates", message, throwable))
}
val bot = telegramBot(token)
bot.buildBehaviourWithLongPolling {
val me = getMe()
val filterSelfUpdates = SimpleFilter<ChatMemberUpdated> {
it.member.id == me.id
}
// This bot updates
onChatMemberJoined(initialFilter = filterSelfUpdates) {
internalLogger.i("Bot was added to chat")
send(it.chat.id, "I was added to chat. Please grant me admin permissions to make me able to watch other users' events")
}
onChatMemberGotPromoted(initialFilter = filterSelfUpdates) {
internalLogger.i("Bot was granted admin permissions")
send(it.chat.id, "I was promoted to admin. I now can watch other users' events")
}
onChatMemberGotDemoted(initialFilter = filterSelfUpdates) {
internalLogger.i("Admin permissions were revoked")
send(it.chat.id, "I'm no longer an admin. Admin permissions are required to watch other users' events")
}
// All users updates
onChatMemberJoined {
val member = it.member
internalLogger.i("${member.firstName} joined the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "Welcome ${member.firstName}")
}
onChatMemberLeft {
val member = it.member
internalLogger.i("${member.firstName} left the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "Goodbye ${member.firstName}")
}
onChatMemberGotPromoted {
val newState = it.newChatMemberState.administratorChatMemberOrThrow()
internalLogger.i("${newState.user.firstName} got promoted to ${newState.customTitle ?: "Admin"}: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "${newState.user.firstName} is now an ${newState.customTitle ?: "Admin"}")
}
onChatMemberGotDemoted {
val member = it.member
internalLogger.i("${member.firstName} got demoted: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
send(it.chat.id, "${member.firstName} is now got demoted back to member")
}
onChatMemberGotPromotionChanged {
val member = it.member
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
internalLogger.i(message)
send(it.chat.id, message)
}
onChatMemberUpdated(
initialFilter = chatMemberGotRestrictedFilter + chatMemberGotRestrictionsChangedFilter,
) {
val member = it.member
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
internalLogger.i(message)
send(it.chat.id, message)
}
}.join()
}

View File

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

View File

@@ -1,4 +1,7 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
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
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
@@ -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 }
@@ -31,15 +39,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 +57,7 @@ suspend fun activateResenderBot(
println("Answer info: $answer")
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
println(it)
}
print(bot.getMe())

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:2.3.7'
implementation 'io.ktor:ktor-client-logging-jvm:3.2.3'
}

View File

@@ -106,7 +106,7 @@ suspend fun main(args: Array<String>) {
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
val chatMember = getChatMember(chatId, userId)
return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember {
return chatMember.restrictedMemberChatMemberOrNull() ?: chatMember.whenMemberChatMember {
getChat(chatId).extendedGroupChatOrNull() ?.permissions
}
}

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

@@ -12,10 +12,7 @@ import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.payButton
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.RawChatId
@@ -30,6 +27,7 @@ import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.payments.LabeledPrice
import dev.inmo.tgbotapi.types.payments.stars.StarTransaction
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.regular

View File

@@ -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
@@ -12,7 +12,7 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticke
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.message.textsources.CustomEmojiTextSource
import dev.inmo.tgbotapi.types.message.textsources.regular
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
import dev.inmo.tgbotapi.types.message.textsources.separateForText
import dev.inmo.tgbotapi.types.stickers.StickerSet
import dev.inmo.tgbotapi.utils.bold
@@ -55,14 +55,14 @@ suspend fun activateStickerInfoBot(
withTypingAction(it.chat) {
it.content.textSources.mapNotNull {
if (it is CustomEmojiTextSource) {
getCustomEmojiStickerOrNull(it.customEmojiId) ?.stickerSetName
getCustomEmojiStickerOrNull(it.customEmojiId)?.stickerSetName
} else {
null
}
}.distinct().map {
getStickerSet(it)
}.distinct().flatMap {
it.buildInfo() + regular("\n")
it.buildInfo() + regularTextSource("\n")
}.separateForText().map { entities ->
reply(it, entities)
}
@@ -76,7 +76,7 @@ suspend fun activateStickerInfoBot(
)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
println(it)
}
}.join()

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

View File

@@ -15,8 +15,8 @@ import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.keyboardButtonRequestUserLimit
import dev.inmo.tgbotapi.types.message.textsources.mention
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.mention
import dev.inmo.tgbotapi.utils.row
suspend fun main(args: Array<String>) {
@@ -287,7 +287,7 @@ suspend fun main(args: Array<String>) {
it,
) {
+"You have shared "
+mention(
mention(
when (it.chatEvent.requestId) {
requestIdUserOrBot -> "user or bot"
requestIdUserNonPremium -> "non premium user"

View File

@@ -11,12 +11,19 @@ buildscript {
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "org.jetbrains.kotlin.plugin.compose" version "$kotlin_version"
id "org.jetbrains.compose" version "$compose_version"
}
apply plugin: 'application'
kotlin {
jvm()
jvm {
binaries {
executable {
mainClass.set("WebAppServerKt")
}
}
}
js(IR) {
browser()
binaries.executable()
@@ -27,12 +34,15 @@ kotlin {
dependencies {
implementation kotlin('stdlib')
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
implementation "dev.inmo:tgbotapi.core:$telegram_bot_api_version"
implementation compose.runtime
}
}
jsMain {
dependencies {
implementation "dev.inmo:tgbotapi.webapps:$telegram_bot_api_version"
implementation compose.web.core
}
}
@@ -41,15 +51,12 @@ kotlin {
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
implementation "io.ktor:ktor-server-cio:$ktor_version"
implementation compose.desktop.currentOs
}
}
}
}
application {
mainClassName = "WebAppServerKt"
}
tasks.getByName("compileKotlinJvm")
.dependsOn(jsBrowserDistribution)
tasks.getByName("compileKotlinJvm").configure {

View File

@@ -0,0 +1,3 @@
import dev.inmo.tgbotapi.types.CustomEmojiId
val CustomEmojiIdToSet = CustomEmojiId("5424939566278649034")

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
<title>Web App Example</title>
</head>
<body>
<div id="root"></div>
<script type="application/javascript" src="https://telegram.org/js/telegram-web-app.js"></script>
<script type="application/javascript" src="WebApp.js"></script>
</body>

View File

@@ -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
@@ -6,6 +7,7 @@ import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.set.setUserEmojiStatus
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
@@ -16,12 +18,9 @@ import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.webAppButton
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.InlineQueryId
import dev.inmo.tgbotapi.types.LinkPreviewOptions
import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.*
import io.ktor.http.*
@@ -30,7 +29,6 @@ import io.ktor.server.http.content.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.json.Json
import java.io.File
@@ -63,10 +61,7 @@ suspend fun main(vararg args: String) {
val bot = telegramBot(telegramBotAPIUrlsKeeper)
createKtorServer(
"0.0.0.0",
args.getOrNull(2) ?.toIntOrNull() ?: 8080,
additionalEngineEnvironmentConfigurator = {
parentCoroutineContext += Dispatchers.IO
}
args.getOrNull(2) ?.toIntOrNull() ?: 8080
) {
routing {
val baseJsFolder = File("WebApp/build/dist/js/")
@@ -108,6 +103,26 @@ suspend fun main(vararg args: String) {
call.respond(HttpStatusCode.OK, isSafe.toString())
}
post("setCustomEmoji") {
val requestBody = call.receiveText()
val webAppCheckData = Json.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
val rawUserId = call.parameters[userIdField] ?.toLongOrNull() ?.let(::RawChatId) ?: error("$userIdField should be presented as long value")
val set = if (isSafe) {
runCatching {
bot.setUserEmojiStatus(
UserId(rawUserId),
CustomEmojiIdToSet
)
}.getOrElse { false }
} else {
false
}
call.respond(HttpStatusCode.OK, set.toString())
}
}
}.start(false)
@@ -181,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())

28
WebHooks/README.md Normal file
View File

@@ -0,0 +1,28 @@
# WebHooks
Launches webhook-based simple bot. Use `/start` with bot to get simple info about webhooks
## Launch
```bash
../gradlew run --args="BOT_TOKEN https://sample.com it/is/subpath 8080 debug"
```
Required arguments:
1. Token
2. Arguments starting with `https://`
Optional arguments:
* Any argument == `debug` to enable debug mode
* Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
subroute to place listening of webhooks)
* Any argument as number of port
Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
* `TOKEN` used as token
* Bot will set up its webhook info as `https://sample.com/it/is/subpath`
* Bot will set up to listen webhooks on route `it/is/subpath`
* Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`

23
WebHooks/build.gradle Normal file
View File

@@ -0,0 +1,23 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="WebHooksKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
implementation "io.ktor:ktor-server-cio:$ktor_version"
}

View File

@@ -0,0 +1,87 @@
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.ktor.server.createKtorServer
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.webhook.setWebhookInfo
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.includeWebhookHandlingInRoute
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.utils.buildEntities
import io.ktor.server.routing.*
/**
* Launches webhook-based simple bot. Required arguments:
*
* 1. Token
* *. Arguments starting with `https://`
*
* Optional arguments:
*
* *. Any argument == `debug` to enable debug mode
* *. Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
* subroute to place listening of webhooks)
* *. Any argument as number of port
*
* Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
*
* * `TOKEN` used as token
* * Bot will set up its webhook info as `https://sample.com/it/is/subpath`
* * Bot will set up to listen webhooks on route `it/is/subpath`
* * Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`
*/
suspend fun main(args: Array<String>) {
val botToken = args.first()
val address = args.first { it.startsWith("https://") }
val subpath = args.drop(1).firstOrNull { it != address && it != "debug" }
val port = args.firstNotNullOfOrNull { it.toIntOrNull() } ?: 8080
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val bot = telegramBot(botToken)
val behaviourContext = bot.buildBehaviour (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommand("start", initialFilter = { it.chat is PrivateChat }) {
reply(
it,
buildEntities {
+"Url: $address" + "\n"
+"Listening server: 0.0.0.0" + "\n"
+"Listening port: $port"
}
)
}
setMyCommands(BotCommand("start", "Get webhook info"))
}
val webhookInfoSubpath = subpath ?.let { "/" + it.removePrefix("/") } ?: "" // drop leading `/` to add it in the beginning for correct construction of subpath
bot.setWebhookInfo(address + webhookInfoSubpath)
createKtorServer(
"0.0.0.0",
port,
) {
routing {
if (subpath == null) {
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
} else {
route(subpath) {
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
}
}
}
}.start(true)
}

View File

@@ -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,6 +26,12 @@ allprojects {
}
}
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
maven { url "https://proxy.nexus.inmo.dev/repository/maven-releases/" }
mavenLocal()
}
}
// Fix of https://youtrack.jetbrains.com/issue/KTOR-7912/Module-not-found-errors-when-executing-browserProductionWebpack-task-since-3.0.2
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin.class) {
rootProject.kotlinYarn.resolution("ws", "8.18.0")
}

View File

@@ -5,8 +5,9 @@ org.gradle.jvmargs=-Xmx3148m
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
kotlin_version=2.0.10
telegram_bot_api_version=17.0.0
micro_utils_version=0.22.0
serialization_version=1.7.1
ktor_version=2.3.11
kotlin_version=2.2.21
telegram_bot_api_version=29.0.0
micro_utils_version=0.26.3
serialization_version=1.9.0
ktor_version=3.2.3
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.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip

View File

@@ -52,4 +52,14 @@ include ":BusinessConnectionsBot"
include ":StarTransactionsBot"
include ":GiveawaysBot"
include ":CustomBot"
include ":MemberUpdatedWatcherBot"
include ":WebHooks"
include ":SuggestedPosts"
include ":ChecklistsBot"