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

Compare commits

..

113 Commits

Author SHA1 Message Date
ffbe8fc5e0 Update libs.versions.toml 2023-05-31 01:20:07 +06:00
610ed95296 Update CHANGELOG.md 2023-05-30 14:15:10 +06:00
d3f4a895ff Update libs.versions.toml 2023-05-30 14:14:39 +06:00
51c7c376d7 all previous deprecations have been removed 2023-05-28 20:39:46 +06:00
393197eca1 fixes 2023-05-27 18:33:28 +06:00
67096f8e0e update dependencies 2023-05-27 18:19:14 +06:00
c312a05d6b start 8.0.0 2023-05-27 18:10:52 +06:00
b575695f90 Merge pull request #753 from InsanusMokrassar/7.1.3
7.1.3
2023-05-19 22:50:22 +06:00
65bcf83517 update ClassCasts 2023-05-19 22:42:42 +06:00
f07a179448 InputFile kdocs improvements 2023-05-18 13:06:41 +06:00
ed2c447730 fix of #645 2023-05-18 12:59:35 +06:00
cb4c48d025 waitMediaContent/waitMediaContentMessage/onMediaContent 2023-05-18 11:45:27 +06:00
1a21fa85ac fixes 2023-05-18 11:39:47 +06:00
191fa5406d start 7.1.3 2023-05-11 20:45:24 +06:00
6959dacfc4 Merge pull request #752 from InsanusMokrassar/7.1.2
7.1.2
2023-05-06 13:28:42 +06:00
ced11ab336 downgrade microutils and revert coroutines 2023-05-06 13:25:27 +06:00
6686aef4fa update dependencies in preview mode 2023-05-06 12:25:24 +06:00
9ede545e56 add serialization of Stickers 2023-05-06 12:00:08 +06:00
a74066cf62 start 7.1.2 2023-05-06 11:38:05 +06:00
0a7e99bbb3 Merge pull request #750 from InsanusMokrassar/7.1.1
7.1.1
2023-05-02 01:48:06 +06:00
913f584469 update kotlin poet 2023-05-01 02:15:27 +06:00
20278804bf potential fix of exception printing on skipping of timeout exceptions 2023-05-01 01:52:24 +06:00
631f1b9427 start 7.1.1, update dependencies and add fixes in makeLinkToMessage 2023-04-30 21:00:27 +06:00
5e0106beb5 Update README.md 2023-04-29 11:19:06 +06:00
d37e01f93e Merge pull request #748 from InsanusMokrassar/renovate/ksp
Update ksp to v1.8.21-1.0.11
2023-04-28 00:05:28 +06:00
renovate[bot]
d4492ec7ac Update ksp to v1.8.21-1.0.11 2023-04-27 17:42:32 +00:00
60d24259f4 Merge pull request #747 from InsanusMokrassar/7.1.0
7.1.0
2023-04-22 11:08:29 +06:00
8e6b3b7260 remove redundant class casts 2023-04-22 10:51:37 +06:00
e2cbd5ee0b update deprecation in old updates variables 2023-04-22 10:41:26 +06:00
1e87677ea7 Update ChatMemberUpdated.kt 2023-04-22 01:26:16 +06:00
cb3494cb39 Remove redundant chats folder invite link 2023-04-22 01:24:55 +06:00
ce81be9dcb Update UpdateTypes.kt 2023-04-22 01:10:33 +06:00
2cc02c8c85 Update UpdateTypes.kt 2023-04-22 01:05:34 +06:00
f5b2ee79d6 add info about supported tgbotapi version 2023-04-22 00:28:54 +06:00
0ba6ebb20f add editions for inline messages 2023-04-22 00:18:18 +06:00
3d2df4e255 fixes in SwitchInlineQueryChosenChat 2023-04-21 23:53:53 +06:00
33b6ff1c1e improvements in onDeepLink and waitDeepLink 2023-04-21 23:05:58 +06:00
2859f8ca2f hotfix in buttonField 2023-04-21 22:56:07 +06:00
f1aa67ceda add support of InlineQueryResultsButton 2023-04-21 22:26:11 +06:00
1c66d1e2fc add support of WriteAccessAllowed#webAppName 2023-04-21 21:47:14 +06:00
44e6d7adbc add support of SwitchInlineQueryChosenChatInlineKeyboardButton 2023-04-21 21:42:40 +06:00
61a6d4a880 add support of via_chat_folder_invite_link 2023-04-21 21:13:15 +06:00
e37237cb71 add support of get/set MyName 2023-04-21 20:51:22 +06:00
9a5a196e41 add support of custom emojies in mv2 and html 2023-04-21 20:47:13 +06:00
68f6d8553c start 7.1.0 2023-04-21 18:46:20 +06:00
6fe8e73fca Merge pull request #744 from InsanusMokrassar/7.0.2
7.0.2
2023-04-19 22:53:00 +06:00
241545c0cb fix in updates unique calculation and update dependencies 2023-04-19 20:29:40 +06:00
3b2310ece1 fixes in unique updates handling 2023-04-19 19:36:43 +06:00
e1adde0978 Update libs.versions.toml 2023-04-18 14:15:45 +06:00
3b17bce71a fix changelog and readme 2023-04-18 12:55:06 +06:00
999efc64eb complete native integration 2023-04-18 12:40:45 +06:00
05c99642cb update packages publishing to install openssl 2023-04-18 03:26:58 +06:00
cd6f4831ae MultipleClientKtorRequestsExecutor, DefaultKtorRequestsExecutor, KtorRequestsExecutor as expect class 2023-04-18 03:13:41 +06:00
2778315aed add support of fallback handlers in behaviour builder with fsm 2023-04-17 15:50:04 +06:00
ff8d67a780 potential fix in native 2023-04-13 19:26:58 +06:00
2a5f9a629d fixes in TextSourceSerializer 2023-04-13 13:58:20 +06:00
75b27858dd update dependencies 2023-04-13 12:28:51 +06:00
034e2d7ff3 Update libs.versions.toml 2023-04-04 01:43:53 +06:00
1f69c16f2a fix of build 2023-04-04 00:21:59 +06:00
8d24512413 experimentally add support of natives 2023-04-03 23:33:54 +06:00
09009003e3 start 7.0.2 2023-04-03 23:07:10 +06:00
048486146c Merge pull request #738 from InsanusMokrassar/7.0.1
7.0.1
2023-03-16 20:14:57 +06:00
b688fa570a ChatMemberUpdated now inherits WithChat and fill changes related to WithChat 2023-03-16 19:32:43 +06:00
0eba0c4e15 improvements in same-notations 2023-03-16 19:29:35 +06:00
f8bbfa2b1e Merge pull request #737 from Tolsi/more-same-flow-filters
more "same" flow filters were added
2023-03-16 18:52:13 +06:00
fec2715dc1 start 7.0.1 2023-03-16 18:48:36 +06:00
Sergey Tolmachev
941a25b116 more "same" flow filters were added 2023-03-16 16:53:39 +05:30
741d808db2 Merge pull request #736 from InsanusMokrassar/7.0.0
7.0.0
2023-03-11 22:38:33 +06:00
69683a4e6a upfill changelog 2023-03-11 22:38:09 +06:00
e5a48e9684 rework of multipart files with fixes in new sticker sets 2023-03-11 21:17:59 +06:00
6722ab5f50 add several extensions for addStickerToSet 2023-03-11 17:58:48 +06:00
810b2ab5a1 add Sticker#asInputSticker 2023-03-11 17:39:12 +06:00
5992fdd456 Update Sticker.kt 2023-03-11 01:30:12 +06:00
3c4f8787a6 mask position is nullable in mask stickers and sticker sets 2023-03-11 01:18:39 +06:00
0c24aa1270 add sticker format to stickerset 2023-03-11 00:53:36 +06:00
988d9995c5 add sticker format to sticker object 2023-03-11 00:46:20 +06:00
f75df0a425 Update microutils 2023-03-10 22:34:48 +06:00
9f14eb2ca3 update microutils 2023-03-10 15:08:44 +06:00
6f9f880b79 deprecations removing 2023-03-10 14:59:59 +06:00
2d2310daca fixes in build 2023-03-10 14:43:34 +06:00
dff04d26ca add support of setCustomEmojiStickerSetThumbnail 2023-03-10 14:29:55 +06:00
4390c12180 update uploadStickerFile 2023-03-10 14:24:42 +06:00
1b4d13e452 rework of addStickerToSet 2023-03-10 14:20:34 +06:00
afda5e0e7f rework of create new sticker set 2023-03-10 14:00:57 +06:00
cd354e9456 upgrade version up to 7.0.0 2023-03-10 13:40:15 +06:00
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
246 changed files with 4001 additions and 2955 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

@@ -8,12 +8,16 @@ jobs:
- uses: actions/setup-java@v1
with:
java-version: 11
- name: Setup LibCurl
run: sudo apt install -y libcurl4-openssl-dev
- name: Rewrite version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
cat gradle.properties | sed -e "s/^library_version=\([0-9\.]*\)/library_version=\1-branch_$branch-build${{ github.run_number }}/" > gradle.properties.tmp
rm gradle.properties
mv gradle.properties.tmp gradle.properties
- name: KotlinSymbolProcessing execution
run: ./gradlew ksp
- name: Build
run: ./gradlew build
- name: Publish to Gitea

View File

@@ -1,5 +1,116 @@
# TelegramBotAPI changelog
## 8.0.0
**THIS UPDATE CONTAINS BREAKING CHANGES**
**ALL PROJECT DEPRECATIONS HAVE BEEN REMOVED**
**IN THIS UPDATE KORLIBS HAVE BEEN UPDATED TO VERSION `4.0.2`. SINCE THAT VERSION A LOT OF PACKAGES HAVE BEEN RENAMED.
MIGRATIONS USED IN THIS LIB:**
* `com.soywiz.klock` -> `korlibs.time`
* `com.soywiz.krypto` -> `korlibs.crypto`
* `Versions`:
* `Korlibs`: `3.4.0` -> `4.0.3`
* `MicroUtils`: `0.18.4` -> `0.19.1`
## 7.1.3
* `Versions`:
* `Serialization`: `1.5.0` -> `1.5.1`
* `MicroUtils`: `0.18.1` -> `0.18.4`
* `Core`:
* Actualize kdocs in `InputFile`
* `BehaviourBuilder`:
* Now it is possible to use `waitMediaContent`/`waitMediaContentMessage`/`onMediaContent`
* Add `onMention`/`waitMention` functionality
* Add opportunity to map content with extensions to `Flow`
## 7.1.2
* `Versions`:
* `MicroUtils`: `0.18.0` -> `0.18.1`
* `Core`:
* Now it is possible to serialize `Sticker`s
## 7.1.1
* `Versions`:
* `Kotlin`: `1.8.20` -> `1.8.21`
* `MicroUtils`: `0.17.8` -> `0.18.0`
* `Utils`:
* Fixes in `makeLinkToMessage`
## 7.1.0
**This update contains changes according to the [Telegram Bot API 6.7](https://core.telegram.org/bots/api-changelog#april-21-2023)**
* `API`:
* Rename `editMessageCaption` to `editMessageMedia` due to wrong old naming
* Add `edit` extensions for `InlineMessageIdentifier`s
* `BehaviourBuilder`:
* `BehaviourContext` extensions `onDeepLink` and `waitDeepLinks` now can be used with `Regex` or `String` as first parameters
## 7.0.2
_This update brings experimental support of `linuxX64` and `mingwX64` platforms_
* `Versions`:
* `Kotlin`: `1.8.10` -> `1.8.20`
* `MicroUtils`: `0.17.5` -> `0.17.8`
* `Ktor`: `2.2.4` -> `2.3.0`
* `Core`:
* New `RequestsExecutor` - `MultipleClientKtorRequestsExecutor`
* Old `KtorRequestsExecutor` has been renamed to `DefaultKtorRequestsExecutor`
* `KtorRequestsExecutor` now is `expect class`
* On `JS`, `JVM` and `MinGWX64` platforms it is `DefaultKtorRequestsExecutor`
* On `LinuxX64` platform it is `MultipleClientKtorRequestsExecutor`
## 7.0.1
* `Core`:
* New interface `WithChat` which contains `chat` field
* `Message` now inherits `WithChat`
* `ChatMemberUpdated` now inherits `WithChat`
* `Utils`:
* Improvements in `same`-notations
## 7.0.0
This update contains support of [Telegram Bot API 6.6](https://core.telegram.org/bots/api-changelog#march-9-2023)
**THIS VERSION CONTAINS BREAKING CHANGES**:
* All previous deprecations have been removed
* Fully reworked mechanism of stickers creating and adding
* All separations of stickers types like `Animeted` have been replaces with type `StickerFormat`
* New `InputSticker` type (and all subtypes) as replacements for old raw fields in methods
* Reworked mechanism of files uploading
Other changes
* `Versions`:
* `MicroUtils`: `0.17.3` -> `0.17.5`
## 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`:

View File

@@ -1,9 +1,11 @@
# 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)
# 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.7-blue)](https://core.telegram.org/bots/api-changelog#april-21-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) |
| 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) |
| Platforms | ![JVM](https://img.shields.io/badge/JVM-red?style=plastic&logo=openjdk&logoColor=white) ![Js](https://img.shields.io/badge/JavaScript-323330?style=plastic&logo=javascript&logoColor=F7DF1E) |
| Experimental Platforms | [![Linux x64](https://img.shields.io/badge/LinuxX64-FCC624?style=plastic&logo=linux&logoColor=black)](https://kotlinlang.org/docs/native-target-support.html#tier-1) [![MinGW x64](https://img.shields.io/badge/MinGWX64-black?style=plastic&logo=windows&logoColor=green)](https://kotlinlang.org/docs/native-target-support.html#tier-1) |
<!--- [![Telegram Channel](./resources/tg_channel_qr.jpg)](https://t.me/ktgbotapi) --->

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=6.0.2
library_version=8.0.0

View File

@@ -1,22 +1,22 @@
[versions]
kotlin = "1.8.10"
kotlin-serialization = "1.5.0"
kotlin = "1.8.21"
kotlin-serialization = "1.5.1"
kotlin-coroutines = "1.6.4"
javax-activation = "1.1.1"
korlibs = "3.4.0"
korlibs = "4.0.3"
uuid = "0.7.0"
ktor = "2.2.4"
ktor = "2.3.0"
ksp = "1.8.10-1.0.9"
kotlin-poet = "1.12.0"
ksp = "1.8.21-1.0.11"
kotlin-poet = "1.14.2"
microutils = "0.17.1"
microutils = "0.19.1"
github-release-plugin = "2.4.1"
dokka = "1.7.20"
dokka = "1.8.10"
[libraries]
@@ -31,6 +31,8 @@ kotlin-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref =
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor" }
ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor" }
ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
@@ -45,6 +47,7 @@ microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", version.re
microutils-serialization-base64 = { module = "dev.inmo:micro_utils.serialization.base64", version.ref = "microutils" }
microutils-serialization-encapsulator = { module = "dev.inmo:micro_utils.serialization.encapsulator", version.ref = "microutils" }
microutils-serialization-typedSerializer = { module = "dev.inmo:micro_utils.serialization.typed_serializer", version.ref = "microutils" }
microutils-serialization-mapper = { module = "dev.inmo:micro_utils.serialization.mapper", version.ref = "microutils" }
microutils-languageCodes = { module = "dev.inmo:micro_utils.language_codes", version.ref = "microutils" }
microutils-ktor-common = { module = "dev.inmo:micro_utils.ktor.common", version.ref = "microutils" }
microutils-fsm-common = { module = "dev.inmo:micro_utils.fsm.common", version.ref = "microutils" }
@@ -67,3 +70,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

@@ -13,6 +13,8 @@ kotlin {
browser()
nodejs()
}
linuxX64()
mingwX64()
sourceSets {
commonMain {

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

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.extensions.api
import com.soywiz.klock.DateTime
import com.soywiz.klock.TimeSpan
import korlibs.time.DateTime
import korlibs.time.TimeSpan
import dev.inmo.micro_utils.coroutines.LinkedSupervisorJob
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.abstracts.types.WithReplyMarkup

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.extensions.api.answers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.answers.AnswerInlineQuery
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.abstracts.InlineQueryResult
import dev.inmo.tgbotapi.types.InlineQueries.query.InlineQuery
import dev.inmo.tgbotapi.types.InlineQueryIdentifier
@@ -12,8 +13,37 @@ suspend fun TelegramBot.answerInlineQuery(
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String? = null,
switchPmParameter: String? = null
button: InlineQueryResultsButton? = null
) = execute(
AnswerInlineQuery(inlineQueryID, results, cachedTime, isPersonal, nextOffset, button)
)
suspend fun TelegramBot.answerInlineQuery(
inlineQuery: InlineQuery,
results: List<InlineQueryResult> = emptyList(),
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
button: InlineQueryResultsButton? = null
) = answerInlineQuery(inlineQuery.id, results, cachedTime, isPersonal, nextOffset, button)
suspend fun TelegramBot.answer(
inlineQuery: InlineQuery,
results: List<InlineQueryResult> = emptyList(),
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
button: InlineQueryResultsButton? = null
) = answerInlineQuery(inlineQuery.id, results, cachedTime, isPersonal, nextOffset, button)
suspend fun TelegramBot.answerInlineQuery(
inlineQueryID: InlineQueryIdentifier,
results: List<InlineQueryResult> = emptyList(),
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String?,
switchPmParameter: String?
) = execute(
AnswerInlineQuery(inlineQueryID, results, cachedTime, isPersonal, nextOffset, switchPmText, switchPmParameter)
)
@@ -24,8 +54,8 @@ suspend fun TelegramBot.answerInlineQuery(
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String? = null,
switchPmParameter: String? = null
switchPmText: String?,
switchPmParameter: String?
) = answerInlineQuery(inlineQuery.id, results, cachedTime, isPersonal, nextOffset, switchPmText, switchPmParameter)
suspend fun TelegramBot.answer(
@@ -34,6 +64,6 @@ suspend fun TelegramBot.answer(
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String? = null,
switchPmParameter: String? = null
switchPmText: String?,
switchPmParameter: String?
) = answerInlineQuery(inlineQuery.id, results, cachedTime, isPersonal, nextOffset, switchPmText, switchPmParameter)

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.GetMyName
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.getMyName(
languageCode: IetfLanguageCode? = null
) = execute(GetMyName(languageCode))
suspend fun TelegramBot.getMyName(
languageCode: String?
) = getMyName(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,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.GetMyName
import dev.inmo.tgbotapi.requests.bot.SetMyName
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
suspend fun TelegramBot.setMyName(
name: String? = null,
languageCode: IetfLanguageCode? = null
) = execute(SetMyName(name, languageCode))
suspend fun TelegramBot.setMyName(
name: String?,
languageCode: String?
) = setMyName(name, 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

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.chat.invite_links
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.invite_links.CreateChatInviteLink
import dev.inmo.tgbotapi.types.ChatIdentifier

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.extensions.api.chat.invite_links
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.chat.invite_links.EditChatInviteLink
import dev.inmo.tgbotapi.types.ChatIdentifier

View File

@@ -0,0 +1,114 @@
package dev.inmo.tgbotapi.extensions.api.edit
import dev.inmo.tgbotapi.abstracts.TextedWithTextSources
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.edit.caption.editMessageCaption
import dev.inmo.tgbotapi.extensions.api.edit.location.live.editLiveLocation
import dev.inmo.tgbotapi.extensions.api.edit.media.editMessageMedia
import dev.inmo.tgbotapi.extensions.api.edit.reply_markup.editMessageReplyMarkup
import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.location.LiveLocation
import dev.inmo.tgbotapi.types.media.TelegramMedia
import dev.inmo.tgbotapi.types.message.ParseMode
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.textsources.TextSource
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
import dev.inmo.tgbotapi.utils.EntitiesBuilderBody
import dev.inmo.tgbotapi.utils.buildEntities
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
latitude: Double,
longitude: Double,
horizontalAccuracy: Meters? = null,
heading: Degrees? = null,
proximityAlertRadius: Meters? = null,
replyMarkup: InlineKeyboardMarkup? = null
) = editLiveLocation(messageId, latitude, longitude, horizontalAccuracy, heading, proximityAlertRadius, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
location: LiveLocation,
replyMarkup: InlineKeyboardMarkup? = null
) = editLiveLocation(
messageId, location, replyMarkup
)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
media: TelegramMedia,
replyMarkup: InlineKeyboardMarkup? = null
) = editMessageMedia(messageId, media, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
replyMarkup: InlineKeyboardMarkup? = null
) = editMessageReplyMarkup(messageId, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
text: String,
parseMode: ParseMode? = null,
disableWebPagePreview: Boolean? = null,
replyMarkup: InlineKeyboardMarkup? = null
) = editMessageText(messageId, text, parseMode, disableWebPagePreview, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
entities: TextSourcesList,
disableWebPagePreview: Boolean? = null,
replyMarkup: InlineKeyboardMarkup? = null
) = editMessageText(messageId, entities, disableWebPagePreview, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
separator: TextSource? = null,
disableWebPagePreview: Boolean? = null,
replyMarkup: InlineKeyboardMarkup? = null,
builderBody: EntitiesBuilderBody
) = edit(messageId, buildEntities(separator, builderBody), disableWebPagePreview, replyMarkup)
/**
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.edit(
messageId: InlineMessageIdentifier,
separator: String,
disableWebPagePreview: Boolean? = null,
replyMarkup: InlineKeyboardMarkup? = null,
builderBody: EntitiesBuilderBody
) = edit(messageId, buildEntities(separator, builderBody), disableWebPagePreview, replyMarkup)

View File

@@ -10,7 +10,7 @@ import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
* @param replyMarkup Some [InlineKeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard]
* as a builder for that
*/
suspend fun TelegramBot.editMessageCaption(
suspend fun TelegramBot.editMessageMedia(
inlineMessageId: InlineMessageIdentifier,
media: TelegramMedia,
replyMarkup: InlineKeyboardMarkup? = null

View File

@@ -10,13 +10,6 @@ suspend fun TelegramBot.getStickerSet(
GetStickerSet(name)
)
@Deprecated("Renamed", ReplaceWith("getStickerSetOrThrow(sticker)", "dev.inmo.tgbotapi.extensions.api.get.getStickerSetOrThrow"))
suspend fun TelegramBot.getStickerSet(
sticker: Sticker
) = getStickerSet(
sticker.stickerSetName ?: error("Sticker must contains stickerSetName to be correctly used in getStickerSet method")
)
suspend fun TelegramBot.getStickerSetOrNull(
sticker: Sticker
) = sticker.stickerSetName ?.let {

View File

@@ -685,20 +685,22 @@ suspend inline fun TelegramBot.reply(
suspend inline fun TelegramBot.replyWithSticker(
to: Message,
sticker: InputFile,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(to.chat, sticker, to.threadIdOrNull, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(to.chat, sticker, to.threadIdOrNull, emoji, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
sticker: Sticker,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(to.chat, sticker, to.threadIdOrNull, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(to.chat, sticker, to.threadIdOrNull, emoji, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
// Videos

View File

@@ -761,22 +761,24 @@ suspend inline fun TelegramBot.replyWithSticker(
toMessageId: MessageId,
sticker: InputFile,
threadId: MessageThreadId? = toChatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(toChatId, sticker, threadId, disableNotification, protectContent, toMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(toChatId, sticker, threadId, emoji, disableNotification, protectContent, toMessageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
toChatId: IdChatIdentifier,
toMessageId: MessageId,
sticker: Sticker,
threadId: MessageThreadId? = toChatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(toChatId, sticker, threadId, disableNotification, protectContent, toMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(toChatId, sticker, threadId, emoji, disableNotification, protectContent, toMessageId, allowSendingWithoutReply, replyMarkup)
// Videos

View File

@@ -1111,12 +1111,13 @@ suspend fun TelegramBot.send(
chatId: ChatIdentifier,
sticker: Sticker,
threadId: MessageThreadId? = chatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(chatId, sticker, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(chatId, sticker, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* Will execute [sendSticker] request
@@ -1127,12 +1128,13 @@ suspend fun TelegramBot.send(
chat: Chat,
sticker: Sticker,
threadId: MessageThreadId? = chat.id.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(chat, sticker, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(chat, sticker, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, 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

@@ -19,13 +19,14 @@ suspend fun TelegramBot.sendSticker(
chatId: ChatIdentifier,
sticker: InputFile,
threadId: MessageThreadId? = chatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = execute(
SendSticker(chatId, sticker, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
SendSticker(chatId, sticker, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
)
/**
@@ -36,12 +37,13 @@ suspend fun TelegramBot.sendSticker(
chat: Chat,
sticker: InputFile,
threadId: MessageThreadId? = chat.id.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(chat.id, sticker, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(chat.id, sticker, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -51,12 +53,13 @@ suspend fun TelegramBot.sendSticker(
chatId: ChatIdentifier,
sticker: Sticker,
threadId: MessageThreadId? = chatId.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(chatId, sticker.fileId, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(chatId, sticker.fileId, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -66,9 +69,10 @@ suspend fun TelegramBot.sendSticker(
chat: Chat,
sticker: Sticker,
threadId: MessageThreadId? = chat.id.threadId,
emoji: String? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageId? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendSticker(chat, sticker.fileId, threadId, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
) = sendSticker(chat, sticker.fileId, threadId, emoji, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)

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

@@ -1,90 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.AddAnimatedStickerToSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.addAnimatedStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddAnimatedStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addAnimatedStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddAnimatedStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addAnimatedStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addAnimatedStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addAnimatedStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addAnimatedStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addAnimatedStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addAnimatedStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addAnimatedStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)

View File

@@ -1,90 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.AddStaticStickerToSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.addStaticStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddStaticStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addStaticStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddStaticStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addStaticStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addStaticStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addStaticStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addStaticStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addStaticStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addStaticStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addStaticStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)

View File

@@ -0,0 +1,116 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.requests.stickers.AddStickerToSet
import dev.inmo.tgbotapi.requests.stickers.InputSticker
import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.addStickerToSet(
userId: UserId,
stickerSetName: String,
inputSticker: InputSticker
) = execute(
AddStickerToSet(userId, stickerSetName, inputSticker)
)
suspend fun TelegramBot.addStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: InputSticker
) = addStickerToSet(
userId,
stickerSet.name,
sticker
)
suspend fun TelegramBot.addStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: InputFile,
emojis: List<String>,
keywords: List<String> = emptyList()
) = addStickerToSet(
userId,
stickerSet,
when (stickerSet.stickerType) {
StickerType.CustomEmoji -> InputSticker.WithKeywords.CustomEmoji(
sticker,
emojis,
keywords
)
StickerType.Mask -> InputSticker.Mask(
sticker,
emojis
)
StickerType.Regular -> InputSticker.WithKeywords.Regular(
sticker,
emojis,
keywords
)
is StickerType.Unknown -> error("Unable to create sticker to the set with type ${stickerSet.stickerType}")
}
)
suspend fun TelegramBot.addStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: InputFile,
emojis: List<String>,
maskPosition: MaskPosition? = null
) = addStickerToSet(
userId,
stickerSet.name,
when (stickerSet.stickerType) {
StickerType.CustomEmoji -> InputSticker.WithKeywords.CustomEmoji(
sticker,
emojis,
emptyList()
)
StickerType.Mask -> InputSticker.Mask(
sticker,
emojis,
maskPosition
)
StickerType.Regular -> InputSticker.WithKeywords.Regular(
sticker,
emojis,
emptyList()
)
is StickerType.Unknown -> error("Unable to create sticker to the set with type ${stickerSet.stickerType}")
}
)
suspend fun TelegramBot.addStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: InputSticker
) = addStickerToSet(
user.id,
stickerSet.name,
sticker
)
suspend fun TelegramBot.addStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: InputFile,
emojis: List<String>,
keywords: List<String> = emptyList()
) = addStickerToSet(
user.id, stickerSet, sticker, emojis, keywords
)
suspend fun TelegramBot.addStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: InputFile,
emojis: List<String>,
maskPosition: MaskPosition? = null
) = addStickerToSet(
user.id, stickerSet, sticker, emojis, maskPosition
)

View File

@@ -1,90 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.AddVideoStickerToSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.types.stickers.StickerSet
suspend fun TelegramBot.addVideoStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddVideoStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addVideoStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = execute(
AddVideoStickerToSet(userId, stickerSetName, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.addVideoStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addVideoStickerToSet(
user: CommonUser,
stickerSetName: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
user.id, stickerSetName, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addVideoStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addVideoStickerToSet(
userId: UserId,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
userId, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addVideoStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)
suspend fun TelegramBot.addVideoStickerToSet(
user: CommonUser,
stickerSet: StickerSet,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition? = null
) = addVideoStickerToSet(
user.id, stickerSet.name, sticker, emojis, maskPosition
)

View File

@@ -1,58 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.CreateNewAnimatedStickerSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewAnimatedStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewAnimatedStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewAnimatedStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)
suspend fun TelegramBot.createNewAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewAnimatedStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)

View File

@@ -1,54 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewMaskAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskAnimatedStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskAnimatedStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskAnimatedStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)
suspend fun TelegramBot.createNewMaskAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskAnimatedStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)

View File

@@ -1,54 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewMaskStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)
suspend fun TelegramBot.createNewMaskStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)

View File

@@ -1,54 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewMaskVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskVideoStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = execute(
CreateNewMaskVideoStickerSet(userId, name, title, sticker, emojis, maskPosition)
)
suspend fun TelegramBot.createNewMaskVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskVideoStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)
suspend fun TelegramBot.createNewMaskVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
maskPosition: MaskPosition
) = createNewMaskVideoStickerSet(
user.id, name, title, sticker, emojis, maskPosition
)

View File

@@ -1,50 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewRegularAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String
) = execute(
CreateNewRegularAnimatedStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = execute(
CreateNewRegularAnimatedStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String
) = createNewRegularAnimatedStickerSet(
user.id, name, title, sticker, emojis
)
suspend fun TelegramBot.createNewRegularAnimatedStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = createNewRegularAnimatedStickerSet(
user.id, name, title, sticker, emojis
)

View File

@@ -1,50 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewRegularStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String
) = execute(
CreateNewRegularStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = execute(
CreateNewRegularStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String
) = createNewRegularStickerSet(
user.id, name, title, sticker, emojis
)
suspend fun TelegramBot.createNewRegularStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = createNewRegularStickerSet(
user.id, name, title, sticker, emojis
)

View File

@@ -1,50 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.*
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewRegularVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String
) = execute(
CreateNewRegularVideoStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = execute(
CreateNewRegularVideoStickerSet(userId, name, title, sticker, emojis)
)
suspend fun TelegramBot.createNewRegularVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String
) = createNewRegularVideoStickerSet(
user.id, name, title, sticker, emojis
)
suspend fun TelegramBot.createNewRegularVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String
) = createNewRegularVideoStickerSet(
user.id, name, title, sticker, emojis
)

View File

@@ -1,58 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.CreateNewStaticStickerSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewStaticStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewStaticStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewStaticStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewStaticStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewStaticStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewStaticStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)
suspend fun TelegramBot.createNewStaticStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewStaticStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)

View File

@@ -0,0 +1,31 @@
package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.stickers.CreateNewStickerSet
import dev.inmo.tgbotapi.requests.stickers.InputSticker
import dev.inmo.tgbotapi.types.StickerFormat
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
suspend fun TelegramBot.createNewStickerSet(
userId: UserId,
name: String,
title: String,
stickersFormat: StickerFormat,
stickers: List<InputSticker>,
needsRepainting: Boolean = false
) = execute(
CreateNewStickerSet(userId, name, title, stickersFormat, stickers, needsRepainting)
)
suspend fun TelegramBot.createNewStickerSet(
user: CommonUser,
name: String,
title: String,
stickersFormat: StickerFormat,
stickers: List<InputSticker>,
needsRepainting: Boolean = false,
) = createNewStickerSet(
user.id, name, title, stickersFormat, stickers, needsRepainting
)

View File

@@ -1,58 +0,0 @@
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.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.CreateNewVideoStickerSet
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.stickers.MaskPosition
suspend fun TelegramBot.createNewVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewVideoStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewVideoStickerSet(
userId: UserId,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = execute(
CreateNewVideoStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
)
suspend fun TelegramBot.createNewVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: FileId,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewVideoStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)
suspend fun TelegramBot.createNewVideoStickerSet(
user: CommonUser,
name: String,
title: String,
sticker: MultipartFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
) = createNewVideoStickerSet(
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)

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,26 @@
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.SetCustomEmojiStickerSetThumbnail
import dev.inmo.tgbotapi.requests.stickers.SetStickerSetThumbnail
import dev.inmo.tgbotapi.types.CustomEmojiId
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.setCustomEmojiStickerSetThumbnail(
stickerSetName: StickerSetName,
customEmojiId: CustomEmojiId
) = execute(
SetCustomEmojiStickerSetThumbnail(stickerSetName, customEmojiId)
)
suspend fun TelegramBot.setCustomEmojiStickerSetThumbnail(
stickerSet: StickerSet,
customEmojiId: CustomEmojiId
) = setCustomEmojiStickerSetThumbnail(
stickerSet.name, customEmojiId
)

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,72 +3,72 @@ 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.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
thumbSetName: String,
thumb: FileId
stickerSetName: StickerSetName,
thumbnail: FileId
) = execute(
SetStickerSetThumb(userId, thumbSetName, thumb)
SetStickerSetThumbnail(userId, stickerSetName, thumbnail)
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
thumbSetName: String,
thumb: MultipartFile
stickerSetName: StickerSetName,
thumbnail: MultipartFile
) = execute(
SetStickerSetThumb(userId, thumbSetName, thumb)
SetStickerSetThumbnail(userId, stickerSetName, thumbnail)
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
thumbSetName: String,
thumb: FileId
) = setStickerSetThumb(
user.id, thumbSetName, thumb
stickerSetName: StickerSetName,
thumbnail: FileId
) = setStickerSetThumbnail(
user.id, stickerSetName, thumbnail
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
thumbSetName: String,
thumb: MultipartFile
) = setStickerSetThumb(
user.id, thumbSetName, thumb
stickerSetName: StickerSetName,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
user.id, stickerSetName, thumbnail
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
thumbSet: StickerSet,
thumb: FileId
) = setStickerSetThumb(
userId, thumbSet.name, thumb
stickerSet: StickerSet,
thumbnail: FileId
) = setStickerSetThumbnail(
userId, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
userId: UserId,
thumbSet: StickerSet,
thumb: MultipartFile
) = setStickerSetThumb(
userId, thumbSet.name, thumb
stickerSet: StickerSet,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
userId, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
thumbSet: StickerSet,
thumb: FileId
) = setStickerSetThumb(
user.id, thumbSet.name, thumb
stickerSet: StickerSet,
thumbnail: FileId
) = setStickerSetThumbnail(
user.id, stickerSet.name, thumbnail
)
suspend fun TelegramBot.setStickerSetThumb(
suspend fun TelegramBot.setStickerSetThumbnail(
user: CommonUser,
thumbSet: StickerSet,
thumb: MultipartFile
) = setStickerSetThumb(
user.id, thumbSet.name, thumb
stickerSet: StickerSet,
thumbnail: MultipartFile
) = setStickerSetThumbnail(
user.id, stickerSet.name, thumbnail
)

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

@@ -3,19 +3,22 @@ package dev.inmo.tgbotapi.extensions.api.stickers
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.stickers.UploadStickerFile
import dev.inmo.tgbotapi.types.StickerFormat
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.UserId
suspend fun TelegramBot.uploadStickerFile(
userId: UserId,
sticker: MultipartFile
sticker: MultipartFile,
stickerFormat: StickerFormat
) = execute(
UploadStickerFile(userId, sticker)
UploadStickerFile(userId, sticker, stickerFormat)
)
suspend fun TelegramBot.uploadStickerFile(
user: CommonUser,
sticker: MultipartFile
sticker: MultipartFile,
stickerFormat: StickerFormat
) = execute(
UploadStickerFile(user.id, sticker)
UploadStickerFile(user.id, sticker, stickerFormat)
)

View File

@@ -9,6 +9,7 @@ import dev.inmo.tgbotapi.types.files.TelegramMediaFile
import dev.inmo.tgbotapi.types.message.content.MediaContent
import io.ktor.util.cio.use
import io.ktor.util.cio.writeChannel
import io.ktor.utils.io.copyAndClose
import io.ktor.utils.io.copyTo
import kotlinx.coroutines.job
import java.io.File
@@ -25,7 +26,7 @@ suspend fun TelegramBot.downloadFile(
doOutsideOfCoroutine { destFile.createNewFile() }
destFile.writeChannel(coroutineContext.job).use {
readChannel.copyTo(this)
readChannel.copyAndClose(this)
}
return destFile

View File

@@ -30,11 +30,17 @@ suspend fun TelegramBot.downloadFileToTemp(
suspend fun TelegramBot.downloadFileToTemp(
pathedFile: PathedFile
) = downloadFileToTemp(
): File = downloadFileToTemp(
pathedFile.filePath
).apply {
runCatching {
renameTo(File(parentFile, "$nameWithoutExtension.${pathedFile.fileName.fileExtension}"))
).run {
val newFile = File(parentFile, "$nameWithoutExtension.${pathedFile.fileName.fileExtension}")
val success = runCatching {
renameTo(newFile)
}.getOrElse { false }
if (success) {
newFile
} else {
this@run
}
}

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

@@ -93,8 +93,9 @@ interface BehaviourContextWithFSM<T : State> : BehaviourContext, StatesMachine<T
behaviourContext: BehaviourContext,
handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
statesManager: StatesManager<T>,
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, onStateHandlingErrorHandler)
) = DefaultBehaviourContextWithFSM<T>(behaviourContext, statesManager, handlers, fallbackHandler, onStateHandlingErrorHandler)
}
}
@@ -129,11 +130,12 @@ class DefaultBehaviourContextWithFSM<T : State>(
private val behaviourContext: BehaviourContext,
private val statesManager: StatesManager<T>,
private val handlers: List<BehaviourWithFSMStateHandlerHolder<*, T>>,
private val fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
private val onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler()
) : BehaviourContext by behaviourContext, BehaviourContextWithFSM<T> {
private val updatesFlows = mutableMapOf<Any, DefaultBehaviourContextWithFSM<T>>()
private val additionalHandlers = mutableListOf<BehaviourWithFSMStateHandlerHolder<*, T>>()
private var actualHandlersList = additionalHandlers + handlers
private var actualHandlersList = additionalHandlers + handlers + listOfNotNull(fallbackHandler)
protected val statesJobs = mutableMapOf<T, Job>()
protected val statesJobsMutex = Mutex()
@@ -250,6 +252,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
handlers,
statesManager,
fallbackHandler,
onStateHandlingErrorHandler
)
@@ -265,6 +268,7 @@ class DefaultBehaviourContextWithFSM<T : State>(
behaviourContext.copy(bot, scope, broadcastChannelsSize, onBufferOverflow, upstreamUpdatesFlow, triggersHolder),
handlers,
statesManager,
fallbackHandler,
onStateHandlingErrorHandler
)
}

View File

@@ -31,6 +31,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
@@ -41,6 +42,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
),
presetHandlers,
statesManager,
fallbackHandler,
onStateHandlingErrorHandler
).apply { block() }
@@ -59,6 +61,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
@@ -71,6 +74,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
fallbackHandler,
onStateHandlingErrorHandler,
block
).run {
@@ -104,6 +108,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
block: CustomBehaviourContextReceiver<DefaultBehaviourContextWithFSM<T>, Unit>
): DefaultBehaviourContextWithFSM<T> = BehaviourContextWithFSM(
@@ -114,6 +119,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSM(
),
presetHandlers,
statesManager,
fallbackHandler,
onStateHandlingErrorHandler
).apply { block() }
@@ -137,6 +143,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
autoDisableWebhooks: Boolean = true,
@@ -150,6 +157,7 @@ suspend fun <T : State> TelegramBot.buildBehaviourWithFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
fallbackHandler,
onStateHandlingErrorHandler,
block
).run {

View File

@@ -46,6 +46,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
@@ -65,6 +66,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSM(
defaultExceptionsHandler,
statesManager,
presetHandlers,
fallbackHandler,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,
@@ -97,6 +99,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
defaultExceptionsHandler: ExceptionHandler<Unit>? = null,
statesManager: StatesManager<T> = DefaultStatesManager(InMemoryDefaultStatesManagerRepo()),
presetHandlers: List<BehaviourWithFSMStateHandlerHolder<*, T>> = listOf(),
fallbackHandler: BehaviourWithFSMStateHandlerHolder<T, T>? = null,
testServer: Boolean = false,
onStateHandlingErrorHandler: StateHandlingErrorHandler<T> = defaultStateHandlingErrorHandler(),
timeoutSeconds: Seconds = 30,
@@ -116,6 +119,7 @@ suspend fun <T : State> telegramBotWithBehaviourAndFSMAndStartLongPolling(
defaultExceptionsHandler,
statesManager,
presetHandlers,
fallbackHandler,
onStateHandlingErrorHandler,
timeoutSeconds,
autoDisableWebhooks,

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,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder
import dev.inmo.micro_utils.coroutines.*
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.handlers_registrar.TriggersHolder
import dev.inmo.tgbotapi.types.UpdateIdentifier
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.updateshandlers.*
import kotlinx.coroutines.*
@@ -71,9 +72,17 @@ class DefaultBehaviourContext(
private val additionalUpdatesSharedFlow = MutableSharedFlow<Update>(0, broadcastChannelsSize, onBufferOverflow)
override val allUpdatesFlow: Flow<Update> = (additionalUpdatesSharedFlow.asSharedFlow()).let {
if (upstreamUpdatesFlow != null) {
var lastHandledUpdate = -1L
val handledUpdates = mutableSetOf<UpdateIdentifier>()
(it + upstreamUpdatesFlow).filter {
(it.updateId > lastHandledUpdate).also { passed -> if (passed) { lastHandledUpdate = it.updateId } }
val passed = handledUpdates.add(it.updateId)
(passed).also { passed ->
val needToDropCount = handledUpdates.size - broadcastChannelsSize
if (needToDropCount > 0) {
handledUpdates.removeAll(
handledUpdates.take(needToDropCount).ifEmpty { return@also }
)
}
}
}
} else {
it

View File

@@ -10,186 +10,127 @@ import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
typealias CommonMessageToContentMapper<T> = suspend CommonMessage<T>.() -> T?
@RiskFeature(lowLevelRiskFeatureMessage)
suspend inline fun <reified O : MessageContent> BehaviourContext.waitContent(
suspend inline fun BehaviourContext.waitContent(
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null }
): Flow<O> = waitContentMessage<O>(initRequest, errorFactory).map { it.content }
): Flow<MessageContent> = waitContentMessage(initRequest, errorFactory).map { it.content }
inline fun <reified T : MessageContent> Flow<MessageContent>.mapContent() = mapNotNull { it as? T }
@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
) = waitContent<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<MessageContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextedContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent(initRequest, errorFactory).mapContent<TextedContent>()
suspend fun BehaviourContext.waitContact(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<ContactContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<ContactContent>()
suspend fun BehaviourContext.waitDice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<DiceContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<DiceContent>()
suspend fun BehaviourContext.waitGame(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<GameContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<GameContent>()
suspend fun BehaviourContext.waitLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<LocationContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<LocationContent>()
suspend fun BehaviourContext.waitLiveLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<LiveLocationContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<LiveLocationContent>()
suspend fun BehaviourContext.waitStaticLocation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<StaticLocationContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<StaticLocationContent>()
suspend fun BehaviourContext.waitPoll(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<PollContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<PollContent>()
suspend fun BehaviourContext.waitText(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<TextContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<TextContent>()
suspend fun BehaviourContext.waitVenue(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VenueContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<VenueContent>()
suspend fun BehaviourContext.waitAudioMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
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
) = waitContent<DocumentMediaGroupPartContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<AudioMediaGroupPartContent>()
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
) = waitContent<MediaContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<DocumentMediaGroupPartContent>()
suspend fun BehaviourContext.waitMedia(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<MediaContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<MediaContent>()
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<MediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<MediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<MediaGroupPartContent>()
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitVisualMediaGroupContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<VisualMediaGroupPartContent>()
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<TextedMediaContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<TextedMediaContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<TextedMediaContent>()
suspend fun BehaviourContext.waitAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<AnimationContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<AnimationContent>()
suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<AudioContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAudio(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<AudioContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<AudioContent>()
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<DocumentContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitDocument(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<DocumentContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
) = waitContent(initRequest, errorFactory).mapContent<DocumentContent>()
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<PhotoContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitPhoto(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContent<PhotoContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<PhotoContent>()
suspend fun BehaviourContext.waitSticker(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<StickerContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContent<VideoContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<StickerContent>()
suspend fun BehaviourContext.waitVideo(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VideoContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<VideoContent>()
suspend fun BehaviourContext.waitVideoNote(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VideoNoteContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<VideoNoteContent>()
suspend fun BehaviourContext.waitVoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VoiceContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<VoiceContent>()
suspend fun BehaviourContext.waitInvoice(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<InvoiceContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<InvoiceContent>()
suspend fun BehaviourContext.waitVisualContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent<VisualMediaGroupPartContent>(initRequest, errorFactory)
) = waitContent(initRequest, errorFactory).mapContent<VisualMediaGroupPartContent>()
suspend fun BehaviourContext.waitMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent(initRequest, errorFactory).mapContent<MediaContent>()

View File

@@ -5,6 +5,7 @@ package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.*
@@ -12,212 +13,138 @@ import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.lowLevelRiskFeatureMessage
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.mapNotNull
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(
suspend inline fun BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
noinline errorFactory: NullableRequestBuilder<*> = { null }
): Flow<CommonMessage<O>> = expectFlow(
): Flow<CommonMessage<MessageContent>> = expectFlow(
initRequest,
errorFactory
) {
if (it !is BaseSentMessageUpdate) {
return@expectFlow emptyList()
}
listOfNotNull((it.data as? CommonMessage<*>) ?.withContent<O>())
listOfNotNull((it.data as? CommonMessage<*>))
}
internal inline fun <reified T : MessageContent> contentMessageConverter(
noinline mapper: CommonMessageToCommonMessageMapper<T>? = null
): suspend CommonMessage<MessageContent>.() -> CommonMessage<T>? = mapper ?.let {
{
if (content is T) {
@Suppress("UNCHECKED_CAST")
val message = (this as CommonMessage<T>)
safelyWithoutExceptions { mapper(message) }
} else {
null
}
}
} ?: {
@Suppress("UNCHECKED_CAST")
if (content is T) this as CommonMessage<T> else null
}
inline fun <reified T : MessageContent> Flow<CommonMessage<MessageContent>>.mapWithContent() = mapNotNull { it.withContentOrNull<T>() }
@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
) = waitContentMessage<MessageContent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitAnyContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContentMessage<MessageContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory)
suspend fun BehaviourContext.waitTextedContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
) = waitContentMessage(initRequest, errorFactory).mapWithContent<TextedContent>()
suspend fun BehaviourContext.waitContactMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<ContactContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<ContactContent>()
suspend fun BehaviourContext.waitDiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<DiceContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<DiceContent>()
suspend fun BehaviourContext.waitGameMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<GameContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<GameContent>()
suspend fun BehaviourContext.waitLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<LocationContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<LocationContent>()
suspend fun BehaviourContext.waitLiveLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<LiveLocationContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<LiveLocationContent>()
suspend fun BehaviourContext.waitStaticLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<StaticLocationContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<StaticLocationContent>()
suspend fun BehaviourContext.waitPollMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<PollContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<PollContent>()
suspend fun BehaviourContext.waitTextMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<TextContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<TextContent>()
suspend fun BehaviourContext.waitVenueMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VenueContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudioMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContentMessage<AudioMediaGroupPartContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VenueContent>()
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
) = waitContentMessage<DocumentMediaGroupPartContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<AudioMediaGroupPartContent>()
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
) = waitContentMessage<MediaContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<DocumentMediaGroupPartContent>()
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
) = waitContentMessage<MediaGroupPartContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<MediaContent>()
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
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<MediaGroupPartContent>()
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
) = waitContentMessage<TextedMediaContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VisualMediaGroupPartContent>()
suspend fun BehaviourContext.waitTextedMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<TextedMediaContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<TextedMediaContent>()
suspend fun BehaviourContext.waitAnimationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<AnimationContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitAudioMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContentMessage<AudioContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<AnimationContent>()
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
) = waitContentMessage<DocumentContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<AudioContent>()
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
) = waitContentMessage<PhotoContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<DocumentContent>()
suspend fun BehaviourContext.waitPhotoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<PhotoContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<PhotoContent>()
suspend fun BehaviourContext.waitStickerMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<StickerContent>(initRequest, errorFactory)
@Deprecated(includeMediaGroupsDeprecationMessage)
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
includeMediaGroups: Boolean
) = waitContentMessage<VideoContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<StickerContent>()
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VideoContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VideoContent>()
suspend fun BehaviourContext.waitVideoNoteMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VideoNoteContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VideoNoteContent>()
suspend fun BehaviourContext.waitVoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VoiceContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VoiceContent>()
suspend fun BehaviourContext.waitInvoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<InvoiceContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<InvoiceContent>()
suspend fun BehaviourContext.waitVisualContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage<VisualMediaGroupPartContent>(initRequest, errorFactory)
) = waitContentMessage(initRequest, errorFactory).mapWithContent<VisualMediaGroupPartContent>()
suspend fun BehaviourContext.waitMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage(initRequest, errorFactory).mapWithContent<MediaContent>()

View File

@@ -21,3 +21,17 @@ suspend fun BehaviourContext.waitDeepLinks(
.flattenCommandsWithParams().mapNotNull {
it.first to (it.second.second.singleOrNull() ?.regularTextSourceOrNull() ?.source ?.removePrefix(" ") ?: return@mapNotNull null)
}
suspend fun BehaviourContext.waitDeepLinks(
regex: Regex,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
): Flow<Pair<CommonMessage<TextContent>, String>> = waitDeepLinks(initRequest, errorFactory).filter {
regex.matches(it.second)
}
suspend fun BehaviourContext.waitDeepLinks(
deepLink: String,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
): Flow<Pair<CommonMessage<TextContent>, String>> = waitDeepLinks(Regex("^$deepLink$"), initRequest, errorFactory)

View File

@@ -109,7 +109,7 @@ suspend fun BehaviourContext.waitGroupChatCreatedEvents(
suspend fun BehaviourContext.waitLeftChatMemberEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEvents<LeftChatMember>(initRequest, errorFactory)
) = waitEvents<LeftChatMemberEvent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitNewChatPhotoEvents(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }

View File

@@ -106,7 +106,7 @@ suspend fun BehaviourContext.waitGroupChatCreatedEventsMessages(
suspend fun BehaviourContext.waitLeftChatMemberEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitEventsMessages<LeftChatMember>(initRequest, errorFactory)
) = waitEventsMessages<LeftChatMemberEvent>(initRequest, errorFactory)
suspend fun BehaviourContext.waitNewChatPhotoEventsMessages(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }

View File

@@ -0,0 +1,107 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.whenMentionTextSource
import dev.inmo.tgbotapi.extensions.utils.whenTextMentionTextSource
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.Username
import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.message.content.TextedContent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
/**
* Check, that [TextedContent.textSources] contains:
*
* * Any [dev.inmo.tgbotapi.types.message.textsources.MentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.MentionTextSource.username]
* equal to [username]
* * Any [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource.user]
* with the same [username]
*/
fun TextedContent.isWithMention(username: Username) = textSources.any {
it.whenMentionTextSource {
it.username == username
} ?: it.whenTextMentionTextSource {
it.user.username == username
} ?: false
}
/**
* Check, that [TextedContent.textSources] contains:
*
* * Any [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource] with [dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource.user]
* with the same [userId]
*/
fun TextedContent.isWithTextMention(userId: UserId) = textSources.any {
it.whenTextMentionTextSource {
it.user.id == userId
} ?: false
}
/**
* Uses [isWithMention] with [user] [Username] (is presented) or [isWithTextMention] with [user] [UserId] to determine
* user mentioning in [this] [CommonMessage]
*/
fun TextedContent.isWithMention(user: User): Boolean = user.username ?.let { username -> isWithMention(username) } == true || isWithTextMention(user.id)
/**
* Uses [isWithMention] passing [username] as argument to take only messages with [username] mentions or text mentions
*/
fun Flow<TextedContent>.filterMentions(username: Username) = filter {
it.isWithMention(username)
}
/**
* Uses [isWithTextMention] passing [userId] as argument to take only messages with [userId] text mentions
*/
fun Flow<TextedContent>.filterTextMentions(userId: UserId) = filter {
it.isWithTextMention(userId)
}
/**
* Uses [isWithMention] passing [user] as argument to take only messages with [user] mentions or text mentions
*/
fun Flow<TextedContent>.filterMentions(user: User) = filter {
it.isWithMention(user)
}
/**
* Creates cold [Flow] with the messages with [TextedContent] where [username] has been mentioned
*
* @see filterMentions
* @see filterTextMentions
*/
suspend fun BehaviourContext.waitContentWithMentions (
username: Username,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContent(initRequest, errorFactory).mapContent<TextedContent>().filterMentions(username)
/**
* Creates cold [Flow] with the messages with [TextedContent] where [userId] has been mentioned with text
*
* @see filterTextMentions
* @see filterMentions
* @see dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource
*/
suspend fun BehaviourContext.waitContentWithTextMentions (
userId: UserId,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitTextedContent(initRequest, errorFactory).filterTextMentions(userId)
/**
* Creates cold [Flow] with the messages with [TextedContent] where [user] has been mentioned as text or mentioned
* with text
*
* @see filterMentions
* @see filterTextMentions
*/
suspend fun BehaviourContext.waitContentWithMentions (
user: User,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitTextedContent(initRequest, errorFactory).filterMentions(user)

View File

@@ -0,0 +1,82 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.expectations
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.utils.whenMentionTextSource
import dev.inmo.tgbotapi.extensions.utils.whenTextMentionTextSource
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.Username
import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.TextedContent
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
fun CommonMessage<TextedContent>.isWithMention(username: Username) = content.isWithMention(username)
fun CommonMessage<TextedContent>.isWithTextMention(userId: UserId) = content.isWithTextMention(userId)
/**
* Uses [isWithMention] with [user] [Username] (is presented) or [isWithTextMention] with [user] [UserId] to determine
* user mentioning in [this] [CommonMessage]
*/
fun CommonMessage<TextedContent>.isWithMention(user: User): Boolean = content.isWithMention(user)
/**
* Uses [isWithMention] passing [username] as argument to take only messages with [username] mentions or text mentions
*/
fun Flow<CommonMessage<TextedContent>>.filterMentionsMessages(username: Username) = filter {
it.isWithMention(username)
}
/**
* Uses [isWithTextMention] passing [userId] as argument to take only messages with [userId] text mentions
*/
fun Flow<CommonMessage<TextedContent>>.filterTextMentionsMessages(userId: UserId) = filter {
it.isWithTextMention(userId)
}
/**
* Uses [isWithMention] passing [user] as argument to take only messages with [user] mentions or text mentions
*/
fun Flow<CommonMessage<TextedContent>>.filterMentionsMessages(user: User) = filter {
it.isWithMention(user)
}
/**
* Creates cold [Flow] with the messages with [TextedContent] where [username] has been mentioned
*
* @see filterMentions
* @see filterTextMentions
*/
suspend fun BehaviourContext.waitContentMessageWithMentions (
username: Username,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitContentMessage(initRequest, errorFactory).mapWithContent<TextedContent>().filterMentionsMessages(username)
/**
* Creates cold [Flow] with the messages with [TextedContent] where [userId] has been mentioned with text
*
* @see filterTextMentions
* @see filterMentions
* @see dev.inmo.tgbotapi.types.message.textsources.TextMentionTextSource
*/
suspend fun BehaviourContext.waitContentMessageWithTextMentions (
userId: UserId,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitTextedContentMessage(initRequest, errorFactory).filterTextMentionsMessages(userId)
/**
* Creates cold [Flow] with the messages with [TextedContent] where [user] has been mentioned as text or mentioned
* with text
*
* @see filterMentions
* @see filterTextMentions
*/
suspend fun BehaviourContext.waitContentMessageWithMentions (
user: User,
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null }
) = waitTextedContentMessage(initRequest, errorFactory).filterMentionsMessages(user)

View File

@@ -248,6 +248,30 @@ suspend fun <BC : BehaviourContext> BC.onText(
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.onTextedContent(
initialFilter: CommonMessageFilter<TextedContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, TextedMessage, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in TextedMessage, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, TextedMessage>
) = onContentMessageWithType(
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,
@@ -631,3 +655,27 @@ suspend fun <BC : BehaviourContext> BC.onVisualContent(
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.onMediaContent(
initialFilter: CommonMessageFilter<MediaContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, MediaMessage, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in MediaMessage, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, MediaMessage>
) = 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)
@@ -46,3 +46,24 @@ suspend fun <BC : BehaviourContext> BC.onDeepLink(
this@onDeepLink.launchSafelyWithoutExceptions { triggersHolder.handleableCommandsHolder.unregisterHandleable(startRegex) }
}
}
suspend fun <BC : BehaviourContext> BC.onDeepLink(
regex: Regex,
initialFilter: SimpleFilter<Pair<TextMessage, String>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, Pair<TextMessage, String>, Update> = { (message, _), update -> MessageFilterByChat(this, message, update) },
markerFactory: MarkerFactory<Pair<TextMessage, String>, Any> = MarkerFactory { (message, _) -> ByChatMessageMarkerFactory(message) },
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, Pair<TextMessage, String>>
): Job {
val internalFilter = SimpleFilter<Pair<TextMessage, String>> {
regex.matches(it.second)
}
return onDeepLink(initialFilter ?.let { internalFilter * it } ?: internalFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
}
suspend fun <BC : BehaviourContext> BC.onDeepLink(
deepLink: String,
initialFilter: SimpleFilter<Pair<TextMessage, String>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, Pair<TextMessage, String>, Update> = { (message, _), update -> MessageFilterByChat(this, message, update) },
markerFactory: MarkerFactory<Pair<TextMessage, String>, Any> = MarkerFactory { (message, _) -> ByChatMessageMarkerFactory(message) },
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, Pair<TextMessage, String>>
): Job = onDeepLink(Regex("^$deepLink$"), initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)

View File

@@ -346,10 +346,10 @@ suspend fun <BC : BehaviourContext> BC.onGroupChatCreated(
* data
*/
suspend fun <BC : BehaviourContext> BC.onLeftChatMember(
initialFilter: SimpleFilter<ChatEventMessage<LeftChatMember>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<LeftChatMember>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<LeftChatMember>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<LeftChatMember>>
initialFilter: SimpleFilter<ChatEventMessage<LeftChatMemberEvent>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, ChatEventMessage<LeftChatMemberEvent>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in ChatEventMessage<LeftChatMemberEvent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, ChatEventMessage<LeftChatMemberEvent>>
) = onEvent(initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
/**

View File

@@ -0,0 +1,357 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.CustomBehaviourContextAndTwoTypesReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.CustomBehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.isWithMention
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.isWithTextMention
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.AnyMarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.times
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.Username
import dev.inmo.tgbotapi.types.chat.User
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.AnimationContent
import dev.inmo.tgbotapi.types.message.content.AudioContent
import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent
import dev.inmo.tgbotapi.types.message.content.PhotoContent
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextedContent
import dev.inmo.tgbotapi.types.message.content.VideoContent
import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupPartContent
import dev.inmo.tgbotapi.types.message.content.VoiceContent
import dev.inmo.tgbotapi.types.update.abstracts.Update
internal suspend inline fun <BC : BehaviourContext, reified T : TextedContent> BC.onMention(
username: Username,
initialFilter: CommonMessageFilter<T>? = null,
noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<T>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<T>, Any> = AnyMarkerFactory(),
noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<T>>
) = onContentMessageWithType<BC, T>(
initialFilter * {
it.content.isWithMention(username)
},
subcontextUpdatesFilter,
markerFactory,
scenarioReceiver
)
internal suspend inline fun <BC : BehaviourContext, reified T : TextedContent> BC.onTextMention(
userId: UserId,
initialFilter: CommonMessageFilter<T>? = null,
noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<T>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<T>, Any> = AnyMarkerFactory(),
noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<T>>
) = onContentMessageWithType<BC, T>(
initialFilter * {
it.content.isWithTextMention(userId)
},
subcontextUpdatesFilter,
markerFactory,
scenarioReceiver
)
internal suspend inline fun <BC : BehaviourContext, reified T : TextedContent> BC.onMention(
user: User,
initialFilter: CommonMessageFilter<T>? = null,
noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<T>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<T>, Any> = AnyMarkerFactory(),
noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<T>>
) = onContentMessageWithType<BC, T>(
initialFilter * {
it.content.isWithMention(user)
},
subcontextUpdatesFilter,
markerFactory,
scenarioReceiver
)
suspend fun <BC : BehaviourContext> BC.onMentionWithAnyContent(
username: Username,
initialFilter: CommonMessageFilter<TextedContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextedContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextedContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextedContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithAnyContent(
userId: UserId,
initialFilter: CommonMessageFilter<TextedContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextedContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextedContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextedContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithAnyContent(
user: User,
initialFilter: CommonMessageFilter<TextedContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextedContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextedContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextedContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVoiceContent(
username: Username,
initialFilter: CommonMessageFilter<VoiceContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VoiceContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VoiceContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VoiceContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithVoiceContent(
userId: UserId,
initialFilter: CommonMessageFilter<VoiceContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VoiceContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VoiceContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VoiceContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVoiceContent(
user: User,
initialFilter: CommonMessageFilter<VoiceContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VoiceContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VoiceContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VoiceContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithMediaGroupContent(
username: Username,
initialFilter: CommonMessageFilter<MediaGroupContent<MediaGroupPartContent>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupContent<MediaGroupPartContent>>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupContent<MediaGroupPartContent>>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithMediaGroupContent(
userId: UserId,
initialFilter: CommonMessageFilter<MediaGroupContent<MediaGroupPartContent>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupContent<MediaGroupPartContent>>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupContent<MediaGroupPartContent>>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupContent<MediaGroupPartContent>>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithMediaGroupContent(
user: User,
initialFilter: CommonMessageFilter<MediaGroupContent<MediaGroupPartContent>>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupContent<MediaGroupPartContent>>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupContent<MediaGroupPartContent>>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupContent<MediaGroupPartContent>>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithMediaGroupPartContent(
username: Username,
initialFilter: CommonMessageFilter<MediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupPartContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithMediaGroupPartContent(
userId: UserId,
initialFilter: CommonMessageFilter<MediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupPartContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithMediaGroupPartContent(
user: User,
initialFilter: CommonMessageFilter<MediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<MediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaGroupPartContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithAudioContent(
username: Username,
initialFilter: CommonMessageFilter<AudioContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AudioContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AudioContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AudioContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithAudioContent(
userId: UserId,
initialFilter: CommonMessageFilter<AudioContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AudioContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AudioContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AudioContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithAudioContent(
user: User,
initialFilter: CommonMessageFilter<AudioContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AudioContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AudioContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AudioContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithDocumentContent(
username: Username,
initialFilter: CommonMessageFilter<DocumentContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DocumentContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<DocumentContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DocumentContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithDocumentContent(
userId: UserId,
initialFilter: CommonMessageFilter<DocumentContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DocumentContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<DocumentContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DocumentContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithDocumentContent(
user: User,
initialFilter: CommonMessageFilter<DocumentContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DocumentContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<DocumentContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DocumentContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVisualMediaGroupPartContent(
username: Username,
initialFilter: CommonMessageFilter<VisualMediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VisualMediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VisualMediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VisualMediaGroupPartContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithVisualMediaGroupPartContent(
userId: UserId,
initialFilter: CommonMessageFilter<VisualMediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VisualMediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VisualMediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VisualMediaGroupPartContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVisualMediaGroupPartContent(
user: User,
initialFilter: CommonMessageFilter<VisualMediaGroupPartContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VisualMediaGroupPartContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VisualMediaGroupPartContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VisualMediaGroupPartContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVideoContent(
username: Username,
initialFilter: CommonMessageFilter<VideoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VideoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VideoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VideoContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithVideoContent(
userId: UserId,
initialFilter: CommonMessageFilter<VideoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VideoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VideoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VideoContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithVideoContent(
user: User,
initialFilter: CommonMessageFilter<VideoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VideoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<VideoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VideoContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithPhotoContent(
username: Username,
initialFilter: CommonMessageFilter<PhotoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<PhotoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<PhotoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<PhotoContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithPhotoContent(
userId: UserId,
initialFilter: CommonMessageFilter<PhotoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<PhotoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<PhotoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<PhotoContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithPhotoContent(
user: User,
initialFilter: CommonMessageFilter<PhotoContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<PhotoContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<PhotoContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<PhotoContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithAnimationContent(
username: Username,
initialFilter: CommonMessageFilter<AnimationContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AnimationContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AnimationContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AnimationContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithAnimationContent(
userId: UserId,
initialFilter: CommonMessageFilter<AnimationContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AnimationContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AnimationContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AnimationContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithAnimationContent(
user: User,
initialFilter: CommonMessageFilter<AnimationContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AnimationContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<AnimationContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AnimationContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithTextContent(
username: Username,
initialFilter: CommonMessageFilter<TextContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextContent>>
) = onMention(username, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onTextMentionWithTextContent(
userId: UserId,
initialFilter: CommonMessageFilter<TextContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextContent>>
) = onTextMention(userId, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)
suspend fun <BC : BehaviourContext> BC.onMentionWithTextContent(
user: User,
initialFilter: CommonMessageFilter<TextContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextContent>, Update>? = null,
markerFactory: MarkerFactory<in CommonMessage<TextContent>, Any> = AnyMarkerFactory(),
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextContent>>
) = onMention(user, initialFilter, subcontextUpdatesFilter, markerFactory, scenarioReceiver)

View File

@@ -0,0 +1,6 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
actual var defaultCoroutineScopeProvider: () -> CoroutineScope = { CoroutineScope(Dispatchers.Default) }

View File

@@ -0,0 +1,6 @@
package dev.inmo.tgbotapi.extensions.behaviour_builder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
actual var defaultCoroutineScopeProvider: () -> CoroutineScope = { CoroutineScope(Dispatchers.Default) }

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"
@@ -25,6 +26,7 @@ kotlin {
api libs.microutils.serialization.base64
api libs.microutils.serialization.encapsulator
api libs.microutils.serialization.typedSerializer
api libs.microutils.serialization.mapper
api libs.microutils.ktor.common
api libs.microutils.languageCodes
@@ -46,11 +48,23 @@ kotlin {
api libs.javax.activation
}
}
linuxX64Main {
dependencies {
api libs.ktor.client.curl
}
}
mingwX64Main {
dependencies {
api libs.ktor.client.winhttp
}
}
}
}
dependencies {
add("kspJvm", project(":tgbotapi.ksp"))
add("kspCommonMainMetadata", project(":tgbotapi.ksp"))
}
ksp {

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.abstracts
import dev.inmo.tgbotapi.types.chat.Chat
/**
* All inheritors of this interface have [chat] field and related to this [chat]
*/
interface WithChat {
val chat: Chat
}

View File

@@ -5,5 +5,3 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
interface WithReplyMarkup {
val replyMarkup: KeyboardMarkup?
}
@Deprecated("Renamed", ReplaceWith("WithReplyMarkup", "dev.inmo.tgbotapi.abstracts.types.WithReplyMarkup"))
typealias ReplyMarkup = WithReplyMarkup

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.bot.exceptions
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.Response
import dev.inmo.tgbotapi.types.RetryAfterError
import io.ktor.utils.io.errors.IOException

View File

@@ -1,134 +1,25 @@
package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.exceptions.*
import dev.inmo.tgbotapi.bot.ktor.base.*
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.Response
import dev.inmo.tgbotapi.utils.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.*
import io.ktor.client.statement.bodyAsText
import io.ktor.client.statement.readText
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import io.ktor.client.*
import kotlinx.serialization.json.Json
@RiskFeature
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
SimpleRequestCallFactory(),
MultipartRequestCallFactory(),
DownloadFileRequestCallFactory,
DownloadFileChannelRequestCallFactory
)
class KtorRequestsExecutor(
/**
* Represents default [BaseRequestsExecutor] working on [Ktor](https://ktor.io) [HttpClient]
*
* * On JS, JVM and MingwX64 platforms it is [dev.inmo.tgbotapi.bot.ktor.base.DefaultKtorRequestsExecutor]
* * On LinuxX64 it is [dev.inmo.tgbotapi.bot.ktor.base.MultipleClientKtorRequestsExecutor]
*/
expect class KtorRequestsExecutor (
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) {
this + createTelegramBotDefaultKtorCallRequestsFactories()
} else {
this
}
}
private val client = client.config {
if (client.pluginOrNull(HttpTimeout) == null) {
install(HttpTimeout)
}
}
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit(request) {
var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) {
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
result = potentialFactory.makeCall(
client,
telegramAPIUrlsKeeper,
request,
jsonFormatter
)
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(result, request, potentialFactory)
if (result != null) {
factoryHandledRequest = potentialFactory
break
}
}
result ?.let {
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
}
}.let {
val result = it.exceptionOrNull() ?.let { e ->
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
when (e) {
is ClientRequestException -> {
val exceptionResult = runCatchingSafely {
val content = e.response.bodyAsText()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
}
exceptionResult.exceptionOrNull() ?.let {
CommonBotException(cause = e)
} ?: exceptionResult.getOrThrow()
}
is BotException -> e
else -> CommonBotException(cause = e)
}
} ?.let { Result.failure(it) } ?: it
pipelineStepsHolder.onRequestReturnResult(result, request, callsFactories)
}
}
override fun close() {
client.close()
}
}
class KtorRequestsExecutorBuilder(
var telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
) {
var client: HttpClient = HttpClient()
var callsFactories: List<KtorCallFactory> = emptyList()
var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)
}
inline fun telegramBot(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = KtorRequestsExecutorBuilder(telegramAPIUrlsKeeper).apply(builder).build()
/**
* Shortcut for [telegramBot]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun telegramBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), builder)
requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
jsonFormatter: Json = nonstrictJsonFormat,
pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor

View File

@@ -0,0 +1,54 @@
package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.ktor.base.*
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.utils.*
import io.ktor.client.HttpClient
import kotlinx.serialization.json.Json
@RiskFeature
fun createTelegramBotDefaultKtorCallRequestsFactories() = listOf(
SimpleRequestCallFactory(),
MultipartRequestCallFactory(),
DownloadFileRequestCallFactory,
DownloadFileChannelRequestCallFactory
)
class KtorRequestsExecutorBuilder(
var telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper
) {
var client: HttpClient = HttpClient()
var callsFactories: List<KtorCallFactory> = emptyList()
var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat
fun build() = KtorRequestsExecutor(
telegramAPIUrlsKeeper,
client,
callsFactories,
excludeDefaultFactories,
requestsLimiter,
jsonFormatter
)
}
inline fun telegramBot(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = KtorRequestsExecutorBuilder(telegramAPIUrlsKeeper).apply(builder).build()
/**
* Shortcut for [telegramBot]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun telegramBot(
token: String,
apiUrl: String = telegramBotAPIDefaultUrl,
testServer: Boolean = false,
builder: KtorRequestsExecutorBuilder.() -> Unit = {}
): TelegramBot = telegramBot(TelegramAPIUrlsKeeper(token, testServer, apiUrl), builder)

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.coroutines.safelyWithResult
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.requests.GetUpdates
@@ -56,7 +56,7 @@ abstract class AbstractRequestCallFactory : KtorCallFactory {
val content = response.bodyAsText()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
return safelyWithResult {
return runCatchingSafely {
(responseObject.result?.let {
jsonFormatter.decodeFromJsonElement(request.resultDeserializer, it)
} ?: response.let {

View File

@@ -0,0 +1,100 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.exceptions.BotException
import dev.inmo.tgbotapi.bot.exceptions.CommonBotException
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.ktor.createTelegramBotDefaultKtorCallRequestsFactories
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.Response
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.nonstrictJsonFormat
import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.statement.*
import kotlinx.serialization.json.Json
class DefaultKtorRequestsExecutor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) {
this + createTelegramBotDefaultKtorCallRequestsFactories()
} else {
this
}
}
private val client = client.config {
if (client.pluginOrNull(HttpTimeout) == null) {
install(HttpTimeout)
}
}
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit(request) {
var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) {
pipelineStepsHolder.onBeforeCallFactoryMakeCall(request, potentialFactory)
result = potentialFactory.makeCall(
client,
telegramAPIUrlsKeeper,
request,
jsonFormatter
)
result = pipelineStepsHolder.onAfterCallFactoryMakeCall(result, request, potentialFactory)
if (result != null) {
factoryHandledRequest = potentialFactory
break
}
}
result ?.let {
pipelineStepsHolder.onRequestResultPresented(it, request, factoryHandledRequest, callsFactories)
} ?: pipelineStepsHolder.onRequestResultAbsent(request, callsFactories) ?: error("Can't execute request: $request")
}
}.let {
val result = it.exceptionOrNull() ?.let { e ->
pipelineStepsHolder.onRequestException(request, e) ?.let { return@let it }
when (e) {
is ClientRequestException -> {
val exceptionResult = runCatchingSafely {
val content = e.response.bodyAsText()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
}
exceptionResult.exceptionOrNull() ?.let {
CommonBotException(cause = e)
} ?: exceptionResult.getOrThrow()
}
is BotException -> e
else -> CommonBotException(cause = e)
}
} ?.let { Result.failure(it) } ?: it
pipelineStepsHolder.onRequestReturnResult(result, request, callsFactories)
}
}
override fun close() {
client.close()
}
}

View File

@@ -4,8 +4,7 @@ import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.mapWithCommonValues
import io.ktor.client.HttpClient
import io.ktor.client.request.forms.MultiPartFormDataContent
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.*
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders

View File

@@ -0,0 +1,118 @@
package dev.inmo.tgbotapi.bot.ktor.base
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.ktor.KtorCallFactory
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.json.Json
/**
* This function is used in default constructor of [MultipleClientKtorRequestsExecutor] and on all non-native
* platforms and MingwX64 should return [client]
*
* On LinuxX64 it will create copy with Curl engine or throw an exception if engine is different with Curl
*
* @throws IllegalArgumentException When pass non Curl-based [HttpClient] on LinuxX64
*/
internal expect inline fun platformClientCopy(client: HttpClient): HttpClient
/**
* Will use its parameters of constructor to create several [DefaultKtorRequestsExecutor] and use them in [execute]
* and [close] operations
*
* This [BaseRequestsExecutor] has been created for LinuxX64 target due to its inability of requests paralleling
*
* Under the hood on each [execute] it will take [DefaultKtorRequestsExecutor] and mark it as busy, execute
* [Request], free up taken [DefaultKtorRequestsExecutor] and return (or throw) the result of execution
*
* @param requestExecutorsCount Amount of [DefaultKtorRequestsExecutor] which will be created and used under the
* hood
*/
class MultipleClientKtorRequestsExecutor (
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean,
requestsLimiter: RequestLimiter,
jsonFormatter: Json,
pipelineStepsHolder: KtorPipelineStepsHolder,
requestExecutorsCount: Int,
clientFactory: () -> HttpClient
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val requestExecutors = (0 until requestExecutorsCount).map {
DefaultKtorRequestsExecutor(
telegramAPIUrlsKeeper,
clientFactory(),
callsFactories,
excludeDefaultFactories,
requestsLimiter,
jsonFormatter,
pipelineStepsHolder
)
}.toSet()
private val freeClients = MutableStateFlow<Set<DefaultKtorRequestsExecutor>>(requestExecutors)
private val clientAllocationMutex = Mutex()
private val takerFlow = freeClients.mapNotNull {
clientAllocationMutex.withLock {
freeClients.value.firstOrNull() ?.also {
freeClients.value -= it
} ?: return@mapNotNull null
}
}
constructor(
telegramAPIUrlsKeeper: TelegramAPIUrlsKeeper,
client: HttpClient,
callsFactories: List<KtorCallFactory>,
excludeDefaultFactories: Boolean,
requestsLimiter: RequestLimiter,
jsonFormatter: Json,
pipelineStepsHolder: KtorPipelineStepsHolder
) : this(
telegramAPIUrlsKeeper,
callsFactories,
excludeDefaultFactories,
requestsLimiter,
jsonFormatter,
pipelineStepsHolder,
client.engineConfig.threadsCount,
{ platformClientCopy(client) }
)
private suspend fun prepareRequestsExecutor(): DefaultKtorRequestsExecutor {
return takerFlow.first()
}
private suspend fun freeRequestsExecutor(client: DefaultKtorRequestsExecutor) {
clientAllocationMutex.withLock {
freeClients.value += client
}
}
private suspend fun <T> withRequestExecutor(block: suspend (DefaultKtorRequestsExecutor) -> T): T {
val requestsExecutor = prepareRequestsExecutor()
val result = runCatchingSafely {
block(requestsExecutor)
}
freeRequestsExecutor(requestsExecutor)
return result.getOrThrow()
}
override suspend fun <T : Any> execute(request: Request<T>): T = withRequestExecutor {
it.execute(request)
}
override fun close() {
requestExecutors.forEach {
it.close()
}
}
}

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.bot.settings.limiters
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.types.MilliSeconds
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Semaphore
@@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlin.math.roundToLong
private fun now(): Long = DateTime.nowUnixLong()
private fun now(): Long = DateTime.nowUnixMillisLong()
@Serializable
class CommonLimiter(

View File

@@ -1,18 +1,26 @@
package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
import kotlinx.coroutines.delay
/**
* Simple limiter which will lock any request when [TooMuchRequestsExceptions] is thrown and rerun request after lock time
* Simple limiter which will lock any request when [TooMuchRequestsException] is thrown and rerun request after lock time
*/
object ExceptionsOnlyLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T {
return try {
block()
} catch (e: TooMuchRequestsException) {
delay(e.retryAfter.leftToRetry)
limit(block)
var result: Result<T>? = null
while (result == null || result.isFailure) {
result = runCatchingSafely {
block()
}.onFailure {
if (it is TooMuchRequestsException) {
delay(it.retryAfter.leftToRetry)
} else {
throw it
}
}
}
return result.getOrThrow()
}
}

View File

@@ -21,6 +21,11 @@ import kotlinx.serialization.encoding.Encoder
* @see ByteArray.asMultipartFile
* @see ByteReadChannel.asMultipartFile
* @see ByteReadChannelAllocator.asMultipartFile
*
* @see fromInput
* @see fromFile
* @see fromId
* @see fromUrl
*/
@Serializable(InputFileSerializer::class)
sealed class InputFile {
@@ -29,15 +34,32 @@ sealed class InputFile {
companion object {
operator fun invoke(file: MPPFile) = file.asMultipartFile()
/**
* Creates [MultipartFile] based on incoming [filename] and [inputSource]
*/
fun fromInput(filename: String, inputSource: () -> Input) = MultipartFile(filename, inputSource)
/**
* Creates [MultipartFile] based on incoming [MPPFile] (common File in java, for example)
*/
fun fromFile(file: MPPFile) = invoke(file)
/**
* Creates [FileId] from the incomming [id] [String] with believe that it is [FileId]
*/
fun fromId(id: String) = FileId(id)
/**
* Creates [FileUrl] from the incomming [url] [String]
*/
fun fromUrl(url: String) = FileUrl(url)
}
}
internal const val attachPrefix = "attach://"
internal inline val InputFile.attachFileId
get() = "attach://$fileId"
get() = "$attachPrefix$fileId"
internal inline val InputFile.fileIdToSend
get() = when (this) {
is FileId -> fileId
@@ -60,8 +82,8 @@ fun String.toInputFile() = FileId(this)
@RiskFeature
object InputFileSerializer : KSerializer<InputFile> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(FileId::class.toString(), PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: InputFile) = encoder.encodeString(value.fileId)
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString())
override fun serialize(encoder: Encoder, value: InputFile) = encoder.encodeString(value.fileIdToSend)
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString().removePrefix(attachPrefix))
}
// TODO:: add checks for files size

View File

@@ -23,11 +23,30 @@ data class AnswerInlineQuery(
val isPersonal: Boolean? = null,
@SerialName(nextOffsetField)
val nextOffset: String? = null,
@SerialName(switchPmTextField)
val switchPmText: String? = null,
@SerialName(switchPmParameterField)
val switchPmParameter: String? = null
@SerialName(buttonField)
val button: InlineQueryResultsButton? = null,
) : SimpleRequest<Boolean> {
constructor(
inlineQueryID: InlineQueryIdentifier,
results: List<InlineQueryResult> = emptyList(),
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String?,
switchPmParameter: String?
) : this(
inlineQueryID,
results,
cachedTime,
isPersonal,
nextOffset,
switchPmText ?.let {
switchPmParameter ?.let {
InlineQueryResultsButton.Start(switchPmText, switchPmParameter)
}
}
)
override fun method(): String = "answerInlineQuery"
override val resultDeserializer: DeserializationStrategy<Boolean>
get() = Boolean.serializer()
@@ -40,8 +59,23 @@ fun InlineQuery.createAnswer(
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String? = null,
switchPmParameter: String? = null
button: InlineQueryResultsButton? = null,
) = AnswerInlineQuery(
id,
results,
cachedTime,
isPersonal,
nextOffset,
button
)
fun InlineQuery.createAnswer(
results: List<InlineQueryResult> = emptyList(),
cachedTime: Int? = null,
isPersonal: Boolean? = null,
nextOffset: String? = null,
switchPmText: String?,
switchPmParameter: String?
) = AnswerInlineQuery(
id,
results,

View File

@@ -0,0 +1,89 @@
package dev.inmo.tgbotapi.requests.answers
import dev.inmo.micro_utils.common.Warning
import dev.inmo.tgbotapi.types.StartParameter
import dev.inmo.tgbotapi.types.startParameterField
import dev.inmo.tgbotapi.types.textField
import dev.inmo.tgbotapi.types.webAppField
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.internal.ClassCastsIncluded
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable(InlineQueryResultsButtonSerializer::class)
@ClassCastsIncluded
sealed interface InlineQueryResultsButton {
val text: String
@Serializable
class Raw internal constructor(
@SerialName(textField)
val text: String,
@SerialName(webAppField)
val webAppInfo: WebAppInfo? = null,
@SerialName(startParameterField)
val deepLinkParameter: String? = null
)
@Serializable(InlineQueryResultsButtonSerializer::class)
data class WebApp(
@SerialName(textField)
override val text: String,
@SerialName(webAppField)
val webAppInfo: WebAppInfo
) : InlineQueryResultsButton
@Serializable(InlineQueryResultsButtonSerializer::class)
data class Start(
@SerialName(textField)
override val text: String,
@SerialName(startParameterField)
val deepLinkParameter: String
) : InlineQueryResultsButton
@Serializable(InlineQueryResultsButtonSerializer::class)
data class Unknown internal constructor (
@SerialName(textField)
override val text: String
) : InlineQueryResultsButton
companion object {
operator fun invoke(
text: String,
deepLinkParameter: String
) = Start(text, deepLinkParameter)
operator fun invoke(
text: String,
webAppInfo: WebAppInfo
) = WebApp(text, webAppInfo)
}
}
object InlineQueryResultsButtonSerializer : KSerializer<InlineQueryResultsButton> {
override val descriptor: SerialDescriptor = InlineQueryResultsButton.Raw.serializer().descriptor
override fun deserialize(decoder: Decoder): InlineQueryResultsButton {
val raw = InlineQueryResultsButton.Raw.serializer().deserialize(decoder)
return when {
raw.webAppInfo != null -> InlineQueryResultsButton.WebApp(raw.text, raw.webAppInfo)
raw.deepLinkParameter != null -> InlineQueryResultsButton.Start(raw.text, raw.deepLinkParameter)
else -> InlineQueryResultsButton.Unknown(raw.text)
}
}
override fun serialize(encoder: Encoder, value: InlineQueryResultsButton) {
InlineQueryResultsButton.Raw.serializer().serialize(
encoder,
InlineQueryResultsButton.Raw(
value.text,
(value as? InlineQueryResultsButton.WebApp)?.webAppInfo,
(value as? InlineQueryResultsButton.Start)?.deepLinkParameter,
)
)
}
}

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 GetMyName(
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<BotName>, WithOptionalLanguageCode {
override fun method(): String = "getMyName"
override val resultDeserializer: DeserializationStrategy<BotName>
get() = BotName.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 SetMyName(
@SerialName(nameField)
val name: String? = null,
@SerialName(languageCodeField)
@Serializable(IetfLanguageCodeSerializer::class)
override val ietfLanguageCode: IetfLanguageCode? = null
) : SimpleRequest<Boolean>, WithOptionalLanguageCode {
override fun method(): String = "setMyName"
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

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.requests.chat.abstracts
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.types.*
import kotlinx.serialization.DeserializationStrategy

View File

@@ -19,7 +19,7 @@ data class GetChat(
override val resultDeserializer: DeserializationStrategy<ExtendedChat> = if (chatId is ChatIdWithThreadId) {
ExtendedChatSerializer.BasedOnForumThread(chatId.threadId)
} else {
ExtendedChatSerializer
ExtendedChatSerializer.Companion
}
override val requestSerializer: SerializationStrategy<*>
get() = serializer()

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.requests.chat.invite_links
import com.soywiz.klock.DateTime
import korlibs.time.DateTime
import dev.inmo.tgbotapi.requests.abstracts.SimpleRequest
import dev.inmo.tgbotapi.requests.chat.abstracts.*
import dev.inmo.tgbotapi.types.*

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