1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-17 04:20:13 +00:00

Compare commits

..

107 Commits
5.0.1 ... 6.2.0

Author SHA1 Message Date
b0e32e8ad9 add opportunity to send emoji with sticker 2023-03-10 12:43:09 +06:00
79c9e6258f add setStickerMaskPosition 2023-03-10 12:28:16 +06:00
90b9c66bea rename all thumb* usages 2023-03-10 12:24:12 +06:00
e7b21dcd3d rename setStickerSetThumb to setStickerSetThumbnail 2023-03-10 12:11:17 +06:00
e30361ad1e rename thumb field 2023-03-10 12:02:40 +06:00
d8c659f866 add setStickerKeywords 2023-03-10 11:32:44 +06:00
5533303d86 add setStickerEmojiList 2023-03-10 11:28:21 +06:00
1633b9baaf add deleteStickerSet 2023-03-10 11:18:37 +06:00
a9725eb439 add support of setStickerSetTitle 2023-03-10 11:16:13 +06:00
a0aadef31b add support of localized bot description 2023-03-10 00:34:57 +06:00
ac88fd1d02 start 6.2.0 2023-03-10 00:23:16 +06:00
e3e318312d Merge pull request #734 from InsanusMokrassar/6.1.0
6.1.0
2023-03-08 17:10:47 +06:00
4c08fb7d26 small improvement in #732 solution 2023-03-08 16:41:10 +06:00
d83ff12560 update dependencies 2023-03-08 16:38:24 +06:00
818ef8481d fix of #732 2023-03-08 16:33:41 +06:00
f8cd446133 start 6.1.0 2023-03-08 16:24:31 +06:00
804b991921 update kdocs 2023-03-04 21:18:50 +06:00
817068aa71 Merge pull request #731 from InsanusMokrassar/renovate/dokka
Update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.8.10
2023-03-04 10:48:42 +06:00
renovate[bot]
0cca343612 Update dependency org.jetbrains.dokka:dokka-gradle-plugin to v1.8.10 2023-03-04 03:54:29 +00:00
6532bf255b Merge pull request #730 from InsanusMokrassar/6.0.3
6.0.3
2023-03-02 23:28:16 +06:00
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
fa0a2818a0 hotfix in LiveFlowLocation 2023-02-17 15:40:49 +06:00
2d3fe45389 Merge pull request #720 from InsanusMokrassar/5.2.0
5.2.0
2023-02-17 15:36:27 +06:00
02b5d282d3 now it is possible to handle send content message in handleLiveLocation 2023-02-17 15:31:40 +06:00
7795bc2b50 LiveLocationProvider#message now is public, but as value instead of variable 2023-02-17 15:28:41 +06:00
a95365a691 update microutils up to 0.16.10 2023-02-17 14:03:00 +06:00
07082bf896 start 5.2.0 2023-02-17 14:02:18 +06:00
6a3fc47f62 Merge pull request #717 from InsanusMokrassar/5.1.1
5.1.1
2023-02-17 14:01:21 +06:00
1c94e86b40 small improvements 2023-02-13 12:06:28 +06:00
0416b200b8 fixes :) 2023-02-13 12:03:23 +06:00
48c4e90912 fixes 2023-02-13 12:00:09 +06:00
5fc88e89b9 Fixes in content waiting expectators 2023-02-13 11:49:36 +06:00
dad42cf939 makeUserLink 2023-02-13 11:16:49 +06:00
041c3ecc1b start 5.1.1 2023-02-11 18:57:46 +06:00
103dd949ce Merge pull request #712 from InsanusMokrassar/5.1.0
5.1.0
2023-02-06 14:08:03 +06:00
e3acdf1802 fix of #697 2023-02-06 13:28:38 +06:00
f81d28dd5f fill changelog and fix several issues 2023-02-06 12:41:19 +06:00
8e02a702f1 improvements in ChatPermissions 2023-02-06 12:04:30 +06:00
cb7a343208 improve copying functions in chat permissions 2023-02-06 11:36:00 +06:00
3be8ddae74 rights copying hotfix 2023-02-06 11:29:07 +06:00
9cd1862300 add opportunity to copy chat permissions 2023-02-06 11:25:31 +06:00
81fdf50217 ChatPermissions now is interface 2023-02-06 10:26:48 +06:00
554d47e301 Update README.md 2023-02-06 00:59:33 +06:00
b66ae7ad77 Update README.md 2023-02-06 00:58:36 +06:00
4ddced8e26 Merge pull request #715 from madhead/feature/replace_can_send_media_messages_field
Replaced the fields `can_send_media_messages`…
2023-02-06 00:25:40 +06:00
d003047a6c Merge branch '5.1.0' into feature/replace_can_send_media_messages_field 2023-02-06 00:24:19 +06:00
1e4a78c812 add support for independent chat permissions 2023-02-06 00:21:46 +06:00
2a3ffd707e improvements in KeyboardButtonRequestChat 2023-02-05 23:41:02 +06:00
aca076381b renames in request buttons 2023-02-05 23:26:41 +06:00
12ac227d2d small fix in request chat button 2023-02-05 23:18:14 +06:00
e235280253 fixes in RequestId 2023-02-05 22:29:21 +06:00
0da0c4e894 Revert "fixes in keyboards"
This reverts commit ae8ef0dd3c.
2023-02-05 22:25:43 +06:00
47f1509ecc add RequestId.random 2023-02-05 22:01:43 +06:00
ae8ef0dd3c fixes in keyboards 2023-02-05 21:47:15 +06:00
687f9e95fa support of user shared/chat shared 2023-02-05 20:46:25 +06:00
6dbe5f024f Merge pull request #713 from madhead/feature/user_chat_id_in_chat_join_request
Add support for `user_chat_id` field
2023-02-05 18:34:36 +06:00
a39a276299 Update libs.versions.toml 2023-02-05 18:34:05 +06:00
9f57e5685f Merge branch '5.1.0' into feature/user_chat_id_in_chat_join_request 2023-02-05 18:33:43 +06:00
bdcba202c9 Update ChatJoinRequest.kt 2023-02-05 18:32:46 +06:00
3c48dcb2a6 downgrade kotlin 2023-02-05 18:26:29 +06:00
b59d94d0a9 Update libs.versions.toml 2023-02-05 17:41:45 +06:00
db74b55c41 Update CHANGELOG.md 2023-02-05 17:41:45 +06:00
37b5af235a start 5.1.0 2023-02-05 17:41:07 +06:00
e2b05ce575 Merge pull request #711 from InsanusMokrassar/5.0.2
5.0.2
2023-02-05 17:34:42 +06:00
49851ee3d7 update micro_utils version 2023-02-05 16:22:33 +06:00
madhead
cd596cc66d Replaced the fields can_send_media_messages in the classes RestrictedChatMember and ChatPermissions with separate fields can_send_audios, can_send_documents, can_send_photos, can_send_videos, can_send_video_notes, and can_send_voice_notes for different media types. 2023-02-03 22:16:21 +01:00
madhead
5667ae8095 Added the field user_chat_id to the class ChatJoinRequest. 2023-02-03 21:26:55 +01:00
f29996aac8 Update libs.versions.toml 2023-02-04 00:56:47 +06:00
32613bacc6 Update CHANGELOG.md 2023-02-04 00:56:06 +06:00
cf9dba0ecc start 5.1.0 2023-02-04 00:55:46 +06:00
a8c4879769 update dependencies 2023-02-02 09:25:21 +06:00
f083e94c05 fix in BehaviourContext.onEditedContentMessage 2023-02-02 09:24:16 +06:00
c332413e5a start 5.0.2 2023-02-02 09:21:57 +06:00
c9f3d99cd7 Merge pull request #708 from InsanusMokrassar/5.0.1
5.0.1
2023-01-18 23:45:19 +06:00
139 changed files with 2795 additions and 469 deletions

View File

@@ -12,10 +12,10 @@ jobs:
with:
java-version: 11
- name: Build
run: ./gradlew dokkaHtml
run: ./gradlew dokkaHtmlMultiModule
- name: Publish KDocs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs/build/dokka/html
publish_dir: ./build/dokka/htmlMultiModule
publish_branch: kdocs

View File

@@ -1,10 +1,97 @@
# TelegramBotAPI changelog
## 6.2.0
## 6.1.0
* `Versions`:
* `MicroUtils`: `0.17.2` -> `0.17.3`
* `API`:
* Fix of [#732](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/732)
## 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
* `Versions`:
* `MicroUtils`: `0.16.8` -> `0.16.10`
## 5.1.1
* `Core`:
* Add opportunity to get user link with `makeUserLink`
* `BehaviourBuilder`:
* Fixes in content waiting expectators
## 5.1.0
[Bot API 6.5](https://core.telegram.org/bots/api-changelog#february-3-2023) support
* `Core`:
* `ChatPermissions` now is interface and have two main realizations: `ChatPermissions.Granular` and
`ChatPermissions.Common`
* `RestrictedChatMember` now implements `ChatPermissions` too
* `API`:
* Now it is possible to pass all long polling parameters in all places used it
* `Issues`:
* Fix of [#697](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/697)
## 5.0.2
* `Versions`:
* `MicroUtils`: `0.16.6` -> `0.16.8`
* `Ktor`: `2.2.2` -> `2.2.3`
* `BehaviourBuilder`:
* Fixes in `BehaviourContext.onEditedContentMessage` - now it will trigger callback on channel post edits too
## 5.0.1
* `Versions`:
* `MicroUtils`: `0.16.4` -> `0.16.6`
* `Ktor`: `2.2.1` -> `2.2.2`
* `MicroUtils`: `0.16.4` -> `0.16.6`
* `Ktor`: `2.2.1` -> `2.2.2`
* `Core`:
* Fixes in `SendMediaGroup` request
* Fixes in `SetChatAdministratorCustomTitle` request (thanks to [@madhead](https://github.com/madhead))

View File

@@ -1,14 +1,14 @@
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.4-blue)](https://core.telegram.org/bots/api-changelog#december-30-2022)
# TelegramBotAPI [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi) [![Supported version](https://img.shields.io/badge/Telegram%20Bot%20API-6.5-blue)](https://core.telegram.org/bots/api-changelog#february-3-2023)
| Docs | [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue&logo=kotlin)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Bookstack&message=Tutorial&color=blue&logo=bookstack)](https://bookstack.inmo.dev/books/telegrambotapi/chapter/introduction-tutorial) |
|:---:|:---:|
| 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) |
<!--- [![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">
<a href="https://t.me/InMoTelegramBotAPI">
<a href="https://t.me/ktgbotapi">
<img src="./resources/tg_channel_qr.jpg">
</a>
</p>

View File

@@ -14,6 +14,10 @@ buildscript {
}
}
plugins {
alias(libs.plugins.kotlin.dokka)
}
// temporal crutch until legacy tests will be stabled or legacy target will be removed
allprojects {
repositories {

View File

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

View File

@@ -1,22 +1,22 @@
[versions]
kotlin = "1.7.22"
kotlin-serialization = "1.4.1"
kotlin = "1.8.10"
kotlin-serialization = "1.5.0"
kotlin-coroutines = "1.6.4"
javax-activation = "1.1.1"
korlibs = "3.4.0"
uuid = "0.6.0"
ktor = "2.2.2"
uuid = "0.7.0"
ktor = "2.2.4"
ksp = "1.7.22-1.0.8"
ksp = "1.8.10-1.0.9"
kotlin-poet = "1.12.0"
microutils = "0.16.6"
microutils = "0.17.3"
github-release-plugin = "2.4.1"
dokka = "1.7.20"
dokka = "1.8.10"
[libraries]
@@ -67,3 +67,4 @@ github-release-plugin = { module = "com.github.breadmoirai:github-release", vers
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

After

Width:  |  Height:  |  Size: 448 KiB

View File

@@ -19,4 +19,3 @@ include ":tgbotapi.behaviour_builder"
include ":tgbotapi.behaviour_builder.fsm"
include ":tgbotapi"
include ":tgbotapi.webapps"
include ":docs"

View File

@@ -1,6 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "org.jetbrains.dokka"
}
project.description = "API extensions with \"Telegram Bot API\"-like extensions for TelegramBot and RequestsExecutor"

View File

@@ -5,7 +5,10 @@ import dev.inmo.tgbotapi.requests.DeleteMessage
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.content.MediaGroupCollectionContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
suspend fun TelegramBot.deleteMessage(
chatId: ChatIdentifier,
@@ -21,7 +24,16 @@ suspend fun TelegramBot.deleteMessage(
suspend fun TelegramBot.deleteMessage(
message: Message
) = deleteMessage(message.chat, message.messageId)
): Boolean {
val mediaGroupContent = ((message as? ContentMessage<*>) ?.content as? MediaGroupCollectionContent<*>)
if (mediaGroupContent == null) {
return deleteMessage(message.chat, message.messageId)
} else {
return mediaGroupContent.group.map {
deleteMessage(it.sourceMessage)
}.all { it }
}
}
suspend fun TelegramBot.delete(
chatId: ChatIdentifier,

View File

@@ -6,7 +6,6 @@ import dev.inmo.tgbotapi.abstracts.*
import dev.inmo.tgbotapi.abstracts.types.WithReplyMarkup
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.location.live.editLiveLocation
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.sendLiveLocation
import dev.inmo.tgbotapi.types.*
@@ -17,6 +16,7 @@ import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.LocationContent
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.map
import kotlinx.serialization.Serializable
import kotlin.js.JsName
@@ -45,7 +45,8 @@ suspend fun TelegramBot.handleLiveLocation(
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null
allowSendingWithoutReply: Boolean? = null,
sentMessageFlow: FlowCollector<ContentMessage<LocationContent>>? = null
) {
var currentLiveLocationMessage: ContentMessage<LocationContent>? = null
val updateMessageJob = CoroutineScope(currentCoroutineContext().LinkedSupervisorJob()).launchSafelyWithoutExceptions(start = CoroutineStart.LAZY) {
@@ -73,7 +74,9 @@ suspend fun TelegramBot.handleLiveLocation(
replyToMessageId,
allowSendingWithoutReply,
it.replyMarkup
)
).also {
sentMessageFlow ?.emit(it)
}
} else {
edit(
capturedLiveLocationMessage,
@@ -83,7 +86,9 @@ suspend fun TelegramBot.handleLiveLocation(
it.heading,
it.proximityAlertRadius,
it.replyMarkup
)
).also {
sentMessageFlow ?.emit(it)
}
}
}
}
@@ -102,7 +107,8 @@ suspend fun TelegramBot.handleLiveLocation(
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null
allowSendingWithoutReply: Boolean? = null,
sentMessageFlow: FlowCollector<ContentMessage<LocationContent>>? = null
) {
handleLiveLocation(
chatId,
@@ -121,7 +127,8 @@ suspend fun TelegramBot.handleLiveLocation(
disableNotification,
protectContent,
replyToMessageId,
allowSendingWithoutReply
allowSendingWithoutReply,
sentMessageFlow
)
}
@@ -139,7 +146,8 @@ suspend fun TelegramBot.handleLiveLocation(
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null
allowSendingWithoutReply: Boolean? = null,
sentMessageFlow: FlowCollector<ContentMessage<LocationContent>>? = null
) {
handleLiveLocation(
chatId,
@@ -154,6 +162,7 @@ suspend fun TelegramBot.handleLiveLocation(
disableNotification,
protectContent,
replyToMessageId,
allowSendingWithoutReply
allowSendingWithoutReply,
sentMessageFlow
)
}

View File

@@ -2,15 +2,22 @@ package dev.inmo.tgbotapi.extensions.api
import com.soywiz.klock.DateTime
import com.soywiz.klock.TimeSpan
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.abstracts.types.WithReplyMarkup
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.location.live.editLiveLocation
import dev.inmo.tgbotapi.extensions.api.edit.location.live.stopLiveLocation
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.sendLiveLocation
import dev.inmo.tgbotapi.requests.send.SendLiveLocation
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.location.LiveLocation
import dev.inmo.tgbotapi.types.location.Location
import dev.inmo.tgbotapi.types.location.StaticLocation
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
@@ -18,7 +25,15 @@ import dev.inmo.tgbotapi.types.message.content.LocationContent
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import io.ktor.utils.io.core.Closeable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.js.JsName
import kotlin.jvm.JvmName
import kotlin.math.ceil
val defaultLivePeriodDelayMillis = (livePeriodLimit.last - 60L) * 1000L
@@ -45,7 +60,8 @@ class LiveLocationProvider internal constructor(
private set
get() = field || leftUntilCloseMillis.millisecondsLong < 0L
private var message: ContentMessage<LocationContent> = initMessage
var message: ContentMessage<LocationContent> = initMessage
private set
val lastLocation: LiveLocation
get() = message.content.location as LiveLocation

View File

@@ -0,0 +1,16 @@
package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.GetMyCommands
import dev.inmo.tgbotapi.requests.bot.GetMyDescription
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.getMyDescription(
languageCode: IetfLanguageCode? = null
) = execute(GetMyDescription(languageCode))
suspend fun TelegramBot.getMyDescription(
languageCode: String?
) = getMyDescription(languageCode ?.let(::IetfLanguageCode))

View File

@@ -0,0 +1,16 @@
package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.GetMyCommands
import dev.inmo.tgbotapi.requests.bot.GetMyShortDescription
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.getMyShortDescription(
languageCode: IetfLanguageCode? = null
) = execute(GetMyShortDescription(languageCode))
suspend fun TelegramBot.getMyShortDescription(
languageCode: String?
) = getMyShortDescription(languageCode ?.let(::IetfLanguageCode))

View File

@@ -0,0 +1,19 @@
package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.GetMyCommands
import dev.inmo.tgbotapi.requests.bot.GetMyDescription
import dev.inmo.tgbotapi.requests.bot.SetMyDescription
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.setMyDescription(
description: String? = null,
languageCode: IetfLanguageCode? = null
) = execute(SetMyDescription(description, languageCode))
suspend fun TelegramBot.setMyDescription(
description: String?,
languageCode: String?
) = setMyDescription(description, languageCode ?.let(::IetfLanguageCode))

View File

@@ -0,0 +1,15 @@
package dev.inmo.tgbotapi.extensions.api.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.bot.SetMyShortDescription
suspend fun TelegramBot.setMyShortDescription(
shortDescription: String? = null,
languageCode: IetfLanguageCode? = null
) = execute(SetMyShortDescription(shortDescription, languageCode))
suspend fun TelegramBot.setMyShortDescription(
shortDescription: String?,
languageCode: String?
) = setMyShortDescription(shortDescription, languageCode ?.let(::IetfLanguageCode))

View File

@@ -14,27 +14,31 @@ suspend fun TelegramBot.restrictChatMember(
chatId: ChatIdentifier,
userId: UserId,
untilDate: TelegramDate? = null,
permissions: ChatPermissions = ChatPermissions()
) = execute(RestrictChatMember(chatId, userId, untilDate, permissions))
permissions: ChatPermissions = ChatPermissions(),
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = execute(RestrictChatMember(chatId, userId, untilDate, permissions, useIndependentChatPermissions))
suspend fun TelegramBot.restrictChatMember(
chat: PublicChat,
userId: UserId,
untilDate: TelegramDate? = null,
permissions: ChatPermissions = ChatPermissions()
) = restrictChatMember(chat.id, userId, untilDate, permissions)
permissions: ChatPermissions = ChatPermissions(),
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = restrictChatMember(chat.id, userId, untilDate, permissions, useIndependentChatPermissions)
suspend fun TelegramBot.restrictChatMember(
chatId: IdChatIdentifier,
user: User,
untilDate: TelegramDate? = null,
permissions: ChatPermissions = ChatPermissions()
) = restrictChatMember(chatId, user.id, untilDate, permissions)
permissions: ChatPermissions = ChatPermissions(),
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = restrictChatMember(chatId, user.id, untilDate, permissions, useIndependentChatPermissions)
suspend fun TelegramBot.restrictChatMember(
chat: PublicChat,
user: User,
untilDate: TelegramDate? = null,
permissions: ChatPermissions = ChatPermissions()
) = restrictChatMember(chat.id, user.id, untilDate, permissions)
permissions: ChatPermissions = ChatPermissions(),
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = restrictChatMember(chat.id, user.id, untilDate, permissions, useIndependentChatPermissions)

View File

@@ -8,10 +8,12 @@ import dev.inmo.tgbotapi.types.chat.PublicChat
suspend fun TelegramBot.setDefaultChatMembersPermissions(
chatId: ChatIdentifier,
permissions: ChatPermissions
) = execute(SetChatPermissions(chatId, permissions))
permissions: ChatPermissions,
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = execute(SetChatPermissions(chatId, permissions, useIndependentChatPermissions))
suspend fun TelegramBot.setDefaultChatMembersPermissions(
chat: PublicChat,
permissions: ChatPermissions
) = setDefaultChatMembersPermissions(chat.id, permissions)
permissions: ChatPermissions,
useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) = setDefaultChatMembersPermissions(chat.id, permissions, useIndependentChatPermissions)

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

@@ -73,7 +73,7 @@ suspend fun TelegramBot.sendAnimation(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendAnimation(
chatId, animation.fileId, animation.thumb ?.fileId, text, parseMode, spoilered, duration, width, height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
chatId, animation.fileId, animation.thumbnail ?.fileId, text, parseMode, spoilered, duration, width, height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
)
/**
@@ -177,7 +177,7 @@ suspend fun TelegramBot.sendAnimation(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendAnimation(
chatId, animation.fileId, animation.thumb ?.fileId, entities, spoilered, duration, width, height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
chatId, animation.fileId, animation.thumbnail ?.fileId, entities, spoilered, duration, width, height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
)
/**

View File

@@ -88,7 +88,7 @@ suspend fun TelegramBot.sendAudio(
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendAudio(chatId, audio.fileId, audio.thumb ?.fileId, text, parseMode, audio.duration, audio.performer, title, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendAudio(chatId, audio.fileId, audio.thumbnail ?.fileId, text, parseMode, audio.duration, audio.performer, title, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -180,7 +180,7 @@ suspend inline fun TelegramBot.sendAudio(
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendAudio(chatId, audio.fileId, audio.thumb ?.fileId, entities, audio.duration, audio.performer, title, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendAudio(chatId, audio.fileId, audio.thumbnail ?.fileId, entities, audio.duration, audio.performer, title, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or

View File

@@ -83,7 +83,7 @@ suspend fun TelegramBot.sendDocument(
replyMarkup: KeyboardMarkup? = null,
disableContentTypeDetection: Boolean? = null
) = sendDocument(
chatId, document.fileId, document.thumb ?.fileId, text, parseMode, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup, disableContentTypeDetection
chatId, document.fileId, document.thumbnail ?.fileId, text, parseMode, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup, disableContentTypeDetection
)
/**
@@ -170,7 +170,7 @@ suspend inline fun TelegramBot.sendDocument(
replyMarkup: KeyboardMarkup? = null,
disableContentTypeDetection: Boolean? = null
) = sendDocument(
chatId, document.fileId, document.thumb ?.fileId, entities, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup, disableContentTypeDetection
chatId, document.fileId, document.thumbnail ?.fileId, entities, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup, disableContentTypeDetection
)
/**

View File

@@ -70,7 +70,7 @@ suspend fun TelegramBot.sendVideo(
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendVideo(chatId, video.fileId, video.thumb ?.fileId, text, parseMode, spoilered, video.duration, video.width, video.height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendVideo(chatId, video.fileId, video.thumbnail ?.fileId, text, parseMode, spoilered, video.duration, video.width, video.height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -167,7 +167,7 @@ suspend inline fun TelegramBot.sendVideo(
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendVideo(chatId, video.fileId, video.thumb ?.fileId, entities, spoilered, video.duration, video.width, video.height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendVideo(chatId, video.fileId, video.thumbnail ?.fileId, entities, spoilered, video.duration, video.width, video.height, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or

View File

@@ -57,7 +57,7 @@ suspend fun TelegramBot.sendVideoNote(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendVideoNote(
chatId, videoNote.fileId, videoNote.thumb ?.fileId, videoNote.duration, videoNote.width, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
chatId, videoNote.fileId, videoNote.thumbnail ?.fileId, videoNote.duration, videoNote.width, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup
)
/**

View File

@@ -0,0 +1,25 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.stickers.DeleteStickerFromSet
import dev.inmo.tgbotapi.requests.stickers.DeleteStickerSet
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.deleteStickerSet(
name: StickerSetName
) = execute(
DeleteStickerSet(name)
)
suspend fun TelegramBot.deleteStickerSet(
sticker: Sticker
) = deleteStickerSet(
sticker.stickerSetName ?: error("Unable to take name of sticker set from sticker $sticker")
)
suspend fun TelegramBot.deleteStickerSet(
stickerSet: StickerSet,
) = deleteStickerSet(stickerSet.name)

View File

@@ -0,0 +1,25 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.stickers.SetStickerEmojiList
import dev.inmo.tgbotapi.requests.stickers.SetStickerPositionInSet
import dev.inmo.tgbotapi.types.files.Sticker
suspend fun TelegramBot.setStickerEmojiList(
sticker: FileId,
emojis: List<String>
) = execute(
SetStickerEmojiList(
sticker,
emojis
)
)
suspend fun TelegramBot.setStickerEmojiList(
sticker: Sticker,
vararg emojis: String
) = setStickerEmojiList(
sticker.fileId,
emojis.toList()
)

View File

@@ -0,0 +1,26 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.stickers.SetStickerEmojiList
import dev.inmo.tgbotapi.requests.stickers.SetStickerKeywords
import dev.inmo.tgbotapi.requests.stickers.SetStickerPositionInSet
import dev.inmo.tgbotapi.types.files.Sticker
suspend fun TelegramBot.setStickerKeywords(
sticker: FileId,
keywords: List<String>
) = execute(
SetStickerKeywords(
sticker,
keywords
)
)
suspend fun TelegramBot.setStickerKeywords(
sticker: Sticker,
vararg keywords: String
) = setStickerKeywords(
sticker.fileId,
keywords.toList()
)

View File

@@ -0,0 +1,19 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.stickers.SetStickerEmojiList
import dev.inmo.tgbotapi.requests.stickers.SetStickerMaskPosition
import dev.inmo.tgbotapi.requests.stickers.SetStickerPositionInSet
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.setStickerMaskPosition(
sticker: FileId,
maskPosition: MaskPosition
) = execute(
SetStickerMaskPosition(
sticker,
maskPosition
)
)

View File

@@ -3,27 +3,95 @@ package dev.inmo.tgbotapi.extensions.api.thumbs
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.SetStickerSetThumb
import dev.inmo.tgbotapi.requests.stickers.SetStickerSetThumbnail
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
stickerSetName: StickerSetName,
thumbnail: FileId
) = execute(
SetStickerSetThumbnail(userId, stickerSetName, thumbnail)
)
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
stickerSetName: StickerSetName,
thumbnail: MultipartFile
) = execute(
SetStickerSetThumbnail(userId, stickerSetName, thumbnail)
)
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
stickerSetName: StickerSetName,
thumbnail: FileId
) = setStickerSetThumbnail(
user.id, stickerSetName, thumbnail
)
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
stickerSetName: StickerSetName,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
user.id, stickerSetName, thumbnail
)
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
stickerSet: StickerSet,
thumbnail: FileId
) = setStickerSetThumbnail(
userId, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
stickerSet: StickerSet,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
userId, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
stickerSet: StickerSet,
thumbnail: FileId
) = setStickerSetThumbnail(
user.id, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
stickerSet: StickerSet,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
user.id, stickerSet.name, thumbnail
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(userId, thumbSetName, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
userId: UserId,
thumbSetName: String,
thumb: FileId
) = execute(
SetStickerSetThumb(userId, thumbSetName, thumb)
SetStickerSetThumbnail(userId, thumbSetName, thumb)
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(userId, thumbSetName, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
userId: UserId,
thumbSetName: String,
thumb: MultipartFile
) = execute(
SetStickerSetThumb(userId, thumbSetName, thumb)
SetStickerSetThumbnail(userId, thumbSetName, thumb)
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(user, thumbSetName, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
user: CommonUser,
thumbSetName: String,
@@ -32,6 +100,7 @@ suspend fun TelegramBot.setStickerSetThumb(
user.id, thumbSetName, thumb
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(user, thumbSetName, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
user: CommonUser,
thumbSetName: String,
@@ -40,6 +109,7 @@ suspend fun TelegramBot.setStickerSetThumb(
user.id, thumbSetName, thumb
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(userId, thumbSet, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
userId: UserId,
thumbSet: StickerSet,
@@ -48,6 +118,7 @@ suspend fun TelegramBot.setStickerSetThumb(
userId, thumbSet.name, thumb
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(userId, thumbSet, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
userId: UserId,
thumbSet: StickerSet,
@@ -56,6 +127,7 @@ suspend fun TelegramBot.setStickerSetThumb(
userId, thumbSet.name, thumb
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(user, thumbSet, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
user: CommonUser,
thumbSet: StickerSet,
@@ -64,6 +136,7 @@ suspend fun TelegramBot.setStickerSetThumb(
user.id, thumbSet.name, thumb
)
@Deprecated("Renamed in telegram bot api", ReplaceWith("setStickerSetThumbnail(user, thumbSet, thumb)", "dev.inmo.tgbotapi.extensions.api.thumbs.setStickerSetThumbnail"))
suspend fun TelegramBot.setStickerSetThumb(
user: CommonUser,
thumbSet: StickerSet,

View File

@@ -0,0 +1,22 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.stickers.SetStickerSetTitle
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.setStickerSetTitle(
name: StickerSetName,
title: String
) = execute(SetStickerSetTitle(name, title))
suspend fun TelegramBot.setStickerSetTitle(
sticker: Sticker,
title: String
) = setStickerSetTitle(sticker.stickerSetName ?: error("Unable to take name of sticker set from sticker $sticker"), title)
suspend fun TelegramBot.setStickerSetTitle(
stickerSet: StickerSet,
title: String
) = setStickerSetTitle(stickerSet.name, title)

View File

@@ -1,7 +1,8 @@
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.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.Update
import dev.inmo.tgbotapi.updateshandlers.UpdateReceiver
@@ -28,26 +29,18 @@ fun CoroutineScope.updateHandlerWithMediaGroupsAdaptation(
)
launch {
launch {
launchSafelyWithoutExceptions {
for (update in updatesChannel) {
val dataAsPossiblySentViaBotCommonMessage = update.data as? PossiblySentViaBotCommonMessage<*>
if (dataAsPossiblySentViaBotCommonMessage == null) {
output(update)
continue
val data = update.data
when {
data is PossiblyMediaGroupMessage<*> && data.mediaGroupId != null -> {
mediaGroupChannel.send("${data.mediaGroupId}${update::class.simpleName}" to update as BaseMessageUpdate)
}
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) {
mediaGroup.convertWithMediaGroupUpdates().forEach {
output(it)

View File

@@ -1,6 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "org.jetbrains.dokka"
}
project.description = "Behaviour Builder DSL"

View File

@@ -8,6 +8,8 @@ import dev.inmo.micro_utils.fsm.common.utils.StateHandlingErrorHandler
import dev.inmo.micro_utils.fsm.common.utils.defaultStateHandlingErrorHandler
import dev.inmo.tgbotapi.bot.TelegramBot
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.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.*
@@ -46,6 +48,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
* Use [buildBehaviourWithFSM] to create [BehaviourContextWithFSM] and launch getting of updates
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* 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(
upstreamUpdatesFlow: Flow<Update>? = null,
@@ -54,6 +60,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<DefaultBehaviourContextWithFSM<T>, Job> = buildBehaviourWithFSM(
upstreamUpdatesFlow,
@@ -66,7 +76,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
).run {
this to scope.launch {
start()
longPolling(flowsUpdatesFilter, scope = scope)
longPolling(flowsUpdatesFilter, timeoutSeconds, scope, autoDisableWebhooks, autoSkipTimeoutExceptions, mediaGroupsDebounceTimeMillis, defaultExceptionsHandler)
}
}
@@ -112,6 +122,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
* using [longPolling]. For [longPolling] will be used result [BehaviourContextWithFSM] for both parameters
* 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 BehaviourContext
* @see longPolling
@@ -124,6 +138,10 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
) = FlowsUpdatesFilter().let {
buildBehaviourWithFSM(
@@ -138,7 +156,12 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
start()
longPolling(
flowsUpdatesFilter,
scope = scope
timeoutSeconds,
scope,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
defaultExceptionsHandler
)
}
}

View File

@@ -11,6 +11,8 @@ import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.bot.TelegramBot
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.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.CoroutineScope
@@ -25,6 +27,10 @@ import kotlin.coroutines.coroutineContext
* **WARNING** This method WILL NOT launch any listening of updates. Use something like
* [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]
*
* @see [BehaviourContext]
@@ -42,6 +48,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): TelegramBot = telegramBot(
token,
@@ -56,6 +66,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block
)
}
@@ -64,6 +78,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
* Create bot using [telegramBot] and start listening for updates using [buildBehaviourWithFSMAndStartLongPolling]. This
* 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
* here
*
@@ -81,6 +99,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
@@ -95,6 +117,10 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
statesManager,
presetHandlers,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block
)
}

View File

@@ -1,6 +1,7 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "org.jetbrains.dokka"
}
project.description = "Behaviour Builder DSL"

View File

@@ -5,6 +5,8 @@ import dev.inmo.micro_utils.coroutines.ExceptionHandler
import dev.inmo.tgbotapi.bot.TelegramBot
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.updateHandlerWithMediaGroupsAdaptation
import dev.inmo.tgbotapi.types.Seconds
import dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter
import kotlinx.coroutines.*
@@ -46,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
* 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 BehaviourContext
* @see startGettingOfUpdatesByLongPolling
@@ -53,6 +59,10 @@ suspend fun TelegramBot.buildBehaviour(
suspend fun TelegramBot.buildBehaviourWithLongPolling(
scope: CoroutineScope = defaultCoroutineScopeProvider(),
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: BehaviourContextReceiver<Unit>
): Job {
val behaviourContext = buildBehaviour(
@@ -62,6 +72,10 @@ suspend fun TelegramBot.buildBehaviourWithLongPolling(
)
return longPolling(
behaviourContext,
scope = behaviourContext
scope = behaviourContext,
timeoutSeconds = timeoutSeconds,
autoDisableWebhooks = autoDisableWebhooks,
autoSkipTimeoutExceptions = autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis = mediaGroupsDebounceTimeMillis
)
}

View File

@@ -5,6 +5,8 @@ import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.ktor.telegramBot
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.updateshandlers.FlowsUpdatesFilter
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.*
@@ -52,6 +54,10 @@ suspend fun telegramBotWithBehaviour(
*
* **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
* here
*
@@ -66,6 +72,10 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
builder: KtorRequestsExecutorBuilder.() -> Unit = {},
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
testServer: Boolean = false,
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
autoSkipTimeoutExceptions: Boolean = true,
mediaGroupsDebounceTimeMillis: Long? = 1000L,
block: BehaviourContextReceiver<Unit>
): Pair<TelegramBot, Job> {
return telegramBot(
@@ -77,6 +87,10 @@ suspend fun telegramBotWithBehaviourAndLongPolling(
it to it.buildBehaviourWithLongPolling(
scope ?: CoroutineScope(coroutineContext),
defaultExceptionsHandler,
timeoutSeconds,
autoDisableWebhooks,
autoSkipTimeoutExceptions,
mediaGroupsDebounceTimeMillis,
block
)
}

View File

@@ -16,119 +16,180 @@ typealias CommonMessageToContentMapper<T> = suspend CommonMessage<T>.() -> T?
@RiskFeature(lowLevelRiskFeatureMessage)
suspend inline fun <reified O : MessageContent> BehaviourContext.waitContent(
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null }
): Flow<O> = waitContentMessage<O>(initRequest, includeMediaGroups, errorFactory).map { it.content }
): Flow<O> = waitContentMessage<O>(initRequest, errorFactory).map { it.content }
@Deprecated(
includeMediaGroupsDeprecationMessage,
ReplaceWith("waitAnyContent(initRequest, errorFactory)", "dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContent")
)
suspend fun BehaviourContext.waitContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<MessageContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitContact(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<ContactContent>(initRequest, false, errorFactory)
) = waitContent<ContactContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<DiceContent>(initRequest, false, errorFactory)
) = waitContent<DiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitGame(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<GameContent>(initRequest, false, errorFactory)
) = waitContent<GameContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<LocationContent>(initRequest, false, errorFactory)
) = waitContent<LocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitLiveLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<LiveLocationContent>(initRequest, false, errorFactory)
) = waitContent<LiveLocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitStaticLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<StaticLocationContent>(initRequest, false, errorFactory)
) = waitContent<StaticLocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitPoll(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<PollContent>(initRequest, false, errorFactory)
) = waitContent<PollContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitText(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<TextContent>(initRequest, false, errorFactory)
) = waitContent<TextContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVenue(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VenueContent>(initRequest, false, errorFactory)
) = waitContent<VenueContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<AudioMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<AudioMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<AudioMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitDocumentMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<DocumentMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<DocumentMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDocumentMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<DocumentMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContent<MediaContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<MediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<MediaContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<MediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<MediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<MediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<VisualMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContent<TextedMediaContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<TextedMediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<TextedMediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<AnimationContent>(initRequest, false, errorFactory)
) = waitContent<AnimationContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContent<AudioContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<AudioContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<AudioContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContent<DocumentContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<DocumentContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<DocumentContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContent<PhotoContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<PhotoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<PhotoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitSticker(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<StickerContent>(initRequest, false, errorFactory)
) = waitContent<StickerContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContent<VideoContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContent<VideoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VideoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVideoNote(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VideoNoteContent>(initRequest, false, errorFactory)
) = waitContent<VideoNoteContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VoiceContent>(initRequest, false, errorFactory)
) = waitContent<VoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitInvoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<InvoiceContent>(initRequest, false, errorFactory)
) = waitContent<InvoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)

View File

@@ -13,17 +13,20 @@ import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
import kotlinx.coroutines.flow.Flow
const val includeMediaGroupsDeprecationMessage = "includeMediaGroups is deprecated and its usage will not lead to any changes"
typealias CommonMessageToCommonMessageMapper<T> = suspend CommonMessage<T>.() -> CommonMessage<T>?
@RiskFeature(lowLevelRiskFeatureMessage)
suspend inline fun <reified O : MessageContent> BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null }
): Flow<CommonMessage<O>> = expectFlow(
initRequest,
errorFactory
) {
if (it !is BaseSentMessageUpdate) {
return@expectFlow emptyList()
}
listOfNotNull((it.data as? CommonMessage<*>) ?.withContent<O>())
}
@@ -44,114 +47,177 @@ internal inline fun <reified T : MessageContent> contentMessageConverter(
if (content is T) this as CommonMessage<T> else null
}
@Deprecated(
includeMediaGroupsDeprecationMessage,
ReplaceWith("waitAnyContentMessage(initRequest, errorFactory)", "dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContentMessage")
)
suspend fun BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<MessageContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContentMessage<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitContactMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<ContactContent>(initRequest, false, errorFactory)
) = waitContentMessage<ContactContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<DiceContent>(initRequest, false, errorFactory)
) = waitContentMessage<DiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitGameMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<GameContent>(initRequest, false, errorFactory)
) = waitContentMessage<GameContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<LocationContent>(initRequest, false, errorFactory)
) = waitContentMessage<LocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitLiveLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<LiveLocationContent>(initRequest, false, errorFactory)
) = waitContentMessage<LiveLocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitStaticLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<StaticLocationContent>(initRequest, false, errorFactory)
) = waitContentMessage<StaticLocationContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitPollMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<PollContent>(initRequest, false, errorFactory)
) = waitContentMessage<PollContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<TextContent>(initRequest, false, errorFactory)
) = waitContentMessage<TextContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVenueMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VenueContent>(initRequest, false, errorFactory)
) = waitContentMessage<VenueContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudioMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<AudioMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<AudioMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAudioMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<AudioMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitDocumentMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<DocumentMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<DocumentMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDocumentMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<DocumentMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitMediaMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContentMessage<MediaContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<MediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitMediaMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<MediaContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAnyMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<MediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<MediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<MediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVisualMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitTextedMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = true
) = waitContentMessage<TextedMediaContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<TextedMediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextedMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<TextedMediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnimationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<AnimationContent>(initRequest, false, errorFactory)
) = waitContentMessage<AnimationContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudioMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContentMessage<AudioContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<AudioContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAudioMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<AudioContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitDocumentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContentMessage<DocumentContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<DocumentContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDocumentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<DocumentContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitPhotoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContentMessage<PhotoContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<PhotoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitPhotoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<PhotoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitStickerMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<StickerContent>(initRequest, false, errorFactory)
) = waitContentMessage<StickerContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean = false
) = waitContentMessage<VideoContent>(initRequest, includeMediaGroups, errorFactory)
includeMediaGroups: Boolean
) = waitContentMessage<VideoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VideoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVideoNoteMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VideoNoteContent>(initRequest, false, errorFactory)
) = waitContentMessage<VideoNoteContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VoiceContent>(initRequest, false, errorFactory)
) = waitContentMessage<VoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitInvoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<InvoiceContent>(initRequest, false, errorFactory)
) = waitContentMessage<InvoiceContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)

View File

@@ -17,6 +17,9 @@ import dev.inmo.tgbotapi.types.message.ChatEvents.forum.WriteAccessAllowed
import dev.inmo.tgbotapi.types.message.ChatEvents.voice.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent
import dev.inmo.tgbotapi.types.request.ChatShared
import dev.inmo.tgbotapi.types.request.ChatSharedRequest
import dev.inmo.tgbotapi.types.request.UserShared
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
import kotlinx.coroutines.flow.Flow
@@ -171,3 +174,18 @@ suspend fun BehaviourContext.waitWriteAccessAllowed(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<WriteAccessAllowed>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatSharedRequest(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<ChatSharedRequest>(initRequest, errorFactory)
suspend fun BehaviourContext.waitUserShared(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<UserShared>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatShared(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<ChatShared>(initRequest, errorFactory)

View File

@@ -9,10 +9,17 @@ import dev.inmo.tgbotapi.types.message.ChatEvents.*
import dev.inmo.tgbotapi.types.message.ChatEvents.abstracts.*
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicClosed
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicCreated
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicEdited
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.ForumTopicReopened
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.GeneralForumTopicHidden
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.GeneralForumTopicUnhidden
import dev.inmo.tgbotapi.types.message.ChatEvents.forum.WriteAccessAllowed
import dev.inmo.tgbotapi.types.message.ChatEvents.voice.*
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent
import dev.inmo.tgbotapi.types.request.ChatShared
import dev.inmo.tgbotapi.types.request.ChatSharedRequest
import dev.inmo.tgbotapi.types.request.UserShared
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
import kotlinx.coroutines.flow.Flow
@@ -148,3 +155,34 @@ suspend fun BehaviourContext.waitForumTopicReopenedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<ForumTopicReopened>(initRequest, errorFactory)
suspend fun BehaviourContext.waitForumTopicEditedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<ForumTopicEdited>(initRequest, errorFactory)
suspend fun BehaviourContext.waitGeneralForumTopicHiddenEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<GeneralForumTopicHidden>(initRequest, errorFactory)
suspend fun BehaviourContext.waitGeneralForumTopicUnhiddenEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<GeneralForumTopicUnhidden>(initRequest, errorFactory)
suspend fun BehaviourContext.waitWriteAccessAllowedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<WriteAccessAllowed>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatSharedRequestEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<ChatSharedRequest>(initRequest, errorFactory)
suspend fun BehaviourContext.waitUserSharedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<UserShared>(initRequest, errorFactory)
suspend fun BehaviourContext.waitChatSharedEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<ChatShared>(initRequest, errorFactory)

View File

@@ -607,3 +607,27 @@ suspend fun <BC : BehaviourContext> BC.onInvoice(
markerFactory,
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,
) {
(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)
}.also {
triggersHolder.handleableCommandsHolder.registerHandleable(startRegex)

View File

@@ -1,18 +1,3 @@
/**
* @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
*/@file:Suppress("unused", "UNCHECKED_CAST")
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
@@ -35,7 +20,7 @@ internal suspend inline fun <BC : BehaviourContext, reified T : MessageContent>
noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<T>>
) = on(markerFactory, initialFilter, subcontextUpdatesFilter, scenarioReceiver) {
when (it) {
is BaseEditMessageUpdate -> (it.editMessageUpdateOrNull() ?.data ?.withContent<T>())
is BaseEditMessageUpdate -> (it.data.withContent<T>())
else -> null
} ?.let(::listOfNotNull)
}

View File

@@ -23,6 +23,9 @@ import dev.inmo.tgbotapi.types.message.PrivateEventMessage
import dev.inmo.tgbotapi.types.message.abstracts.ChatEventMessage
import dev.inmo.tgbotapi.types.message.abstracts.SupergroupEventMessage
import dev.inmo.tgbotapi.types.message.payments.SuccessfulPaymentEvent
import dev.inmo.tgbotapi.types.request.ChatShared
import dev.inmo.tgbotapi.types.request.ChatSharedRequest
import dev.inmo.tgbotapi.types.request.UserShared
import dev.inmo.tgbotapi.types.update.abstracts.Update
internal suspend inline fun <BC : BehaviourContext, reified T : ChatEvent> BC.onEvent(
@@ -657,3 +660,65 @@ suspend fun <BC : BehaviourContext> BC.onWriteAccessAllowed(
markerFactory: MarkerFactory<in ChatEventMessage<WriteAccessAllowed>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, SupergroupEventMessage<WriteAccessAllowed>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, 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.onChatSharedRequest(
initialFilter: SimpleFilter<PrivateEventMessage<ChatSharedRequest>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, PrivateEventMessage<ChatSharedRequest>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<ChatSharedRequest>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, PrivateEventMessage<ChatSharedRequest>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, 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.onUserShared(
initialFilter: SimpleFilter<PrivateEventMessage<UserShared>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, PrivateEventMessage<UserShared>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<UserShared>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, PrivateEventMessage<UserShared>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, 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.onChatShared(
initialFilter: SimpleFilter<PrivateEventMessage<ChatShared>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, PrivateEventMessage<ChatShared>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<ChatShared>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, PrivateEventMessage<ChatShared>>
) = onEventWithCustomChatEventMessage(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)

View File

@@ -2,6 +2,7 @@ plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.google.devtools.ksp"
id "org.jetbrains.dokka"
}
project.description = "Core part of tgbotapi with all (and only) required functionality for working with Telegram Bot API"

View File

@@ -0,0 +1,23 @@
package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
class GetMyDescription(
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<BotDescription>, WithOptionalLanguageCode {
override fun method(): String = "getMyDescription"
override val resultDeserializer: DeserializationStrategy<BotDescription>
get() = BotDescription.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -0,0 +1,23 @@
package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
class GetMyShortDescription(
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<BotShortDescription>, WithOptionalLanguageCode {
override fun method(): String = "getMyShortDescription"
override val resultDeserializer: DeserializationStrategy<BotShortDescription>
get() = BotShortDescription.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
class SetMyDescription(
@SerialName(descriptionField)
val description: String? = null,
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyDescription"
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.tgbotapi.requests.bot
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
import dev.inmo.micro_utils.language_codes.IetfLanguageCodeSerializer
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.abstracts.WithOptionalLanguageCode
import dev.inmo.tgbotapi.types.commands.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
class SetMyShortDescription(
@SerialName(shortDescriptionField)
val shortDescription: String? = null,
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyShortDescription"
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -16,7 +16,9 @@ data class RestrictChatMember(
@SerialName(untilDateField)
override val untilDate: TelegramDate? = null,
@SerialName(permissionsField)
val permissions: ChatPermissions = ChatPermissions()
val permissions: ChatPermissions = ChatPermissions(),
@SerialName(useIndependentChatPermissionsField)
val useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
) : ChatMemberRequest<Boolean>, UntilDate {
override fun method(): String = "restrictChatMember"
override val resultDeserializer: DeserializationStrategy<Boolean>

View File

@@ -12,7 +12,9 @@ data class SetChatPermissions (
@SerialName(chatIdField)
override val chatId: ChatIdentifier,
@SerialName(permissionsField)
val permissions: ChatPermissions
val permissions: ChatPermissions,
@SerialName(useIndependentChatPermissionsField)
val useIndependentChatPermissions: Boolean? = permissions.isGranular.takeIf { it }
): ChatRequest, SimpleRequest<Boolean> {
override fun method(): String = "setChatPermissions"
override val resultDeserializer: DeserializationStrategy<Boolean>

View File

@@ -1,5 +1,9 @@
package dev.inmo.tgbotapi.requests.send.abstracts
interface ThumbedSendMessageRequest<T: Any>: SendMessageRequest<T> {
val thumbnail: String?
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnail"))
val thumb: String?
}
get() = thumbnail
}

View File

@@ -129,8 +129,8 @@ data class SendAnimationData internal constructor(
override val chatId: ChatIdentifier,
@SerialName(animationField)
val animation: String? = null,
@SerialName(thumbField)
override val thumb: String? = null,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
@@ -187,8 +187,8 @@ data class SendAnimationData internal constructor(
data class SendAnimationFiles internal constructor(
val animation: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
animationField to animation,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -126,8 +126,8 @@ data class SendAudioData internal constructor(
override val chatId: ChatIdentifier,
@SerialName(audioField)
val audio: String? = null,
@SerialName(thumbField)
override val thumb: String? = null,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
@@ -182,8 +182,8 @@ data class SendAudioData internal constructor(
data class SendAudioFiles internal constructor(
val audio: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
audioField to audio,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -144,8 +144,8 @@ data class SendDocumentData internal constructor(
override val chatId: ChatIdentifier,
@SerialName(documentField)
val document: String? = null,
@SerialName(thumbField)
override val thumb: String? = null,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
@@ -193,8 +193,8 @@ data class SendDocumentData internal constructor(
data class SendDocumentFiles internal constructor(
val document: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
documentField to document,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -8,14 +8,19 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializationStrategyClass
import dev.inmo.tgbotapi.types.message.content.StickerContent
import dev.inmo.tgbotapi.utils.mapOfNotNull
import dev.inmo.tgbotapi.utils.toJsonWithoutNulls
import kotlinx.serialization.*
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
fun SendSticker(
chatId: ChatIdentifier,
sticker: InputFile,
threadId: MessageThreadId? = chatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
@@ -32,7 +37,7 @@ fun SendSticker(
replyMarkup
).let {
when (sticker) {
is MultipartFile -> SendStickerByFile(it, sticker)
is MultipartFile -> SendStickerByFile(it, sticker, emoji)
is FileId -> it
}
}
@@ -69,8 +74,16 @@ data class SendStickerByFileId internal constructor(
data class SendStickerByFile internal constructor(
@Transient
private val sendStickerByFileId: SendStickerByFileId,
val sticker: MultipartFile
val sticker: MultipartFile,
val emoji: String?
) : MultipartRequest<ContentMessage<StickerContent>>, Request<ContentMessage<StickerContent>> by sendStickerByFileId {
override val mediaMap: Map<String, MultipartFile> = mapOf(stickerField to sticker)
override val paramsJson: JsonObject = sendStickerByFileId.toJsonWithoutNulls(SendStickerByFileId.serializer())
override val paramsJson: JsonObject
get() {
return JsonObject(
mapOfNotNull(
emojiField to emoji ?.let { JsonPrimitive(it) }
) + sendStickerByFileId.toJsonWithoutNulls(SendStickerByFileId.serializer())
)
}
}

View File

@@ -133,8 +133,8 @@ data class SendVideoData internal constructor(
override val chatId: ChatIdentifier,
@SerialName(videoField)
val video: String? = null,
@SerialName(thumbField)
override val thumb: String? = null,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(captionField)
override val text: String? = null,
@SerialName(parseModeField)
@@ -193,8 +193,8 @@ data class SendVideoData internal constructor(
data class SendVideoFiles internal constructor(
val video: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
videoField to video,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -62,8 +62,8 @@ data class SendVideoNoteData internal constructor(
override val chatId: ChatIdentifier,
@SerialName(videoNoteField)
val videoNote: String? = null,
@SerialName(thumbField)
override val thumb: String? = null,
@SerialName(thumbnailField)
override val thumbnail: String? = null,
@SerialName(durationField)
override val duration: Long? = null,
@SerialName(lengthField)
@@ -99,8 +99,8 @@ data class SendVideoNoteData internal constructor(
data class SendVideoNoteFiles internal constructor(
val videoNote: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
videoNoteField to videoNote,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -156,8 +156,8 @@ data class SendVoiceData internal constructor(
data class SendVoiceFiles internal constructor(
val voice: MultipartFile? = null,
val thumb: MultipartFile? = null
val thumbnail: MultipartFile? = null
) : Files by mapOfNotNull(
voiceField to voice,
thumbField to thumb
thumbnailField to thumbnail
)

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerAction
import dev.inmo.tgbotapi.types.stickerField
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@@ -9,8 +10,8 @@ import kotlinx.serialization.builtins.serializer
@Serializable
data class DeleteStickerFromSet(
@SerialName(stickerField)
val sticker: FileId
) : SimpleRequest<Boolean> {
override val sticker: FileId
) : StickerAction<Boolean> {
override fun method(): String = "deleteStickerFromSet"
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()

View File

@@ -0,0 +1,21 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.nameField
import dev.inmo.tgbotapi.types.stickerField
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
data class DeleteStickerSet(
@SerialName(nameField)
val name: StickerSetName
) : SimpleRequest<Boolean> {
override fun method(): String = "deleteStickerSet"
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
}

View File

@@ -0,0 +1,33 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.OwnerStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerSetAction
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
data class SetStickerEmojiList (
@SerialName(stickerField)
override val sticker: FileId,
@SerialName(emojiListField)
val emojis: List<String>
) : StickerAction<Boolean> {
constructor(sticker: FileId, vararg emojis: String) : this(sticker, emojis.toList())
init {
require(emojis.size !in emojisInStickerLimit) {
"Emojis size should be in range $emojisInStickerLimit, but was ${emojis.size}"
}
}
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "setStickerEmojiList"
}

View File

@@ -0,0 +1,38 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.OwnerStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerSetAction
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
data class SetStickerKeywords (
@SerialName(stickerField)
override val sticker: FileId,
@SerialName(keywordsField)
val keywords: List<String>
) : StickerAction<Boolean> {
constructor(sticker: FileId, vararg keywords: String) : this(sticker, keywords.toList())
init {
require(keywords.size !in keywordsInStickerLimit) {
"Keywords list size should be in range $keywordsInStickerLimit, but was ${keywords.size}"
}
keywords.forEach {
require(it.length in stickerKeywordLengthLimit) {
"Keyword length should be in range $stickerKeywordLengthLimit, but was ${it.length} (word \"$it\")"
}
}
}
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "setStickerKeywords"
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.OwnerStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@Serializable
data class SetStickerMaskPosition (
@SerialName(stickerField)
override val sticker: FileId,
@SerialName(maskPositionField)
val maskPosition: MaskPosition
) : StickerAction<Boolean> {
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "setStickerMaskPosition"
}

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerAction
import dev.inmo.tgbotapi.types.positionField
import dev.inmo.tgbotapi.types.stickerField
import kotlinx.serialization.*
@@ -10,10 +11,10 @@ import kotlinx.serialization.builtins.serializer
@Serializable
data class SetStickerPositionInSet(
@SerialName(stickerField)
val sticker: FileId,
override val sticker: FileId,
@SerialName(positionField)
val position: Int
) : SimpleRequest<Boolean> {
) : StickerAction<Boolean> {
init {
if (position < 0) {
throw IllegalArgumentException("Position must be positive or 0")

View File

@@ -0,0 +1,47 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.OwnerStickerSetAction
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.*
fun SetStickerSetThumbnail(
userId: UserId,
stickerSetName: String,
thumbnail: MultipartFile
): Request<Boolean> {
return CommonMultipartFileRequest(
SetStickerSetThumbnail(userId, stickerSetName),
mapOf(thumbnailField to thumbnail)
)
}
@Deprecated("Renamed", ReplaceWith("SetStickerSetThumbnail(userId, stickerSetName, thumbnail)", "dev.inmo.tgbotapi.requests.stickers.SetStickerSetThumbnail"))
fun SetStickerSetThumb(
userId: UserId,
stickerSetName: String,
thumbnail: MultipartFile
): Request<Boolean> = SetStickerSetThumbnail(userId, stickerSetName, thumbnail)
@Serializable
data class SetStickerSetThumbnail (
@SerialName(userIdField)
override val userId: UserId,
@SerialName(nameField)
override val name: StickerSetName,
@SerialName(thumbnailField)
val thumbnail: FileId? = null
) : OwnerStickerSetAction {
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "setStickerSetThumbnail"
}
@Deprecated("Renamed", ReplaceWith("SetStickerSetThumbnail(userId, name, thumbnail)", "dev.inmo.tgbotapi.requests.stickers.SetStickerSetThumbnail"))
fun SetStickerSetThumb(
userId: UserId,
name: StickerSetName,
thumbnail: FileId? = null
) = SetStickerSetThumbnail(userId, name, thumbnail)

View File

@@ -6,28 +6,15 @@ import dev.inmo.tgbotapi.requests.stickers.abstracts.StickerSetAction
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.*
fun SetStickerSetThumb(
userId: UserId,
stickerSetName: String,
thumb: MultipartFile
): Request<Boolean> {
return CommonMultipartFileRequest(
SetStickerSetThumb(userId, stickerSetName),
mapOf(thumbField to thumb)
)
}
@Serializable
data class SetStickerSetThumb (
@SerialName(userIdField)
override val userId: UserId,
data class SetStickerSetTitle (
@SerialName(nameField)
override val name: StickerSetName,
@SerialName(thumbField)
val thumb: FileId? = null
@SerialName(titleField)
val title: String
) : StickerSetAction {
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "setStickerSetThumb"
override fun method(): String = "setStickerSetTitle"
}

View File

@@ -0,0 +1,11 @@
package dev.inmo.tgbotapi.requests.stickers.abstracts
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.UserId
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
interface OwnerStickerSetAction : StickerSetAction {
val userId: UserId
}

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.requests.stickers.abstracts
import dev.inmo.tgbotapi.types.stickers.MaskPosition
interface StandardStickerSetAction : StickerSetAction {
interface StandardStickerSetAction : OwnerStickerSetAction {
val emojis: String // must be more than one
val maskPosition: MaskPosition?
}
}

View File

@@ -0,0 +1,8 @@
package dev.inmo.tgbotapi.requests.stickers.abstracts
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
interface StickerAction<T : Any> : SimpleRequest<T> {
val sticker: FileId
}

View File

@@ -1,14 +1,14 @@
package dev.inmo.tgbotapi.requests.stickers.abstracts
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.StickerSetName
import dev.inmo.tgbotapi.types.UserId
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
interface StickerSetAction : SimpleRequest<Boolean> {
val userId: UserId
val name: String
val name: StickerSetName
override val resultDeserializer: KSerializer<Boolean>
get() = Boolean.serializer()
}
}

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.types
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BotDescription(
@SerialName(descriptionField)
val description: String
)

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.types
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BotShortDescription(
@SerialName(shortDescriptionField)
val shortDescription: String
)

View File

@@ -14,6 +14,7 @@ import kotlinx.serialization.json.longOrNull
import kotlin.jvm.JvmInline
const val internalLinkBeginning = "https://t.me"
const val internalUserLinkBeginning = "tg://user?id="
@Serializable(ChatIdentifierSerializer::class)
@ClassCastsIncluded
@@ -66,15 +67,18 @@ fun IdChatIdentifier.toChatWithThreadId(threadId: MessageThreadId) = IdChatIdent
*/
@Warning("This API have restrictions in Telegram System")
val Identifier.userLink: String
get() = "tg://user?id=$this"
get() = "$internalUserLinkBeginning$this"
/**
* https://core.telegram.org/bots/api#formatting-options
*/
@Warning("This API have restrictions in Telegram System")
val UserId.userLink: String
get() = chatId.userLink
val User.link: String
val User.userLink: String
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

View File

@@ -134,6 +134,12 @@ val suggestedTipAmountsLimit = 1 .. 4
val inputFieldPlaceholderLimit = 1 .. 64
val emojisInStickerLimit = 1 .. 20
val keywordsInStickerLimit = 0 .. 20
val stickerKeywordLengthLimit = 0 .. 64
const val botActionActualityTime: Seconds = 5
// Made as lazy for correct work in K/JS
@@ -257,10 +263,24 @@ const val createsJoinRequestField = "creates_join_request"
const val pendingJoinRequestCountField = "pending_join_request_count"
const val memberLimitField = "member_limit"
const val iconColorField = "icon_color"
const val emojiListField = "emoji_list"
const val requestContactField = "request_contact"
const val requestLocationField = "request_location"
const val requestPollField = "request_poll"
const val requestUserField = "request_user"
const val requestChatField = "request_chat"
const val requestIdField = "request_id"
const val userIsBotField = "user_is_bot"
const val userIsPremiumField = "user_is_premium"
const val chatIsChannelField = "chat_is_channel"
const val chatIsForumField = "chat_is_forum"
const val chatHasUsernameField = "chat_has_username"
const val chatIsCreatedField = "chat_is_created"
const val userAdministratorRightsField = "user_administrator_rights"
const val botAdministratorRightsField = "bot_administrator_rights"
const val botIsMemberField = "bot_is_member"
const val fileNameField = "file_name"
const val mimeTypeField = "mime_type"
@@ -312,9 +332,17 @@ const val stickerFileIdField = "sticker_file_id"
const val gameShortNameField = "game_short_name"
const val thumbnailUrlField = "thumbnail_url"
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailUrlField", "dev.inmo.tgbotapi.types.thumbnailUrlField"))
const val thumbUrlField = "thumb_url"
const val thumbnailMimeTypeField = "thumbnail_mime_type"
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailMimeTypeField", "dev.inmo.tgbotapi.types.thumbnailMimeTypeField"))
const val thumbMimeTypeField = "thumb_mime_type"
const val thumbnailWidthField = "thumbnail_width"
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailWidthField", "dev.inmo.tgbotapi.types.thumbnailWidthField"))
const val thumbWidthField = "thumb_width"
const val thumbnailHeightField = "thumbnail_height"
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailHeightField", "dev.inmo.tgbotapi.types.thumbnailHeightField"))
const val thumbHeightField = "thumb_height"
const val inputMessageContentField = "input_message_content"
@@ -327,7 +355,12 @@ const val scopeField = "scope"
const val isMemberField = "is_member"
const val isForumField = "is_forum"
const val canSendMessagesField = "can_send_messages"
const val canSendMediaMessagesField = "can_send_media_messages"
const val canSendAudiosField = "can_send_audios"
const val canSendDocumentsField = "can_send_documents"
const val canSendPhotosField = "can_send_photos"
const val canSendVideosField = "can_send_videos"
const val canSendVideoNotesField = "can_send_video_notes"
const val canSendVoiceNotesField = "can_send_voice_notes"
const val canSendOtherMessagesField = "can_send_other_messages"
const val canSendPollsField = "can_send_polls"
const val canAddWebPagePreviewsField = "can_add_web_page_previews"
@@ -345,6 +378,7 @@ const val canPinMessagesField = "can_pin_messages"
const val canPromoteMembersField = "can_promote_members"
const val canManageVoiceChatsField = "can_manage_voice_chats"
const val canManageVideoChatsField = "can_manage_video_chats"
const val useIndependentChatPermissionsField = "use_independent_chat_permissions"
const val rightsField = "rights"
const val forChannelsField = "for_channels"
const val canManageChatField = "can_manage_chat"
@@ -361,11 +395,14 @@ const val explanationField = "explanation"
const val idField = "id"
const val pollIdField = "poll_id"
const val textField = "text"
const val thumbnailField = "thumbnail"
@Deprecated("Renamed (in telegram bot api)", ReplaceWith("thumbnailField", "dev.inmo.tgbotapi.types.thumbnailField"))
const val thumbField = "thumb"
const val emojiField = "emoji"
const val emojisField = "emojis"
const val titleField = "title"
const val descriptionField = "description"
const val shortDescriptionField = "short_description"
const val performerField = "performer"
const val durationField = "duration"
const val widthField = "width"
@@ -375,6 +412,7 @@ const val latitudeField = "latitude"
const val longitudeField = "longitude"
const val headingField = "heading"
const val fromField = "from"
const val userChatIdField = "user_chat_id"
const val userField = "user"
const val dateField = "date"
const val chatField = "chat"
@@ -388,6 +426,7 @@ const val offsetField = "offset"
const val limitField = "limit"
const val stickersField = "stickers"
const val stickerField = "sticker"
const val keywordsField = "keywords"
const val urlField = "url"
const val addressField = "address"
const val actionField = "action"

View File

@@ -23,12 +23,12 @@ class InlineQueryResultArticle(
val hideUrl: Boolean? = null,
@SerialName(descriptionField)
override val description: String? = null,
@SerialName(thumbUrlField)
override val thumbUrl: String? = null,
@SerialName(thumbWidthField)
override val thumbWidth: Int? = null,
@SerialName(thumbHeightField)
override val thumbHeight: Int? = null
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String? = null,
@SerialName(thumbnailWidthField)
override val thumbnailWidth: Int? = null,
@SerialName(thumbnailHeightField)
override val thumbnailHeight: Int? = null
) : InlineQueryResult,
ThumbSizedInlineQueryResult,
TitledInlineQueryResult,

View File

@@ -20,12 +20,12 @@ data class InlineQueryResultContact(
override val lastName: String? = null,
@SerialName(vcardField)
override val vcard: String? = null,
@SerialName(thumbUrlField)
override val thumbUrl: String? = null,
@SerialName(thumbWidthField)
override val thumbWidth: Int? = null,
@SerialName(thumbHeightField)
override val thumbHeight: Int? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String? = null,
@SerialName(thumbnailWidthField)
override val thumbnailWidth: Int? = null,
@SerialName(thumbnailHeightField)
override val thumbnailHeight: Int? = null,
@SerialName(replyMarkupField)
override val replyMarkup: InlineKeyboardMarkup? = null,
@SerialName(inputMessageContentField)

View File

@@ -21,24 +21,24 @@ fun InlineQueryResultDocumentImpl(
url: String,
title: String,
mimeType: MimeType,
thumbUrl: String? = null,
thumbWidth: Int? = null,
thumbHeight: Int? = null,
thumbnailUrl: String? = null,
thumbnailWidth: Int? = null,
thumbnailHeight: Int? = null,
description: String? = null,
text: String? = null,
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultDocumentImpl(id, url, title, mimeType, thumbUrl, thumbWidth, thumbHeight, description, text, parseMode, null, replyMarkup, inputMessageContent)
) = InlineQueryResultDocumentImpl(id, url, title, mimeType, thumbnailUrl, thumbnailWidth, thumbnailHeight, description, text, parseMode, null, replyMarkup, inputMessageContent)
fun InlineQueryResultDocumentImpl(
id: InlineQueryIdentifier,
url: String,
title: String,
mimeType: MimeType,
thumbUrl: String? = null,
thumbWidth: Int? = null,
thumbHeight: Int? = null,
thumbnailUrl: String? = null,
thumbnailWidth: Int? = null,
thumbnailHeight: Int? = null,
description: String? = null,
entities: TextSourcesList,
replyMarkup: InlineKeyboardMarkup? = null,
@@ -48,9 +48,9 @@ fun InlineQueryResultDocumentImpl(
url,
title,
mimeType,
thumbUrl,
thumbWidth,
thumbHeight,
thumbnailUrl,
thumbnailWidth,
thumbnailHeight,
description,
entities.makeString(),
null,
@@ -69,12 +69,12 @@ data class InlineQueryResultDocumentImpl internal constructor(
override val title: String,
@SerialName(mimeTypeField)
override val mimeType: MimeType,
@SerialName(thumbUrlField)
override val thumbUrl: String? = null,
@SerialName(thumbWidthField)
override val thumbWidth: Int? = null,
@SerialName(thumbHeightField)
override val thumbHeight: Int? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String? = null,
@SerialName(thumbnailWidthField)
override val thumbnailWidth: Int? = null,
@SerialName(thumbnailHeightField)
override val thumbnailHeight: Int? = null,
@SerialName(descriptionField)
override val description: String? = null,
@SerialName(captionField)

View File

@@ -20,8 +20,8 @@ import kotlinx.serialization.Serializable
fun InlineQueryResultGifImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -30,13 +30,13 @@ fun InlineQueryResultGifImpl(
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultGifImpl(id, url, thumbUrl, thumbMimeType, width, height, duration, title, text, parseMode, null, replyMarkup, inputMessageContent)
) = InlineQueryResultGifImpl(id, url, thumbnailUrl, thumbnailMimeType, width, height, duration, title, text, parseMode, null, replyMarkup, inputMessageContent)
fun InlineQueryResultGifImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -47,8 +47,8 @@ fun InlineQueryResultGifImpl(
) = InlineQueryResultGifImpl(
id,
url,
thumbUrl,
thumbMimeType,
thumbnailUrl,
thumbnailMimeType,
width,
height,
duration,
@@ -63,8 +63,8 @@ fun InlineQueryResultGifImpl(
fun InlineQueryResultGifImpl(
id: InlineQueryIdentifier,
gifFile: FileId,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -73,13 +73,13 @@ fun InlineQueryResultGifImpl(
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultGifImpl(id, gifFile.fileId, thumbUrl, thumbMimeType, width, height, duration, title, text, parseMode, replyMarkup, inputMessageContent)
) = InlineQueryResultGifImpl(id, gifFile.fileId, thumbnailUrl, thumbnailMimeType, width, height, duration, title, text, parseMode, replyMarkup, inputMessageContent)
fun InlineQueryResultGifImpl(
id: InlineQueryIdentifier,
gifFile: FileId,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -88,7 +88,7 @@ fun InlineQueryResultGifImpl(
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultGifImpl(
id, gifFile.fileId, thumbUrl, thumbMimeType, width, height, duration, title, entities, replyMarkup, inputMessageContent
id, gifFile.fileId, thumbnailUrl, thumbnailMimeType, width, height, duration, title, entities, replyMarkup, inputMessageContent
)
@Serializable
@@ -97,10 +97,10 @@ data class InlineQueryResultGifImpl internal constructor(
override val id: InlineQueryIdentifier,
@SerialName(gifUrlField)
override val url: String,
@SerialName(thumbUrlField)
override val thumbUrl: String,
@SerialName(thumbMimeTypeField)
override val thumbMimeType: MimeType? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String,
@SerialName(thumbnailMimeTypeField)
override val thumbnailMimeType: MimeType? = null,
@SerialName(gifWidthField)
override val width: Int? = null,
@SerialName(gifHeightField)
@@ -126,8 +126,8 @@ data class InlineQueryResultGifImpl internal constructor(
}
init {
if (thumbMimeType != null && thumbMimeType !in telegramInlineModeGifPermittedMimeTypes) {
error("Passed thumb mime type is not permitted in Telegram Bot API. Passed $thumbMimeType, but permitted $telegramInlineModeGifPermittedMimeTypes")
if (thumbnailMimeType != null && thumbnailMimeType !in telegramInlineModeGifPermittedMimeTypes) {
error("Passed thumb mime type is not permitted in Telegram Bot API. Passed $thumbnailMimeType, but permitted $telegramInlineModeGifPermittedMimeTypes")
}
}
}

View File

@@ -26,12 +26,12 @@ data class InlineQueryResultLocation(
override val heading: Degrees? = null,
@SerialName(proximityAlertRadiusField)
override val proximityAlertRadius: Meters? = null,
@SerialName(thumbUrlField)
override val thumbUrl: String? = null,
@SerialName(thumbWidthField)
override val thumbWidth: Int? = null,
@SerialName(thumbHeightField)
override val thumbHeight: Int? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String? = null,
@SerialName(thumbnailWidthField)
override val thumbnailWidth: Int? = null,
@SerialName(thumbnailHeightField)
override val thumbnailHeight: Int? = null,
@SerialName(replyMarkupField)
override val replyMarkup: InlineKeyboardMarkup? = null,
@SerialName(inputMessageContentField)

View File

@@ -19,8 +19,8 @@ import kotlinx.serialization.Serializable
fun InlineQueryResultMpeg4GifImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -29,13 +29,13 @@ fun InlineQueryResultMpeg4GifImpl(
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultMpeg4GifImpl(id, url, thumbUrl, thumbMimeType, width, height, duration, title, text, parseMode, null, replyMarkup, inputMessageContent)
) = InlineQueryResultMpeg4GifImpl(id, url, thumbnailUrl, thumbnailMimeType, width, height, duration, title, text, parseMode, null, replyMarkup, inputMessageContent)
fun InlineQueryResultMpeg4GifImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbMimeType: MimeType? = null,
thumbnailUrl: String,
thumbnailMimeType: MimeType? = null,
width: Int? = null,
height: Int? = null,
duration: Int? = null,
@@ -46,8 +46,8 @@ fun InlineQueryResultMpeg4GifImpl(
) = InlineQueryResultMpeg4GifImpl(
id,
url,
thumbUrl,
thumbMimeType,
thumbnailUrl,
thumbnailMimeType,
width,
height,
duration,
@@ -65,10 +65,10 @@ data class InlineQueryResultMpeg4GifImpl internal constructor(
override val id: InlineQueryIdentifier,
@SerialName(mpeg4GifUrlField)
override val url: String,
@SerialName(thumbUrlField)
override val thumbUrl: String,
@SerialName(thumbMimeTypeField)
override val thumbMimeType: MimeType? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String,
@SerialName(thumbnailMimeTypeField)
override val thumbnailMimeType: MimeType? = null,
@SerialName(mpeg4GifWidthField)
override val width: Int? = null,
@SerialName(mpeg4GifHeightField)
@@ -94,8 +94,8 @@ data class InlineQueryResultMpeg4GifImpl internal constructor(
}
init {
if (thumbMimeType != null && thumbMimeType !in telegramInlineModeGifPermittedMimeTypes) {
error("Passed thumb mime type is not permitted in Telegram Bot API. Passed $thumbMimeType, but permitted $telegramInlineModeGifPermittedMimeTypes")
if (thumbnailMimeType != null && thumbnailMimeType !in telegramInlineModeGifPermittedMimeTypes) {
error("Passed thumb mime type is not permitted in Telegram Bot API. Passed $thumbnailMimeType, but permitted $telegramInlineModeGifPermittedMimeTypes")
}
}
}

View File

@@ -18,7 +18,7 @@ import kotlinx.serialization.Serializable
fun InlineQueryResultPhotoImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbnailUrl: String,
width: Int? = null,
height: Int? = null,
title: String? = null,
@@ -27,12 +27,12 @@ fun InlineQueryResultPhotoImpl(
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultPhotoImpl(id, url, thumbUrl, width, height, title, description, text, parseMode, null, replyMarkup, inputMessageContent)
) = InlineQueryResultPhotoImpl(id, url, thumbnailUrl, width, height, title, description, text, parseMode, null, replyMarkup, inputMessageContent)
fun InlineQueryResultPhotoImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbnailUrl: String,
width: Int? = null,
height: Int? = null,
title: String? = null,
@@ -43,7 +43,7 @@ fun InlineQueryResultPhotoImpl(
) = InlineQueryResultPhotoImpl(
id,
url,
thumbUrl,
thumbnailUrl,
width,
height,
title,
@@ -61,8 +61,8 @@ data class InlineQueryResultPhotoImpl internal constructor(
override val id: InlineQueryIdentifier,
@SerialName(photoUrlField)
override val url: String,
@SerialName(thumbUrlField)
override val thumbUrl: String,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String,
@SerialName(photoWidthField)
override val width: Int? = null,
@SerialName(photoHeightField)

View File

@@ -29,12 +29,12 @@ data class InlineQueryResultVenue(
override val googlePlaceId: GooglePlaceId? = null,
@SerialName(googlePlaceTypeField)
override val googlePlaceType: GooglePlaceType? = null,
@SerialName(thumbUrlField)
override val thumbUrl: String? = null,
@SerialName(thumbWidthField)
override val thumbWidth: Int? = null,
@SerialName(thumbHeightField)
override val thumbHeight: Int? = null,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String? = null,
@SerialName(thumbnailWidthField)
override val thumbnailWidth: Int? = null,
@SerialName(thumbnailHeightField)
override val thumbnailHeight: Int? = null,
@SerialName(replyMarkupField)
override val replyMarkup: InlineKeyboardMarkup? = null,
@SerialName(inputMessageContentField)

View File

@@ -19,7 +19,7 @@ import kotlinx.serialization.Serializable
fun InlineQueryResultVideoImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbnailUrl: String,
mimeType: MimeType,
title: String,
width: Int? = null,
@@ -30,12 +30,12 @@ fun InlineQueryResultVideoImpl(
parseMode: ParseMode? = null,
replyMarkup: InlineKeyboardMarkup? = null,
inputMessageContent: InputMessageContent? = null
) = InlineQueryResultVideoImpl(id, url, thumbUrl, mimeType, title, width, height, duration, description, text, parseMode, null, replyMarkup, inputMessageContent)
) = InlineQueryResultVideoImpl(id, url, thumbnailUrl, mimeType, title, width, height, duration, description, text, parseMode, null, replyMarkup, inputMessageContent)
fun InlineQueryResultVideoImpl(
id: InlineQueryIdentifier,
url: String,
thumbUrl: String,
thumbnailUrl: String,
mimeType: MimeType,
title: String,
width: Int? = null,
@@ -48,7 +48,7 @@ fun InlineQueryResultVideoImpl(
) = InlineQueryResultVideoImpl(
id,
url,
thumbUrl,
thumbnailUrl,
mimeType,
title,
width,
@@ -68,8 +68,8 @@ data class InlineQueryResultVideoImpl internal constructor(
override val id: InlineQueryIdentifier,
@SerialName(videoUrlField)
override val url: String,
@SerialName(thumbUrlField)
override val thumbUrl: String,
@SerialName(thumbnailUrlField)
override val thumbnailUrl: String,
@SerialName(mimeTypeField)
override val mimeType: MimeType,
@SerialName(titleField)

View File

@@ -1,6 +1,12 @@
package dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.abstracts
interface ThumbSizedInlineQueryResult : InlineQueryResult, ThumbedInlineQueryResult {
val thumbnailWidth: Int?
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailWidth"))
val thumbWidth: Int?
get() = thumbnailWidth
val thumbnailHeight: Int?
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailHeight"))
val thumbHeight: Int?
}
get() = thumbnailHeight
}

View File

@@ -3,9 +3,15 @@ package dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.abstracts
import dev.inmo.tgbotapi.utils.MimeType
interface ThumbedInlineQueryResult : InlineQueryResult {
val thumbnailUrl: String?
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailUrl"))
val thumbUrl: String?
get() = thumbnailUrl
}
interface ThumbedWithMimeTypeInlineQueryResult : ThumbedInlineQueryResult {
val thumbnailMimeType: MimeType?
@Deprecated("Renamed in telegram bot api", ReplaceWith("thumbnailMimeType"))
val thumbMimeType: MimeType?
}
get() = thumbnailMimeType
}

View File

@@ -3,10 +3,11 @@ package dev.inmo.tgbotapi.types.InlineQueries.query
import dev.inmo.tgbotapi.types.InlineQueryIdentifier
import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.chat.ChatType
import dev.inmo.tgbotapi.types.chat.CommonUser
data class BaseInlineQuery(
override val id: InlineQueryIdentifier,
override val from: User,
override val from: CommonUser,
override val query: String,
override val offset: String,
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.types.InlineQueryIdentifier
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 {
val id: InlineQueryIdentifier
val query: String
val offset: String
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.chat.User
import dev.inmo.tgbotapi.types.chat.ChatType
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.location.Location
data class LocationInlineQuery(
override val id: InlineQueryIdentifier,
override val from: User,
override val from: CommonUser,
override val query: String,
override val offset: String,
override val chatType: ChatType?,

View File

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

View File

@@ -94,6 +94,42 @@ data class RequestPollKeyboardButton(
val requestPoll: KeyboardButtonPollType
) : KeyboardButton
/**
* Private chats only. When user will tap on this button, he will be asked for the chat with [requestChat] options. You
* will be able to catch this [ChatId] in updates and data using
* [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared] in case you are using Behaviour
* Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow]
* and [kotlinx.coroutines.flow.filterIsInstance].
*
* In case you will use [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared] it is
* recommended to use [kotlinx.coroutines.flow.Flow] [kotlinx.coroutines.flow.filter] with checking of incoming
* [dev.inmo.tgbotapi.types.request.UserShared.requestId]
*/
@Serializable
data class RequestUserKeyboardButton(
override val text: String,
@SerialName(requestUserField)
val requestUser: KeyboardButtonRequestUser
) : KeyboardButton
/**
* Private chats only. When user will tap on this button, he will be asked for the chat with [requestChat] options. You
* will be able to catch this [ChatId] in updates and data using
* [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared] in case you are using Behaviour
* Builder OR with [dev.inmo.tgbotapi.updateshandlers.FlowsUpdatesFilter.messagesFlow]
* and [kotlinx.coroutines.flow.filterIsInstance].
*
* In case you will use [dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared] it is
* recommended to use [kotlinx.coroutines.flow.Flow] [kotlinx.coroutines.flow.filter] with checking of incoming
* [dev.inmo.tgbotapi.types.request.ChatShared.requestId]
*/
@Serializable
data class RequestChatKeyboardButton(
override val text: String,
@SerialName(requestChatField)
val requestChat: KeyboardButtonRequestChat
) : KeyboardButton
@RiskFeature
object KeyboardButtonSerializer : KSerializer<KeyboardButton> {
private val internalSerializer = JsonElement.serializer()
@@ -124,6 +160,20 @@ object KeyboardButtonSerializer : KSerializer<KeyboardButton> {
asJson[requestPollField] ?.jsonObject ?: buildJsonObject { }
)
)
asJson is JsonObject && asJson[requestUserField] != null -> RequestUserKeyboardButton(
asJson[textField]!!.jsonPrimitive.content,
nonstrictJsonFormat.decodeFromJsonElement(
KeyboardButtonRequestUser.serializer(),
asJson[requestUserField] ?.jsonObject ?: buildJsonObject { }
)
)
asJson is JsonObject && asJson[requestChatField] != null -> RequestChatKeyboardButton(
asJson[textField]!!.jsonPrimitive.content,
nonstrictJsonFormat.decodeFromJsonElement(
KeyboardButtonRequestChat.serializer(),
asJson[requestChatField] ?.jsonObject ?: buildJsonObject { }
)
)
else -> UnknownKeyboardButton(
when (asJson) {
is JsonObject -> asJson[textField]!!.jsonPrimitive.content
@@ -142,6 +192,8 @@ object KeyboardButtonSerializer : KSerializer<KeyboardButton> {
is WebAppKeyboardButton -> WebAppKeyboardButton.serializer().serialize(encoder, value)
is RequestPollKeyboardButton -> RequestPollKeyboardButton.serializer().serialize(encoder, value)
is SimpleKeyboardButton -> encoder.encodeString(value.text)
is RequestUserKeyboardButton -> RequestUserKeyboardButton.serializer().serialize(encoder, value)
is RequestChatKeyboardButton -> RequestChatKeyboardButton.serializer().serialize(encoder, value)
is UnknownKeyboardButton -> JsonElement.serializer().serialize(encoder, nonstrictJsonFormat.parseToJsonElement(value.raw))
}
}

View File

@@ -0,0 +1,78 @@
package dev.inmo.tgbotapi.types.buttons
import dev.inmo.tgbotapi.types.botAdministratorRightsField
import dev.inmo.tgbotapi.types.botIsMemberField
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights
import dev.inmo.tgbotapi.types.chatHasUsernameField
import dev.inmo.tgbotapi.types.chatIsChannelField
import dev.inmo.tgbotapi.types.chatIsCreatedField
import dev.inmo.tgbotapi.types.chatIsForumField
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.types.requestIdField
import dev.inmo.tgbotapi.types.userAdministratorRightsField
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* @see Channel
* @see Group
*/
@Serializable
data class KeyboardButtonRequestChat(
@SerialName(requestIdField)
val requestId: RequestId,
@SerialName(chatIsChannelField)
val isChannel: Boolean? = null,
@SerialName(chatIsForumField)
val isForum: Boolean? = null,
@SerialName(chatHasUsernameField)
val isPublic: Boolean? = null,
@SerialName(chatIsCreatedField)
val isOwnedBy: Boolean? = null,
@SerialName(userAdministratorRightsField)
val userRightsInChat: ChatAdministratorRights? = null,
@SerialName(botAdministratorRightsField)
val botRightsInChat: ChatAdministratorRights? = null,
@SerialName(botIsMemberField)
val botIsMember: Boolean? = null
) {
companion object {
fun Channel(
requestId: RequestId,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
botIsMember: Boolean? = null
) = KeyboardButtonRequestChat(
requestId = requestId,
isChannel = true,
isForum = null,
isPublic = isPublic,
isOwnedBy = isOwnedBy,
userRightsInChat = userRightsInChat,
botRightsInChat = botRightsInChat,
botIsMember = botIsMember
)
fun Group(
requestId: RequestId,
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
botIsMember: Boolean? = null
) = KeyboardButtonRequestChat(
requestId = requestId,
isChannel = false,
isForum = isForum,
isPublic = isPublic,
isOwnedBy = isOwnedBy,
userRightsInChat = userRightsInChat,
botRightsInChat = botRightsInChat,
botIsMember = botIsMember
)
}
}

View File

@@ -0,0 +1,91 @@
package dev.inmo.tgbotapi.types.buttons
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.types.requestIdField
import dev.inmo.tgbotapi.types.userIsBotField
import dev.inmo.tgbotapi.types.userIsPremiumField
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(KeyboardButtonRequestUser.Companion::class)
@ClassCastsIncluded
sealed interface KeyboardButtonRequestUser {
val requestId: RequestId
val isBot: Boolean?
@Serializable
data class Any(
@SerialName(requestIdField)
override val requestId: RequestId
) : KeyboardButtonRequestUser {
@SerialName(userIsBotField)
@EncodeDefault
override val isBot: Boolean? = null
}
@Serializable
data class Common(
@SerialName(requestIdField)
override val requestId: RequestId,
@SerialName(userIsPremiumField)
val isPremium: Boolean? = null
) : KeyboardButtonRequestUser {
@SerialName(userIsBotField)
@EncodeDefault
override val isBot: Boolean = false
}
@Serializable
data class Bot(
@SerialName(requestIdField)
override val requestId: RequestId
) : KeyboardButtonRequestUser {
@SerialName(userIsBotField)
@EncodeDefault
override val isBot: Boolean = true
}
@Serializer(KeyboardButtonRequestUser::class)
companion object : KSerializer<KeyboardButtonRequestUser> {
@Serializable
private data class Surrogate(
@SerialName(requestIdField)
val requestId: RequestId,
@SerialName(userIsBotField)
val userIsBot: Boolean? = null,
@SerialName(userIsPremiumField)
val userIsPremium: Boolean? = null
)
private val realSerializer = Surrogate.serializer()
override val descriptor: SerialDescriptor = realSerializer.descriptor
override fun deserialize(decoder: Decoder): KeyboardButtonRequestUser {
val surrogate = realSerializer.deserialize(decoder)
return when (surrogate.userIsBot) {
true -> Bot(surrogate.requestId)
false -> Common(surrogate.requestId, surrogate.userIsPremium)
null -> Any(surrogate.requestId)
}
}
override fun serialize(encoder: Encoder, value: KeyboardButtonRequestUser) {
realSerializer.serialize(
encoder,
Surrogate(
value.requestId,
value.isBot,
(value as? Common) ?.isPremium
)
)
}
}
}

View File

@@ -2,6 +2,8 @@ package dev.inmo.tgbotapi.types.buttons.reply
import dev.inmo.tgbotapi.types.buttons.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.*
import dev.inmo.tgbotapi.types.chat.member.ChatAdministratorRights
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
@@ -67,3 +69,161 @@ inline fun webAppReplyButton(
text: String,
url: String
) = webAppReplyButton(text, WebAppInfo(url))
/**
* Creates and put [RequestUserKeyboardButton]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestUserReplyButton(
text: String,
requestUser: KeyboardButtonRequestUser
) = RequestUserKeyboardButton(
text,
requestUser
)
/**
* Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Bot]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestBotReplyButton(
text: String,
requestId: RequestId
) = requestUserReplyButton(
text,
KeyboardButtonRequestUser.Bot(requestId)
)
/**
* Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Common]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestUserReplyButton(
text: String,
requestId: RequestId,
premiumUser: Boolean? = null
) = requestUserReplyButton(
text,
KeyboardButtonRequestUser.Common(requestId, premiumUser)
)
/**
* Creates and put [RequestUserKeyboardButton] with [KeyboardButtonRequestUser.Any]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestUserOrBotReplyButton(
text: String,
requestId: RequestId
) = requestUserReplyButton(
text,
KeyboardButtonRequestUser.Any(requestId)
)
/**
* Creates and put [RequestChatKeyboardButton]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestChatReplyButton(
text: String,
requestChat: KeyboardButtonRequestChat
) = RequestChatKeyboardButton(
text,
requestChat
)
/**
* Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestChatReplyButton(
text: String,
requestId: RequestId,
isChannel: Boolean? = null,
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
botIsMember: Boolean = false
) = requestChatReplyButton(
text,
KeyboardButtonRequestChat(
requestId = requestId,
isChannel = isChannel,
isForum = isForum,
isPublic = isPublic,
isOwnedBy = isOwnedBy,
userRightsInChat = userRightsInChat,
botRightsInChat = botRightsInChat,
botIsMember = botIsMember
)
)
/**
* Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Channel]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestChannelReplyButton(
text: String,
requestId: RequestId,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
botIsMember: Boolean = false
) = requestChatReplyButton(
text,
KeyboardButtonRequestChat.Channel(
requestId = requestId,
isPublic = isPublic,
isOwnedBy = isOwnedBy,
userRightsInChat = userRightsInChat,
botRightsInChat = botRightsInChat,
botIsMember = botIsMember
)
)
/**
* Creates and put [RequestChatKeyboardButton] with [KeyboardButtonRequestChat.Group]
*
* @see replyKeyboard
* @see ReplyKeyboardBuilder.row
*/
inline fun requestChannelReplyButton(
text: String,
requestId: RequestId,
isForum: Boolean? = null,
isPublic: Boolean? = null,
isOwnedBy: Boolean? = null,
userRightsInChat: ChatAdministratorRights? = null,
botRightsInChat: ChatAdministratorRights? = null,
botIsMember: Boolean? = null
) = requestChatReplyButton(
text,
KeyboardButtonRequestChat.Group(
requestId = requestId,
isForum = isForum,
isPublic = isPublic,
isOwnedBy = isOwnedBy,
userRightsInChat = userRightsInChat,
botRightsInChat = botRightsInChat,
botIsMember = botIsMember
)
)

View File

@@ -15,6 +15,8 @@ data class ChatJoinRequest(
val chat: PublicChat,
@SerialName(fromField)
override val from: User,
@SerialName(userChatIdField)
val userChatId: UserId,
@SerialName(dateField)
val date: TelegramDate,
@SerialName(inviteLinkField)

View File

@@ -1,32 +1,259 @@
package dev.inmo.tgbotapi.types.chat
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable
data class ChatPermissions(
@SerialName(canSendMessagesField)
val canSendMessages: Boolean = false,
@SerialName(canSendMediaMessagesField)
val canSendMediaMessages: Boolean = false,
@SerialName(canSendPollsField)
val canSendPolls: Boolean = false,
@SerialName(canSendOtherMessagesField)
val canSendOtherMessages: Boolean = false,
@SerialName(canAddWebPagePreviewsField)
val canAddWebPagePreviews: Boolean = false,
@SerialName(canChangeInfoField)
val canChangeInfo: Boolean = false,
@SerialName(canInviteUsersField)
val canInviteUsers: Boolean = false,
@SerialName(canPinMessagesField)
val canPinMessages: Boolean = false
)
/**
* Represents any type with common permissions list
*
* !!WARNING!! Default serializer of this interface is using [Granular] as surrogate and in fact serialized
* and deserialized as [Granular]. In case you wish some custom behaviour you must implement your own
* [KSerializer] or pass another serializer
*/
@Serializable(ChatPermissions.Companion::class)
interface ChatPermissions {
val canSendMessages: Boolean?
val canSendAudios: Boolean?
val canSendDocuments: Boolean?
val canSendPhotos: Boolean?
val canSendVideos: Boolean?
val canSendVideoNotes: Boolean?
val canSendVoiceNotes: Boolean?
val canSendPolls: Boolean?
val canSendOtherMessages: Boolean?
val canAddWebPagePreviews: Boolean?
val canChangeInfo: Boolean?
val canInviteUsers: Boolean?
val canPinMessages: Boolean?
@Transient
val isGranular
get() = canSendAudios != null ||
canSendDocuments != null ||
canSendVideoNotes != null ||
canSendPhotos != null ||
canSendVideos != null ||
canSendVoiceNotes != null
val canSendStickers: Boolean?
get() = canSendOtherMessages
val canSendGifs: Boolean?
get() = canSendStickers
@Serializable
data class Granular(
@SerialName(canSendMessagesField)
override val canSendMessages: Boolean? = null,
@SerialName(canSendAudiosField)
override val canSendAudios: Boolean? = null,
@SerialName(canSendDocumentsField)
override val canSendDocuments: Boolean? = null,
@SerialName(canSendPhotosField)
override val canSendPhotos: Boolean? = null,
@SerialName(canSendVideosField)
override val canSendVideos: Boolean? = null,
@SerialName(canSendVideoNotesField)
override val canSendVideoNotes: Boolean? = null,
@SerialName(canSendVoiceNotesField)
override val canSendVoiceNotes: Boolean? = null,
@SerialName(canSendPollsField)
override val canSendPolls: Boolean? = null,
@SerialName(canSendOtherMessagesField)
override val canSendOtherMessages: Boolean? = null,
@SerialName(canAddWebPagePreviewsField)
override val canAddWebPagePreviews: Boolean? = null,
@SerialName(canChangeInfoField)
override val canChangeInfo: Boolean? = null,
@SerialName(canInviteUsersField)
override val canInviteUsers: Boolean? = null,
@SerialName(canPinMessagesField)
override val canPinMessages: Boolean? = null
) : ChatPermissions {
@Transient
override val isGranular: Boolean
get() = true
}
@Serializable
data class Common(
@SerialName(canSendPollsField)
override val canSendPolls: Boolean? = null,
@SerialName(canSendOtherMessagesField)
override val canSendOtherMessages: Boolean? = null,
@SerialName(canAddWebPagePreviewsField)
override val canAddWebPagePreviews: Boolean? = null,
@SerialName(canChangeInfoField)
override val canChangeInfo: Boolean? = null,
@SerialName(canInviteUsersField)
override val canInviteUsers: Boolean? = null,
@SerialName(canPinMessagesField)
override val canPinMessages: Boolean? = null
) : ChatPermissions {
@Transient
override val isGranular: Boolean
get() = false
@Transient
override val canSendMessages: Boolean? = canSendOtherMessages ?.let {
it && (canAddWebPagePreviews ?: return@let null)
}
@Transient
override val canSendAudios: Boolean?
get() = canSendMessages
@Transient
override val canSendDocuments: Boolean?
get() = canSendMessages
@Transient
override val canSendPhotos: Boolean?
get() = canSendMessages
@Transient
override val canSendVideos: Boolean?
get() = canSendMessages
@Transient
override val canSendVideoNotes: Boolean?
get() = canSendMessages
@Transient
override val canSendVoiceNotes: Boolean?
get() = canSendMessages
}
companion object : KSerializer<ChatPermissions> {
operator fun invoke(
canSendMessages: Boolean? = null,
canSendAudios: Boolean? = null,
canSendDocuments: Boolean? = null,
canSendPhotos: Boolean? = null,
canSendVideos: Boolean? = null,
canSendVideoNotes: Boolean? = null,
canSendVoiceNotes: Boolean? = null,
canSendPolls: Boolean? = null,
canSendOtherMessages: Boolean? = null,
canAddWebPagePreviews: Boolean? = null,
canChangeInfo: Boolean? = null,
canInviteUsers: Boolean? = null,
canPinMessages: Boolean? = null
) = Granular(
canSendMessages = canSendMessages,
canSendAudios = canSendAudios,
canSendDocuments = canSendDocuments,
canSendPhotos = canSendPhotos,
canSendVideos = canSendVideos,
canSendVideoNotes = canSendVideoNotes,
canSendVoiceNotes = canSendVoiceNotes,
canSendPolls = canSendPolls,
canSendOtherMessages = canSendOtherMessages,
canAddWebPagePreviews = canAddWebPagePreviews,
canChangeInfo = canChangeInfo,
canInviteUsers = canInviteUsers,
canPinMessages = canPinMessages
)
private val realSerializer = Granular.serializer()
private val commonSerializer = Common.serializer()
override val descriptor: SerialDescriptor
get() = realSerializer.descriptor
override fun deserialize(decoder: Decoder): ChatPermissions {
return realSerializer.deserialize(decoder)
}
override fun serialize(encoder: Encoder, value: ChatPermissions) {
realSerializer.serialize(
encoder,
(value as? Granular) ?: value.run {
Granular(
canSendMessages = canSendMessages,
canSendAudios = canSendAudios,
canSendDocuments = canSendDocuments,
canSendPhotos = canSendPhotos,
canSendVideos = canSendVideos,
canSendVideoNotes = canSendVideoNotes,
canSendVoiceNotes = canSendVoiceNotes,
canSendPolls = canSendPolls,
canSendOtherMessages = canSendOtherMessages,
canAddWebPagePreviews = canAddWebPagePreviews,
canChangeInfo = canChangeInfo,
canInviteUsers = canInviteUsers,
canPinMessages = canPinMessages
)
}
)
}
}
/**
* Copying current instance as [ChatPermissions], but realizations of this interface may differently override this
* method
*/
fun copyGranular(
canSendMessages: Boolean? = this.canSendMessages,
canSendAudios: Boolean? = this.canSendAudios,
canSendDocuments: Boolean? = this.canSendDocuments,
canSendPhotos: Boolean? = this.canSendPhotos,
canSendVideos: Boolean? = this.canSendVideos,
canSendVideoNotes: Boolean? = this.canSendVideoNotes,
canSendVoiceNotes: Boolean? = this.canSendVoiceNotes,
canSendPolls: Boolean? = this.canSendPolls,
canSendOtherMessages: Boolean? = this.canSendOtherMessages,
canAddWebPagePreviews: Boolean? = this.canAddWebPagePreviews,
canChangeInfo: Boolean? = this.canChangeInfo,
canInviteUsers: Boolean? = this.canInviteUsers,
canPinMessages: Boolean? = this.canPinMessages
): ChatPermissions = ChatPermissions(
canSendMessages = canSendMessages,
canSendAudios = canSendAudios,
canSendDocuments = canSendDocuments,
canSendPhotos = canSendPhotos,
canSendVideos = canSendVideos,
canSendVideoNotes = canSendVideoNotes,
canSendVoiceNotes = canSendVoiceNotes,
canSendPolls = canSendPolls,
canSendOtherMessages = canSendOtherMessages,
canAddWebPagePreviews = canAddWebPagePreviews,
canChangeInfo = canChangeInfo,
canInviteUsers = canInviteUsers,
canPinMessages = canPinMessages
)
/**
* Copying current instance as [ChatPermissions], but realizations of this interface may differently override this
* method
*/
fun copyCommon(
canSendPolls: Boolean? = this.canSendPolls,
canSendOtherMessages: Boolean? = this.canSendOtherMessages,
canAddWebPagePreviews: Boolean? = this.canAddWebPagePreviews,
canChangeInfo: Boolean? = this.canChangeInfo,
canInviteUsers: Boolean? = this.canInviteUsers,
canPinMessages: Boolean? = this.canPinMessages
): ChatPermissions = ChatPermissions(
canSendMessages = null,
canSendAudios = null,
canSendDocuments = null,
canSendPhotos = null,
canSendVideos = null,
canSendVideoNotes = null,
canSendVoiceNotes = null,
canSendPolls = canSendPolls,
canSendOtherMessages = canSendOtherMessages,
canAddWebPagePreviews = canAddWebPagePreviews,
canChangeInfo = canChangeInfo,
canInviteUsers = canInviteUsers,
canPinMessages = canPinMessages
)
}
val LeftRestrictionsChatPermissions = ChatPermissions(
canSendMessages = true,
canSendMediaMessages = true,
canSendAudios = true,
canSendDocuments = true,
canSendPhotos = true,
canSendVideos = true,
canSendVideoNotes = true,
canSendVoiceNotes = true,
canSendPolls = true,
canSendOtherMessages = true,
canAddWebPagePreviews = true,
@@ -37,7 +264,12 @@ val LeftRestrictionsChatPermissions = ChatPermissions(
val RestrictionsChatPermissions = ChatPermissions(
canSendMessages = false,
canSendMediaMessages = false,
canSendAudios = false,
canSendDocuments = false,
canSendPhotos = false,
canSendVideos = false,
canSendVideoNotes = false,
canSendVoiceNotes = false,
canSendPolls = false,
canSendOtherMessages = false,
canAddWebPagePreviews = false,

View File

@@ -1,6 +1,7 @@
package dev.inmo.tgbotapi.types.chat.member
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.ChatPermissions
import dev.inmo.tgbotapi.types.chat.User
import kotlinx.serialization.*
@@ -13,15 +14,25 @@ data class RestrictedChatMember(
@SerialName(isMemberField)
val isMember: Boolean = false,
@SerialName(canSendMessagesField)
val canSendMessages: Boolean = false,
@SerialName(canSendMediaMessagesField)
val canSendMediaMessages: Boolean = false,
override val canSendMessages: Boolean = false,
@SerialName(canSendAudiosField)
override val canSendAudios: Boolean = false,
@SerialName(canSendDocumentsField)
override val canSendDocuments: Boolean = false,
@SerialName(canSendPhotosField)
override val canSendPhotos: Boolean = false,
@SerialName(canSendVideosField)
override val canSendVideos: Boolean = false,
@SerialName(canSendVideoNotesField)
override val canSendVideoNotes: Boolean = false,
@SerialName(canSendVoiceNotesField)
override val canSendVoiceNotes: Boolean = false,
@SerialName(canSendPollsField)
val canSendPolls: Boolean = false,
override val canSendPolls: Boolean = false,
@SerialName(canSendOtherMessagesField)
val canSendOtherMessages: Boolean = false,
override val canSendOtherMessages: Boolean = false,
@SerialName(canAddWebPagePreviewsField)
val canAddWebpagePreviews: Boolean = false,
override val canAddWebPagePreviews: Boolean = false,
@SerialName(canChangeInfoField)
override val canChangeInfo: Boolean = false,
@SerialName(canInviteUsersField)
@@ -30,7 +41,7 @@ data class RestrictedChatMember(
override val canPinMessages: Boolean = false,
@SerialName(canManageTopicsField)
override val canManageTopics: Boolean = false
) : BannedChatMember, SpecialRightsChatMember {
) : BannedChatMember, SpecialRightsChatMember, ChatPermissions {
@SerialName(statusField)
@Required
private val type: String = "restricted"

Some files were not shown because too many files have changed in this diff Show More