1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-16 12:00:18 +00:00

Compare commits

...

35 Commits

Author SHA1 Message Date
421d5ae9e3 Update CHANGELOG.md 2023-03-02 21:59:58 +06:00
54f1181a14 Update libs.versions.toml 2023-03-02 21:59:26 +06:00
f616a02f7c hotfixes 2023-03-02 20:33:49 +06:00
f1deb93147 changes in InlineQuery 2023-03-02 19:33:15 +06:00
2300b44aae fix in CallbackQuery 2023-03-02 12:43:39 +06:00
fe88cf037a fixes in deeplinks 2023-03-02 08:32:06 +06:00
89920abe35 start 6.0.3 2023-03-02 08:31:35 +06:00
4c4aa491cb Merge pull request #729 from InsanusMokrassar/6.0.2
6.0.2
2023-03-01 15:33:43 +06:00
f0a4425be9 Update CHANGELOG.md 2023-03-01 15:31:34 +06:00
6c8af4cab3 fixes in media groups collecting and kdocs 2023-03-01 13:14:18 +06:00
017d57e5e5 fixes 2023-03-01 12:38:50 +06:00
5a456bcdbf add opportunity to collect media groups with debounce 2023-03-01 11:55:17 +06:00
4182d8f3fe start 6.0.2 2023-03-01 00:59:08 +06:00
c6e2cba09b Merge pull request #728 from InsanusMokrassar/6.0.1
6.0.1
2023-02-28 20:41:35 +06:00
8dd3eefd15 Update CHANGELOG.md 2023-02-28 19:59:23 +06:00
b72d4da8f0 Update libs.versions.toml 2023-02-28 19:57:11 +06:00
ebd023669d start 6.0.1 2023-02-28 19:55:29 +06:00
f7be4e557e Merge pull request #726 from InsanusMokrassar/6.0.0
6.0.0
2023-02-28 13:27:41 +06:00
ec434c6af4 fill changelog with dependencies updates 2023-02-27 22:42:28 +06:00
0398590de6 update microutils version to release one 2023-02-27 22:40:26 +06:00
9ef1b54ada *.link renames 2023-02-27 22:32:08 +06:00
c30ce5c803 revert gradle wrapper version 2023-02-27 20:10:45 +06:00
c0a50bccb0 update publish.gradle and gradle wrapper version 2023-02-27 18:40:01 +06:00
e0cd7dc512 TelegramBot.resend 2023-02-27 18:27:23 +06:00
6ff621b428 Add triggers and waiters for VisualMediaGroupPartContent 2023-02-27 18:10:33 +06:00
964a61749c update dependencies 2023-02-27 17:57:32 +06:00
17930091ac start 6.0.0 2023-02-27 17:55:49 +06:00
2271beadfb Update README.md 2023-02-24 15:31:33 +06:00
44c48a8462 Add files via upload 2023-02-24 15:30:36 +06:00
4d35f89ad1 Merge pull request #722 from InsanusMokrassar/5.2.1
5.2.1
2023-02-21 21:44:53 +06:00
174706b189 changelog fill and callback query improvements 2023-02-19 18:35:36 +06:00
fe17312bb5 Update LiveFlowLocation.kt 2023-02-17 15:50:49 +06:00
d8b5789cd2 5.2.1 2023-02-17 15:48:02 +06:00
f27d0916db Merge pull request #721 from InsanusMokrassar/5.2.0
hotfix in LiveFlowLocation (5.2.0)
2023-02-17 15:42:17 +06:00
2d3fe45389 Merge pull request #720 from InsanusMokrassar/5.2.0
5.2.0
2023-02-17 15:36:27 +06:00
34 changed files with 457 additions and 105 deletions

View File

@@ -1,9 +1,54 @@
# TelegramBotAPI changelog # TelegramBotAPI changelog
## 6.0.3
* `Versions`:
* `MicroUtils`: `0.17.1` -> `0.17.2`
* `Core`:
* `User` in `CallbackQuery` now is `CommonUser` as well as in `from`
* `User` in `InlineQuery` now is `CommonUser` as well as in `from`
* `BehaviourBuilder`:
* Fixes in `DeepLink` triggers and waiters
## 6.0.2
* `Core`:
* Long polling now uses media groups debounce as in webhooks
## 6.0.1
* `Versions`:
* `Ktor`: `2.2.3` -> `2.2.4`
* `MicroUtils`: `0.17.0` -> `0.17.1`
## 6.0.0
* `Versions`:
* `Kotlin`: `1.7.22` -> `1.8.10`
* `MicroUtils`: `0.16.10` -> `0.17.0`
* `Serialization`: `1.4.1` -> `1.5.0`
* `uuid`: `0.6.0` -> `0.7.0`
* `Core`:
* `*.link` extensions have been deprecated with renaming to avoid collisions with `link` methods
* `API`:
* Add `TelegramBot.resend` methods
* `BehaviourBuilder`:
* Add triggers and waiters for `VisualMediaGroupPartContent`
* `Utils`:
* `*.link` extensions have been deprecated with renaming to avoid collisions with `link` methods
## 5.2.1
* `Core`:
* All the `CallbackQuery`es now will receive `CommonUser` instead of `User` due inability of bots to trigger any
inline interaction with others bots
* `API`:
* Now `sentMessageFlow` will take each sent message in `handleLiveLocation`
## 5.2.0 ## 5.2.0
* `Versions`: * `Versions`:
* `MicroUtils`: `0.16.8` -> `0.16.10` * `MicroUtils`: `0.16.8` -> `0.16.10`
## 5.1.1 ## 5.1.1

View File

@@ -5,10 +5,10 @@
| Useful repos | [![Create bot](https://img.shields.io/static/v1?label=Github&message=Template&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template/generate) [![Examples](https://img.shields.io/static/v1?label=Github&message=Examples&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/) | | Useful repos | [![Create bot](https://img.shields.io/static/v1?label=Github&message=Template&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template/generate) [![Examples](https://img.shields.io/static/v1?label=Github&message=Examples&color=blue&logo=github)](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/) |
| Misc | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Small survey](https://img.shields.io/static/v1?label=Google&message=Survey&color=blue&logo=google-sheets)](https://docs.google.com/forms/d/e/1FAIpQLSctdJHT_aEniyYT0-IUAEfo1hsIlezX2owlkEAYX4KPl2V2_A/viewform?usp=sf_link) | | Misc | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Small survey](https://img.shields.io/static/v1?label=Google&message=Survey&color=blue&logo=google-sheets)](https://docs.google.com/forms/d/e/1FAIpQLSctdJHT_aEniyYT0-IUAEfo1hsIlezX2owlkEAYX4KPl2V2_A/viewform?usp=sf_link) |
<!--- [![Telegram Channel](./resources/tg_channel_qr.jpg)](https://t.me/InMoTelegramBotAPI) ---> <!--- [![Telegram Channel](./resources/tg_channel_qr.jpg)](https://t.me/ktgbotapi) --->
<p align="center"> <p align="center">
<a href="https://t.me/InMoTelegramBotAPI"> <a href="https://t.me/ktgbotapi">
<img src="./resources/tg_channel_qr.jpg"> <img src="./resources/tg_channel_qr.jpg">
</a> </a>
</p> </p>

View File

@@ -6,4 +6,4 @@ kotlin.incremental=true
kotlin.incremental.js=true kotlin.incremental.js=true
library_group=dev.inmo library_group=dev.inmo
library_version=5.2.0 library_version=6.0.3

View File

@@ -1,19 +1,19 @@
[versions] [versions]
kotlin = "1.7.22" kotlin = "1.8.10"
kotlin-serialization = "1.4.1" kotlin-serialization = "1.5.0"
kotlin-coroutines = "1.6.4" kotlin-coroutines = "1.6.4"
javax-activation = "1.1.1" javax-activation = "1.1.1"
korlibs = "3.4.0" korlibs = "3.4.0"
uuid = "0.6.0" uuid = "0.7.0"
ktor = "2.2.3" ktor = "2.2.4"
ksp = "1.7.22-1.0.8" ksp = "1.8.10-1.0.9"
kotlin-poet = "1.12.0" kotlin-poet = "1.12.0"
microutils = "0.16.10" microutils = "0.17.2"
github-release-plugin = "2.4.1" github-release-plugin = "2.4.1"
dokka = "1.7.20" dokka = "1.7.20"

View File

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

View File

@@ -1,7 +1,8 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
classifier = 'javadoc' archiveClassifier.convention("javadoc")
archiveClassifier.set("javadoc")
} }
publishing { publishing {
@@ -19,22 +20,22 @@ publishing {
} }
developers { developers {
developer { developer {
id = "InsanusMokrassar" id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei" name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com" email = "ovsyannikov.alexey95@gmail.com"
} }
} }
licenses { licenses {
license { license {
name = "Apache Software License 2.0" name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE" url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
} }
} }
} }
repositories { repositories {
@@ -42,55 +43,55 @@ publishing {
maven { maven {
name = "GithubPackages" name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI") url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials { credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
} }
} }
} }
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) { if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven { maven {
name = "Gitea" name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven") url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials(HttpHeaderCredentials) { credentials(HttpHeaderCredentials) {
name = "Authorization" name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN') value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
} }
authentication { authentication {
header(HttpHeaderAuthentication) header(HttpHeaderAuthentication)
} }
} }
} }
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven { maven {
name = "sonatype" name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials { credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
} }
} }
} }
} }
} }
} }
if (project.hasProperty("signing.gnupg.keyName")) { if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing' apply plugin: 'signing'
signing { signing {
useGpgCmd() useGpgCmd()
sign publishing.publications sign publishing.publications
} }
task signAll { task signAll {
tasks.withType(Sign).forEach { tasks.withType(Sign).forEach {
dependsOn(it) dependsOn(it)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

After

Width:  |  Height:  |  Size: 448 KiB

View File

@@ -86,7 +86,9 @@ suspend fun TelegramBot.handleLiveLocation(
it.heading, it.heading,
it.proximityAlertRadius, it.proximityAlertRadius,
it.replyMarkup it.replyMarkup
) ).also {
sentMessageFlow ?.emit(it)
}
} }
} }
} }

View File

@@ -0,0 +1,108 @@
package dev.inmo.tgbotapi.extensions.api.send
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.threadId
/**
* This method will send [content] to the [chatId] as is
*/
suspend inline fun <T : MessageContent> TelegramBot.resend(
chatId: ChatIdentifier,
content: T,
messageThreadId: MessageThreadId? = chatId.threadId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = execute(
content.createResend(
chatId = chatId,
messageThreadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
replyToMessageId = replyToMessageId,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
) as ContentMessage<T>
/**
* This method will send [content] to the [chatId] as is
*/
suspend inline fun <T : MessageContent> TelegramBot.resend(
chat: Chat,
content: T,
messageThreadId: MessageThreadId? = chat.id.threadId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = resend(
chatId = chat.id,
content = content,
messageThreadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
replyToMessageId = replyToMessageId,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
/**
* This method will send [message] content to the [chatId]. In difference with [copyMessage], this method will use
* native methods for data sending (like [dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto] if inoming content is
* [dev.inmo.tgbotapi.types.message.content.PhotoContent])
*/
suspend inline fun <T : MessageContent> TelegramBot.resend(
chatId: ChatIdentifier,
message: ContentMessage<T>,
messageThreadId: MessageThreadId? = chatId.threadId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = resend(
chatId = chatId,
content = message.content,
messageThreadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
replyToMessageId = replyToMessageId,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
/**
* This method will send [message] content to the [chat]. In difference with [copyMessage], this method will use
* native methods for data sending (like [dev.inmo.tgbotapi.extensions.api.send.media.sendPhoto] if inoming content is
* [dev.inmo.tgbotapi.types.message.content.PhotoContent])
*/
suspend inline fun <T : MessageContent> TelegramBot.resend(
chat: Chat,
message: ContentMessage<T>,
messageThreadId: MessageThreadId? = chat.id.threadId,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = resend(
chatId = chat.id,
message = message,
messageThreadId = messageThreadId,
disableNotification = disableNotification,
protectContent = protectContent,
replyToMessageId = replyToMessageId,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)

View File

@@ -1,7 +1,8 @@
package dev.inmo.tgbotapi.extensions.api.utils package dev.inmo.tgbotapi.extensions.api.utils
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.InternalUtils.convertWithMediaGroupUpdates import dev.inmo.tgbotapi.extensions.api.InternalUtils.convertWithMediaGroupUpdates
import dev.inmo.tgbotapi.types.message.abstracts.PossiblySentViaBotCommonMessage import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage
import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.UpdateReceiver import dev.inmo.tgbotapi.updateshandlers.UpdateReceiver
@@ -28,26 +29,18 @@ fun CoroutineScope.updateHandlerWithMediaGroupsAdaptation(
) )
launch { launch {
launch { launchSafelyWithoutExceptions {
for (update in updatesChannel) { for (update in updatesChannel) {
val dataAsPossiblySentViaBotCommonMessage = update.data as? PossiblySentViaBotCommonMessage<*> val data = update.data
when {
if (dataAsPossiblySentViaBotCommonMessage == null) { data is PossiblyMediaGroupMessage<*> && data.mediaGroupId != null -> {
output(update) mediaGroupChannel.send("${data.mediaGroupId}${update::class.simpleName}" to update as BaseMessageUpdate)
continue }
else -> output(update)
} }
val mediaGroupId = dataAsPossiblySentViaBotCommonMessage.mediaGroupId
if (mediaGroupId == null) {
output(update)
continue
}
mediaGroupChannel.send("${mediaGroupId}${update::class.simpleName}" to update as BaseMessageUpdate)
} }
} }
launch { launchSafelyWithoutExceptions {
for ((_, mediaGroup) in mediaGroupAccumulatedChannel) { for ((_, mediaGroup) in mediaGroupAccumulatedChannel) {
mediaGroup.convertWithMediaGroupUpdates().forEach { mediaGroup.convertWithMediaGroupUpdates().forEach {
output(it) output(it)

View File

@@ -8,6 +8,7 @@ import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation
import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.types.update.abstracts.Update import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
@@ -47,6 +48,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates * Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters * using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* flowsUpdatesFilter and scope * flowsUpdatesFilter and scope
*
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/ */
suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling( suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
upstreamUpdatesFlow: Flow<Update>? = null, upstreamUpdatesFlow: Flow<Update>? = null,
@@ -58,6 +63,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM( ): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow, upstreamUpdatesFlow,
@@ -70,7 +76,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
).run { ).run {
this to scope.launch { this to scope.launch {
start() start()
longPolling(flowsUpdatesFilter, timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, defaultExceptionsHandler) longPolling(flowsUpdatesFilter, timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, mediaGroupsDebounceTimeMillis, defaultExceptionsHandler)
} }
} }
@@ -116,6 +122,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters * using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* flowsUpdatesFilter and scope * flowsUpdatesFilter and scope
* *
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @see buildBehaviourWithFSMAndStartLongPolling * @see buildBehaviourWithFSMAndStartLongPolling
* @see BehaviourContext * @see BehaviourContext
* @see longPolling * @see longPolling
@@ -131,6 +141,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let { ) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM( buildBehaviourWithFSM(
@@ -149,6 +160,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
scope, scope,
autoDisableWebhooks, autoDisableWebhooks,
autoSkipTimeoutExceptions, autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
defaultExceptionsHandler defaultExceptionsHandler
) )
} }

View File

@@ -11,6 +11,7 @@ import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation
import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
@@ -26,6 +27,10 @@ import kotlin.coroutines.coroutineContext
* **WARNING** This method WILL NOT launch any listening of updates. Use something like * **WARNING** This method WILL NOT launch any listening of updates. Use something like
* [startGettingOfUpdatesByLongPolling] or tools for work with webhooks * [startGettingOfUpdatesByLongPolling] or tools for work with webhooks
* *
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @return Created bot which has been used to create [BehaviourContext] via [buildBehaviourWithFSM] * @return Created bot which has been used to create [BehaviourContext] via [buildBehaviourWithFSM]
* *
* @see [BehaviourContext] * @see [BehaviourContext]
@@ -46,6 +51,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot( ): TelegramBot = telegramBot(
token, token,
@@ -63,6 +69,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
timeoutSeconds, timeoutSeconds,
autoDisableWebhooks, autoDisableWebhooks,
autoSkipTimeoutExceptions, autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block block
) )
} }
@@ -71,6 +78,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
* Create bot using [telegramBot] and start listening for updates using [buildBehaviourWithFSMAndStartLongPolling]. This * Create bot using [telegramBot] and start listening for updates using [buildBehaviourWithFSMAndStartLongPolling]. This
* method will launch updates retrieving via long polling inside of [buildBehaviourWithFSMAndStartLongPolling] * method will launch updates retrieving via long polling inside of [buildBehaviourWithFSMAndStartLongPolling]
* *
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed * @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed
* here * here
* *
@@ -91,6 +102,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit> block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> { ): Pair<TelegramBot, Job> {
return telegramBot( return telegramBot(
@@ -108,6 +120,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
timeoutSeconds, timeoutSeconds,
autoDisableWebhooks, autoDisableWebhooks,
autoSkipTimeoutExceptions, autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block block
) )
} }

View File

@@ -5,6 +5,7 @@ import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation
import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -47,6 +48,10 @@ suspend fun TelegramBot.buildBehaviour(
* Use this method to build bot behaviour and run it via long polling. In case you wish to get [FlowsUpdatesFilter] for * Use this method to build bot behaviour and run it via long polling. In case you wish to get [FlowsUpdatesFilter] for
* additional manipulations, you must provide external [FlowsUpdatesFilter] in other [buildBehaviour] function. * additional manipulations, you must provide external [FlowsUpdatesFilter] in other [buildBehaviour] function.
* *
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @see buildBehaviour * @see buildBehaviour
* @see BehaviourContext * @see BehaviourContext
* @see startGettingOfUpdatesByLongPolling * @see startGettingOfUpdatesByLongPolling
@@ -57,6 +62,7 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: BehaviourContextReceiver<Unit> block: BehaviourContextReceiver<Unit>
): Job { ): Job {
val behaviourContext = buildBehaviour( val behaviourContext = buildBehaviour(
@@ -69,6 +75,7 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
scope = behaviourContext, scope = behaviourContext,
timeoutSeconds = timeoutSeconds, timeoutSeconds = timeoutSeconds,
autoDisableWebhooks = autoDisableWebhooks, autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions autoSkipTimeoutExceptions = autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis = mediaGroupsDebounceTimeMillis
) )
} }

View File

@@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.startGettingOfUpdatesByLongPolling
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.updateHandlerWithMediaGroupsAdaptation
import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
@@ -53,6 +54,10 @@ suspend fun telegramBotWithBehaviour(
* *
* **WARNING** This method WILL launch updates listening inside of calling [buildBehaviourWithLongPolling] * **WARNING** This method WILL launch updates listening inside of calling [buildBehaviourWithLongPolling]
* *
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed * @return Pair of [TelegramBot] and [Job]. This [Job] can be used to stop listening updates in your [block] you passed
* here * here
* *
@@ -70,6 +75,7 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: BehaviourContextReceiver<Unit> block: BehaviourContextReceiver<Unit>
): Pair<TelegramBot, Job> { ): Pair<TelegramBot, Job> {
return telegramBot( return telegramBot(
@@ -84,6 +90,7 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
timeoutSeconds, timeoutSeconds,
autoDisableWebhooks, autoDisableWebhooks,
autoSkipTimeoutExceptions, autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block block
) )
} }

View File

@@ -189,3 +189,7 @@ suspend fun BehaviourContext.waitInvoice(
initRequest: Request<*>? = null, initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null } errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<InvoiceContent>(initRequest, errorFactory) ) = waitContent<InvoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)

View File

@@ -216,3 +216,8 @@ suspend fun BehaviourContext.waitInvoiceMessage(
initRequest: Request<*>? = null, initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null } errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<InvoiceContent>(initRequest, errorFactory) ) = waitContentMessage<InvoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)

View File

@@ -607,3 +607,27 @@ suspend fun <BC : BehaviourContext> BC.onInvoice(
markerFactory, markerFactory,
scenarioReceiver scenarioReceiver
) )
/**
* @param initialFilter This filter will be called to remove unnecessary data BEFORE [scenarioReceiver] call
* @param subcontextUpdatesFilter This filter will be applied to each update inside of [scenarioReceiver]. For example,
* this filter will be used if you will call [dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage].
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextAndTwoTypesReceiver] function to create your own.
* Use [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.plus] or [dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times]
* to combinate several filters
* @param [markerFactory] Will be used to identify different "stream". [scenarioReceiver] will be called synchronously
* in one "stream". Output of [markerFactory] will be used as a key for "stream"
* @param scenarioReceiver Main callback which will be used to handle incoming data if [initialFilter] will pass that
* data
*/
suspend fun <BC : BehaviourContext> BC.onVisualContent(
initialFilter: CommonMessageFilter<VisualMediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, VisualMediaGroupMessage, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in VisualMediaGroupMessage, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, VisualMediaGroupMessage>
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
scenarioReceiver
)

View File

@@ -38,7 +38,7 @@ suspend fun <BC : BehaviourContext> BC.onDeepLink(
scenarioReceiver, scenarioReceiver,
) { ) {
(it.messageUpdateOrNull()) ?.data ?.commonMessageOrNull() ?.withContentOrNull<TextContent>() ?.let { message -> (it.messageUpdateOrNull()) ?.data ?.commonMessageOrNull() ?.withContentOrNull<TextContent>() ?.let { message ->
message to message.content.textSources[1].source.removePrefix(" ").decodeURLQueryComponent() message to (message.content.textSources.getOrNull(1) ?.source ?.removePrefix(" ") ?.decodeURLQueryComponent() ?: return@let null)
} ?.let(::listOfNotNull) } ?.let(::listOfNotNull)
}.also { }.also {
triggersHolder.handleableCommandsHolder.registerHandleable(startRegex) triggersHolder.handleableCommandsHolder.registerHandleable(startRegex)

View File

@@ -74,8 +74,11 @@ val Identifier.userLink: String
@Warning("This API have restrictions in Telegram System") @Warning("This API have restrictions in Telegram System")
val UserId.userLink: String val UserId.userLink: String
get() = chatId.userLink get() = chatId.userLink
val User.link: String val User.userLink: String
get() = id.userLink get() = id.userLink
@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.userLink", "dev.inmo.tgbotapi.types.userLink"))
val User.link: String
get() = userLink
typealias UserId = ChatId typealias UserId = ChatId

View File

@@ -3,10 +3,11 @@ package dev.inmo.tgbotapi.types.InlineQueries.query
import dev.inmo.tgbotapi.types.InlineQueryIdentifier import dev.inmo.tgbotapi.types.InlineQueryIdentifier
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.ChatType import dev.inmo.tgbotapi.types.chat.ChatType
import dev.inmo.tgbotapi.types.chat.CommonUser
data class BaseInlineQuery( data class BaseInlineQuery(
override val id: InlineQueryIdentifier, override val id: InlineQueryIdentifier,
override val from: User, override val from: CommonUser,
override val query: String, override val query: String,
override val offset: String, override val offset: String,
override val chatType: ChatType? override val chatType: ChatType?

View File

@@ -3,10 +3,16 @@ package dev.inmo.tgbotapi.types.InlineQueries.query
import dev.inmo.tgbotapi.abstracts.FromUser import dev.inmo.tgbotapi.abstracts.FromUser
import dev.inmo.tgbotapi.types.InlineQueryIdentifier import dev.inmo.tgbotapi.types.InlineQueryIdentifier
import dev.inmo.tgbotapi.types.chat.ChatType import dev.inmo.tgbotapi.types.chat.ChatType
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User
sealed interface InlineQuery : FromUser { sealed interface InlineQuery : FromUser {
val id: InlineQueryIdentifier val id: InlineQueryIdentifier
val query: String val query: String
val offset: String val offset: String
val chatType: ChatType? val chatType: ChatType?
override val from: CommonUser
override val user: CommonUser
get() = from
} }

View File

@@ -3,11 +3,12 @@ package dev.inmo.tgbotapi.types.InlineQueries.query
import dev.inmo.tgbotapi.types.InlineQueryIdentifier import dev.inmo.tgbotapi.types.InlineQueryIdentifier
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.ChatType import dev.inmo.tgbotapi.types.chat.ChatType
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.location.Location import dev.inmo.tgbotapi.types.location.Location
data class LocationInlineQuery( data class LocationInlineQuery(
override val id: InlineQueryIdentifier, override val id: InlineQueryIdentifier,
override val from: User, override val from: CommonUser,
override val query: String, override val query: String,
override val offset: String, override val offset: String,
override val chatType: ChatType?, override val chatType: ChatType?,

View File

@@ -12,7 +12,7 @@ internal data class RawInlineQuery(
@SerialName(idField) @SerialName(idField)
val id: InlineQueryIdentifier, val id: InlineQueryIdentifier,
@SerialName(fromField) @SerialName(fromField)
val from: User, val from: CommonUser,
@SerialName(queryField) @SerialName(queryField)
val query: String, val query: String,
@SerialName(offsetField) @SerialName(offsetField)

View File

@@ -2,16 +2,20 @@ package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.abstracts.FromUser import dev.inmo.tgbotapi.abstracts.FromUser
import dev.inmo.tgbotapi.types.CallbackQueryIdentifier import dev.inmo.tgbotapi.types.CallbackQueryIdentifier
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
sealed interface CallbackQuery : FromUser { sealed interface CallbackQuery : FromUser {
val id: CallbackQueryIdentifier val id: CallbackQueryIdentifier
val chatInstance: String val chatInstance: String
override val from: CommonUser
override val user: CommonUser
get() = from
} }
data class UnknownCallbackQueryType( data class UnknownCallbackQueryType(
override val id: CallbackQueryIdentifier, override val id: CallbackQueryIdentifier,
override val from: User, override val from: CommonUser,
override val chatInstance: String, override val chatInstance: String,
val raw: String val raw: String
) : CallbackQuery ) : CallbackQuery

View File

@@ -1,11 +1,14 @@
package dev.inmo.tgbotapi.types.queries.callback package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import kotlinx.serialization.Serializable
@Serializable
data class InlineMessageIdDataCallbackQuery( data class InlineMessageIdDataCallbackQuery(
override val id: CallbackQueryIdentifier, override val id: CallbackQueryIdentifier,
override val from: User, override val from: CommonUser,
override val chatInstance: String, override val chatInstance: String,
override val inlineMessageId: InlineMessageIdentifier, override val inlineMessageId: InlineMessageIdentifier,
override val data: String override val data: String

View File

@@ -1,11 +1,12 @@
package dev.inmo.tgbotapi.types.queries.callback package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
data class InlineMessageIdGameShortNameCallbackQuery( data class InlineMessageIdGameShortNameCallbackQuery(
override val id: CallbackQueryIdentifier, override val id: CallbackQueryIdentifier,
override val from: User, override val from: CommonUser,
override val chatInstance: String, override val chatInstance: String,
override val inlineMessageId: InlineMessageIdentifier, override val inlineMessageId: InlineMessageIdentifier,
override val gameShortName: String override val gameShortName: String

View File

@@ -1,13 +1,14 @@
package dev.inmo.tgbotapi.types.queries.callback package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.types.CallbackQueryIdentifier import dev.inmo.tgbotapi.types.CallbackQueryIdentifier
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
data class MessageDataCallbackQuery( data class MessageDataCallbackQuery(
override val id: CallbackQueryIdentifier, override val id: CallbackQueryIdentifier,
override val from: User, override val from: CommonUser,
override val chatInstance: String, override val chatInstance: String,
override val message: ContentMessage<MessageContent>, override val message: ContentMessage<MessageContent>,
override val data: String override val data: String

View File

@@ -1,13 +1,14 @@
package dev.inmo.tgbotapi.types.queries.callback package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.types.CallbackQueryIdentifier import dev.inmo.tgbotapi.types.CallbackQueryIdentifier
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
data class MessageGameShortNameCallbackQuery( data class MessageGameShortNameCallbackQuery(
override val id: CallbackQueryIdentifier, override val id: CallbackQueryIdentifier,
override val from: User, override val from: CommonUser,
override val chatInstance: String, override val chatInstance: String,
override val message: ContentMessage<MessageContent>, override val message: ContentMessage<MessageContent>,
override val gameShortName: String override val gameShortName: String

View File

@@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.types.queries.callback package dev.inmo.tgbotapi.types.queries.callback
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.User import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.* import dev.inmo.tgbotapi.types.message.abstracts.*
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializer import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializer
@@ -13,7 +14,7 @@ internal data class RawCallbackQuery(
@SerialName(idField) @SerialName(idField)
val id: CallbackQueryIdentifier, val id: CallbackQueryIdentifier,
@SerialName(fromField) @SerialName(fromField)
val from: User, val from: CommonUser,
@Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class) @Serializable(TelegramBotAPIMessageDeserializeOnlySerializer::class)
val message: ContentMessage<MessageContent>? = null, val message: ContentMessage<MessageContent>? = null,
@SerialName(inlineMessageIdField) @SerialName(inlineMessageIdField)

View File

@@ -18,10 +18,16 @@ fun makeChatLink(identifier: Identifier, threadId: MessageThreadId? = null) = id
fun makeUsernameDeepLinkPrefix(username: String) = "${makeUsernameLink(username)}?start=" fun makeUsernameDeepLinkPrefix(username: String) = "${makeUsernameLink(username)}?start="
fun makeUsernameStartattachPrefix(username: String) = "$internalLinkBeginning/$username?startattach" fun makeUsernameStartattachPrefix(username: String) = "$internalLinkBeginning/$username?startattach"
fun makeUsernameStartattachLink(username: String, data: String? = null) = "${makeUsernameStartattachPrefix(username)}${data?.let { "=$it" } ?: ""}" fun makeUsernameStartattachLink(username: String, data: String? = null) = "${makeUsernameStartattachPrefix(username)}${data?.let { "=$it" } ?: ""}"
inline val Username.link inline val Username.usernameLink
get() = makeUsernameLink(usernameWithoutAt) get() = makeUsernameLink(usernameWithoutAt)
val IdChatIdentifier.link: String @Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.usernameLink", "dev.inmo.tgbotapi.extensions.utils.formatting.usernameLink"))
inline val Username.link
get() = usernameLink
val IdChatIdentifier.chatLink: String
get() = makeChatLink(chatId, threadId) get() = makeChatLink(chatId, threadId)
@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.chatLink", "dev.inmo.tgbotapi.extensions.utils.formatting.chatLink"))
val IdChatIdentifier.link: String
get() = chatLink
fun ChatId.link(threadId: MessageThreadId?) = makeChatLink(chatId, threadId) fun ChatId.link(threadId: MessageThreadId?) = makeChatLink(chatId, threadId)
inline fun Username.link(threadId: MessageThreadId?) = makeUsernameLink(usernameWithoutAt, threadId) inline fun Username.link(threadId: MessageThreadId?) = makeUsernameLink(usernameWithoutAt, threadId)
inline val Username.deepLinkPrefix inline val Username.deepLinkPrefix
@@ -83,20 +89,27 @@ fun makeLinkToMessage(
/** /**
* @see makeLinkToMessage * @see makeLinkToMessage
*/ */
val Message.link: String? val Message.messageLink: String?
get() = makeLinkToMessage( get() = makeLinkToMessage(
chat, chat,
messageId messageId
) )
/**
* @see makeLinkToMessage
*/
@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.messageLink", "dev.inmo.tgbotapi.extensions.utils.formatting.messageLink"))
val Message.link: String?
get() = messageLink
/** /**
* Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no * Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no
* known way to build link * known way to build link
*/ */
val Chat.link: String? val Chat.chatLink: String?
get() { get() {
if (this is UsernameChat) { if (this is UsernameChat) {
username ?.link ?: id.link username ?.usernameLink ?: id.chatLink
} }
if (this is ExtendedPublicChat) { if (this is ExtendedPublicChat) {
inviteLink ?.let { return it } inviteLink ?.let { return it }
@@ -107,6 +120,14 @@ val Chat.link: String?
return null return null
} }
/**
* Link which can be used as by any user to get access to [Chat]. Returns null in case when there are no
* known way to build link
*/
@Deprecated("Deprecated due to the conflicts in name", ReplaceWith("this.chatLink", "dev.inmo.tgbotapi.extensions.utils.formatting.chatLink"))
val Chat.link: String?
get() = chatLink
private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addstickers" private const val stickerSetAddingLinkPrefix = "$internalLinkBeginning/addstickers"
val StickerSetName.stickerSetLink val StickerSetName.stickerSetLink

View File

@@ -27,7 +27,8 @@ fun List<Update>.lastUpdateIdentifier(): UpdateIdentifier? {
} }
/** /**
* Will convert incoming list of updates to list with [MediaGroupUpdate]s * Will convert incoming list of [Update]s to list with [Update]s, which include [dev.inmo.tgbotapi.types.message.abstracts.ContentMessage]s
* with [dev.inmo.tgbotapi.types.message.content.MediaGroupContent]
*/ */
fun List<Update>.convertWithMediaGroupUpdates(): List<Update> { fun List<Update>.convertWithMediaGroupUpdates(): List<Update> {
val resultUpdates = mutableListOf<Update>() val resultUpdates = mutableListOf<Update>()
@@ -67,4 +68,5 @@ fun List<Update>.convertWithMediaGroupUpdates(): List<Update> {
* *
* @throws IllegalStateException * @throws IllegalStateException
*/ */
@Deprecated("Redundant", ReplaceWith("this"))
fun BaseEditMessageUpdate.toEditMediaGroupUpdate() = this fun BaseEditMessageUpdate.toEditMediaGroupUpdate() = this

View File

@@ -19,12 +19,18 @@ import io.ktor.utils.io.CancellationException
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun TelegramBot.longPollingFlow( fun TelegramBot.longPollingFlow(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
exceptionsHandler: (ExceptionHandler<Unit>)? = null, exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST, allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
): Flow<Update> = channelFlow { ): Flow<Update> = channelFlow {
if (autoDisableWebhooks) { if (autoDisableWebhooks) {
runCatchingSafely { runCatchingSafely {
@@ -47,6 +53,52 @@ fun TelegramBot.longPollingFlow(
var lastUpdateIdentifier: UpdateIdentifier? = null var lastUpdateIdentifier: UpdateIdentifier? = null
val updatesHandler: (suspend (List<Update>) -> Unit) = if (mediaGroupsDebounceTimeMillis != null) {
val scope = CoroutineScope(contextToWork)
val updatesReceiver = scope.updateHandlerWithMediaGroupsAdaptation(
{
withContext(contextToWork) {
send(it)
}
},
mediaGroupsDebounceTimeMillis
);
{ originalUpdates: List<Update> ->
originalUpdates.forEach {
updatesReceiver(it)
lastUpdateIdentifier = maxOf(lastUpdateIdentifier ?: it.updateId, it.updateId)
}
}
} else {
{ originalUpdates: List<Update> ->
val converted = originalUpdates.convertWithMediaGroupUpdates()
/**
* Dirty hack for cases when the media group was retrieved not fully:
*
* We are throw out the last media group and will reretrieve it again in the next get updates
* and it will guarantee that it is full
*/
val updates = if (
originalUpdates.size == getUpdatesLimit.last
&& ((converted.last() as? BaseSentMessageUpdate) ?.data as? CommonMessage<*>) ?.content is MediaGroupContent<*>
) {
converted - converted.last()
} else {
converted
}
safelyWithResult {
for (update in updates) {
send(update)
lastUpdateIdentifier = update.updateId
}
}.onFailure {
cancel(it as? CancellationException ?: return@onFailure)
}
}
}
withContext(contextToWork) { withContext(contextToWork) {
while (isActive) { while (isActive) {
safely( safely(
@@ -64,50 +116,25 @@ fun TelegramBot.longPollingFlow(
} }
} }
) { ) {
val updates = execute( execute(
GetUpdates( GetUpdates(
offset = lastUpdateIdentifier?.plus(1), offset = lastUpdateIdentifier?.plus(1),
timeout = timeoutSeconds, timeout = timeoutSeconds,
allowed_updates = allowedUpdates allowed_updates = allowedUpdates
) )
).let { originalUpdates -> ).let { originalUpdates ->
val converted = originalUpdates.convertWithMediaGroupUpdates() updatesHandler(originalUpdates)
/**
* Dirty hack for cases when the media group was retrieved not fully:
*
* We are throw out the last media group and will reretrieve it again in the next get updates
* and it will guarantee that it is full
*/
/**
* Dirty hack for cases when the media group was retrieved not fully:
*
* We are throw out the last media group and will reretrieve it again in the next get updates
* and it will guarantee that it is full
*/
if (
originalUpdates.size == getUpdatesLimit.last
&& ((converted.last() as? BaseSentMessageUpdate) ?.data as? CommonMessage<*>) ?.content is MediaGroupContent<*>
) {
converted - converted.last()
} else {
converted
}
}
safelyWithResult {
for (update in updates) {
send(update)
lastUpdateIdentifier = update.updateId
}
}.onFailure {
cancel(it as? CancellationException ?: return@onFailure)
} }
} }
} }
} }
} }
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun TelegramBot.startGettingOfUpdatesByLongPolling( fun TelegramBot.startGettingOfUpdatesByLongPolling(
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
@@ -115,13 +142,15 @@ fun TelegramBot.startGettingOfUpdatesByLongPolling(
allowedUpdates: List<String>? = ALL_UPDATES_LIST, allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
updatesReceiver: UpdateReceiver<Update> updatesReceiver: UpdateReceiver<Update>
): Job = longPollingFlow( ): Job = longPollingFlow(
timeoutSeconds = timeoutSeconds, timeoutSeconds = timeoutSeconds,
exceptionsHandler = exceptionsHandler, exceptionsHandler = exceptionsHandler,
allowedUpdates = allowedUpdates, allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks, autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions autoSkipTimeoutExceptions = autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis = mediaGroupsDebounceTimeMillis
).subscribeSafely( ).subscribeSafely(
scope, scope,
exceptionsHandler ?: defaultSafelyExceptionHandler, exceptionsHandler ?: defaultSafelyExceptionHandler,
@@ -129,6 +158,10 @@ fun TelegramBot.startGettingOfUpdatesByLongPolling(
) )
/** /**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*
* @return [kotlinx.coroutines.flow.Flow] which will emit updates to the collector while they will be accumulated. Works * @return [kotlinx.coroutines.flow.Flow] which will emit updates to the collector while they will be accumulated. Works
* the same as [longPollingFlow], but it will cancel the flow after the first one [HttpRequestTimeoutException] * the same as [longPollingFlow], but it will cancel the flow after the first one [HttpRequestTimeoutException]
*/ */
@@ -137,7 +170,8 @@ fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
avoidCallbackQueries: Boolean = false, avoidCallbackQueries: Boolean = false,
exceptionsHandler: ExceptionHandler<Unit>? = null, exceptionsHandler: ExceptionHandler<Unit>? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST, allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true autoDisableWebhooks: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
): Flow<Update> = longPollingFlow( ): Flow<Update> = longPollingFlow(
timeoutSeconds = 0, timeoutSeconds = 0,
exceptionsHandler = { exceptionsHandler = {
@@ -149,11 +183,17 @@ fun TelegramBot.createAccumulatedUpdatesRetrieverFlow(
}, },
allowedUpdates = allowedUpdates, allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks, autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = false autoSkipTimeoutExceptions = false,
mediaGroupsDebounceTimeMillis = mediaGroupsDebounceTimeMillis
).filter { ).filter {
!(it is InlineQueryUpdate && avoidInlineQueries || it is CallbackQueryUpdate && avoidCallbackQueries) !(it is InlineQueryUpdate && avoidInlineQueries || it is CallbackQueryUpdate && avoidCallbackQueries)
} }
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun TelegramBot.retrieveAccumulatedUpdates( fun TelegramBot.retrieveAccumulatedUpdates(
avoidInlineQueries: Boolean = false, avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false, avoidCallbackQueries: Boolean = false,
@@ -161,25 +201,33 @@ fun TelegramBot.retrieveAccumulatedUpdates(
exceptionsHandler: (ExceptionHandler<Unit>)? = null, exceptionsHandler: (ExceptionHandler<Unit>)? = null,
allowedUpdates: List<String>? = ALL_UPDATES_LIST, allowedUpdates: List<String>? = ALL_UPDATES_LIST,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
updatesReceiver: UpdateReceiver<Update> updatesReceiver: UpdateReceiver<Update>
): Job = createAccumulatedUpdatesRetrieverFlow( ): Job = createAccumulatedUpdatesRetrieverFlow(
avoidInlineQueries, avoidInlineQueries,
avoidCallbackQueries, avoidCallbackQueries,
exceptionsHandler, exceptionsHandler,
allowedUpdates, allowedUpdates,
autoDisableWebhooks autoDisableWebhooks,
mediaGroupsDebounceTimeMillis
).subscribeSafelyWithoutExceptions( ).subscribeSafelyWithoutExceptions(
scope.LinkedSupervisorScope() scope.LinkedSupervisorScope()
) { ) {
updatesReceiver(it) updatesReceiver(it)
} }
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun TelegramBot.retrieveAccumulatedUpdates( fun TelegramBot.retrieveAccumulatedUpdates(
flowsUpdatesFilter: FlowsUpdatesFilter, flowsUpdatesFilter: FlowsUpdatesFilter,
avoidInlineQueries: Boolean = false, avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false, avoidCallbackQueries: Boolean = false,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
exceptionsHandler: ExceptionHandler<Unit>? = null exceptionsHandler: ExceptionHandler<Unit>? = null
) = retrieveAccumulatedUpdates( ) = retrieveAccumulatedUpdates(
avoidInlineQueries, avoidInlineQueries,
@@ -188,9 +236,15 @@ fun TelegramBot.retrieveAccumulatedUpdates(
exceptionsHandler, exceptionsHandler,
flowsUpdatesFilter.allowedUpdates, flowsUpdatesFilter.allowedUpdates,
autoDisableWebhooks, autoDisableWebhooks,
mediaGroupsDebounceTimeMillis,
flowsUpdatesFilter.asUpdateReceiver flowsUpdatesFilter.asUpdateReceiver
) )
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
suspend fun TelegramBot.flushAccumulatedUpdates( suspend fun TelegramBot.flushAccumulatedUpdates(
avoidInlineQueries: Boolean = false, avoidInlineQueries: Boolean = false,
avoidCallbackQueries: Boolean = false, avoidCallbackQueries: Boolean = false,
@@ -198,6 +252,7 @@ suspend fun TelegramBot.flushAccumulatedUpdates(
allowedUpdates: List<String>? = ALL_UPDATES_LIST, allowedUpdates: List<String>? = ALL_UPDATES_LIST,
exceptionsHandler: ExceptionHandler<Unit>? = null, exceptionsHandler: ExceptionHandler<Unit>? = null,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
updatesReceiver: UpdateReceiver<Update> = {} updatesReceiver: UpdateReceiver<Update> = {}
) = retrieveAccumulatedUpdates( ) = retrieveAccumulatedUpdates(
avoidInlineQueries, avoidInlineQueries,
@@ -206,12 +261,17 @@ suspend fun TelegramBot.flushAccumulatedUpdates(
exceptionsHandler, exceptionsHandler,
allowedUpdates, allowedUpdates,
autoDisableWebhooks, autoDisableWebhooks,
mediaGroupsDebounceTimeMillis,
updatesReceiver updatesReceiver
).join() ).join()
/** /**
* Will [startGettingOfUpdatesByLongPolling] using incoming [flowsUpdatesFilter]. It is assumed that you ALREADY CONFIGURE * Will [startGettingOfUpdatesByLongPolling] using incoming [updatesFilter]. It is assumed that you ALREADY CONFIGURE
* all updates receivers, because this method will trigger getting of updates and. * all updates receivers, because this method will trigger getting of updates and.
*
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/ */
fun TelegramBot.longPolling( fun TelegramBot.longPolling(
updatesFilter: UpdatesFilter, updatesFilter: UpdatesFilter,
@@ -219,6 +279,7 @@ fun TelegramBot.longPolling(
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
exceptionsHandler: ExceptionHandler<Unit>? = null exceptionsHandler: ExceptionHandler<Unit>? = null
): Job = updatesFilter.run { ): Job = updatesFilter.run {
startGettingOfUpdatesByLongPolling( startGettingOfUpdatesByLongPolling(
@@ -228,6 +289,7 @@ fun TelegramBot.longPolling(
allowedUpdates = allowedUpdates, allowedUpdates = allowedUpdates,
autoDisableWebhooks = autoDisableWebhooks, autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions, autoSkipTimeoutExceptions = autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis = mediaGroupsDebounceTimeMillis,
updatesReceiver = asUpdateReceiver updatesReceiver = asUpdateReceiver
) )
} }
@@ -236,6 +298,10 @@ fun TelegramBot.longPolling(
* Will enable [longPolling] by creating [FlowsUpdatesFilter] with [flowsUpdatesFilterUpdatesKeeperCount] as an argument * Will enable [longPolling] by creating [FlowsUpdatesFilter] with [flowsUpdatesFilterUpdatesKeeperCount] as an argument
* and applied [flowUpdatesPreset]. It is assumed that you WILL CONFIGURE all updates receivers in [flowUpdatesPreset], * and applied [flowUpdatesPreset]. It is assumed that you WILL CONFIGURE all updates receivers in [flowUpdatesPreset],
* because of after [flowUpdatesPreset] method calling will be triggered getting of updates. * because of after [flowUpdatesPreset] method calling will be triggered getting of updates.
*
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/ */
@Suppress("unused") @Suppress("unused")
fun TelegramBot.longPolling( fun TelegramBot.longPolling(
@@ -245,15 +311,22 @@ fun TelegramBot.longPolling(
flowsUpdatesFilterUpdatesKeeperCount: Int = 100, flowsUpdatesFilterUpdatesKeeperCount: Int = 100,
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit flowUpdatesPreset: FlowsUpdatesFilter.() -> Unit
): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, exceptionsHandler) ): Job = longPolling(FlowsUpdatesFilter(flowsUpdatesFilterUpdatesKeeperCount).apply(flowUpdatesPreset), timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, mediaGroupsDebounceTimeMillis, exceptionsHandler)
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun RequestsExecutor.startGettingOfUpdatesByLongPolling( fun RequestsExecutor.startGettingOfUpdatesByLongPolling(
updatesFilter: UpdatesFilter, updatesFilter: UpdatesFilter,
timeoutSeconds: Seconds = 30, timeoutSeconds: Seconds = 30,
exceptionsHandler: ExceptionHandler<Unit>? = null, exceptionsHandler: ExceptionHandler<Unit>? = null,
scope: CoroutineScope = CoroutineScope(Dispatchers.Default), scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
autoDisableWebhooks: Boolean = true, autoDisableWebhooks: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
autoSkipTimeoutExceptions: Boolean = true, autoSkipTimeoutExceptions: Boolean = true,
): Job = startGettingOfUpdatesByLongPolling( ): Job = startGettingOfUpdatesByLongPolling(
timeoutSeconds, timeoutSeconds,
@@ -262,5 +335,6 @@ fun RequestsExecutor.startGettingOfUpdatesByLongPolling(
updatesFilter.allowedUpdates, updatesFilter.allowedUpdates,
autoDisableWebhooks, autoDisableWebhooks,
autoSkipTimeoutExceptions, autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
updatesFilter.asUpdateReceiver updatesFilter.asUpdateReceiver
) )

View File

@@ -1,6 +1,5 @@
package dev.inmo.tgbotapi.extensions.utils.updates.retrieving package dev.inmo.tgbotapi.extensions.utils.updates.retrieving
import dev.inmo.micro_utils.coroutines.launchSafely
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.utils.updates.convertWithMediaGroupUpdates import dev.inmo.tgbotapi.extensions.utils.updates.convertWithMediaGroupUpdates
import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage
@@ -12,7 +11,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
* Create [UpdateReceiver] object which will correctly accumulate updates and send into output updates which INCLUDE * Create [UpdateReceiver] object which will correctly accumulate updates and send into output updates which INCLUDE
* [dev.inmo.tgbotapi.types.update.MediaGroupUpdates.MediaGroupUpdate]s. * [dev.inmo.tgbotapi.types.update.MediaGroupUpdates.MediaGroupUpdate]s.

View File

@@ -26,6 +26,9 @@ import java.util.concurrent.Executors
* @param [scope] Will be used for mapping of media groups * @param [scope] Will be used for mapping of media groups
* @param [exceptionsHandler] Pass this parameter to set custom exception handler for getting updates * @param [exceptionsHandler] Pass this parameter to set custom exception handler for getting updates
* @param [block] Some receiver block like [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter] * @param [block] Some receiver block like [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter]
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
* *
* @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
* @see UpdatesFilter * @see UpdatesFilter
@@ -57,6 +60,11 @@ fun Route.includeWebhookHandlingInRoute(
} }
} }
/**
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
*/
fun Route.includeWebhookHandlingInRouteWithFlows( fun Route.includeWebhookHandlingInRouteWithFlows(
scope: CoroutineScope, scope: CoroutineScope,
exceptionsHandler: ExceptionHandler<Unit>? = null, exceptionsHandler: ExceptionHandler<Unit>? = null,
@@ -76,6 +84,9 @@ fun Route.includeWebhookHandlingInRouteWithFlows(
* @param listenRoute address to listen by bot. If null - will be set up in root of host * @param listenRoute address to listen by bot. If null - will be set up in root of host
* @param scope Scope which will be used for * @param scope Scope which will be used for
* @param privateKeyConfig If configured - server will be created with [sslConnector]. [connector] will be used otherwise * @param privateKeyConfig If configured - server will be created with [sslConnector]. [connector] will be used otherwise
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
* *
* @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
* @see UpdatesFilter * @see UpdatesFilter
@@ -132,6 +143,9 @@ fun startListenWebhooks(
* @param listenPort port which will be listen by bot * @param listenPort port which will be listen by bot
* @param listenRoute address to listen by bot * @param listenRoute address to listen by bot
* @param scope Scope which will be used for * @param scope Scope which will be used for
* @param mediaGroupsDebounceTimeMillis Will be used for calling of [updateHandlerWithMediaGroupsAdaptation]. Pass null
* in case you wish to enable classic way of updates handling, but in that mode some media group messages can be
* retrieved in different updates
* *
* @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter * @see dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
* @see UpdatesFilter * @see UpdatesFilter