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

Compare commits

..

99 Commits

Author SHA1 Message Date
a87c21273d BehaviourContext#on*Message extensions 2022-04-17 00:22:00 +06:00
ff2d1615da replace TODO for pipelineStepsHolder with default realization 2022-04-16 23:58:24 +06:00
08ef146daa Update gradle.properties 2022-04-13 23:12:32 +06:00
a404a6c59c fixes in DownloadFIleChannelRequestCallFactory 2022-04-11 21:56:47 +06:00
1089c716f3 deprecate StorageFileInfo 2022-04-11 21:00:56 +06:00
81ad55b19f add KtorPipelineStepsHolder#onRequestReturnResult 2022-04-11 14:41:02 +06:00
863c872f35 start to add pipelines and fix mention creation 2022-04-11 12:46:33 +06:00
ece6a917ed start 0.38.13 2022-04-11 11:59:37 +06:00
12248aa702 Merge pull request #555 from InsanusMokrassar/0.38.12
0.38.12
2022-04-09 12:24:57 +06:00
83ea4f6435 Update gradle.properties 2022-04-08 09:49:06 +06:00
52204d8df1 Experemtally update kotlin 2022-04-08 09:44:37 +06:00
81b9ccdf6a Update VideoContent.kt 2022-04-08 09:29:07 +06:00
fa74f12505 Update CHANGELOG.md 2022-04-08 02:09:22 +06:00
be284c4d96 Update dependencies 2022-04-08 02:08:21 +06:00
1769fcacfd update gradle 2022-04-06 11:45:08 +06:00
4207d89c92 accumulated updates improvements 2022-04-04 13:16:18 +06:00
87ee2f280b remove redundant TextedMediaInputMediaContent 2022-04-03 19:56:34 +06:00
3d458b2dc6 fixes in builds and filling up functionality related to new classes 2022-04-03 19:48:48 +06:00
ee1c7c6533 TextedMediaGroupMediaInput 2022-04-03 19:37:56 +06:00
97d122c770 TextedMediaContent fixes 2022-04-03 19:29:48 +06:00
49da0faf45 replies with texted content 2022-04-03 19:21:38 +06:00
d426cb1210 TextedMediaContent 2022-04-03 19:06:27 +06:00
af83ac79e7 start 0.38.12 2022-04-03 18:35:17 +06:00
602f15c206 Update README.md 2022-04-01 00:01:25 +06:00
c2f40534e6 Update README.md 2022-03-31 23:55:34 +06:00
5693d4d808 Merge pull request #548 from InsanusMokrassar/0.38.11
0.38.11
2022-03-29 23:40:04 +06:00
9fe55aa6ef Update CHANGELOG.md 2022-03-29 12:42:02 +06:00
8f817f1492 reply with any media file 2022-03-29 12:37:54 +06:00
0fd146655d old matrix and row deprecation 2022-03-29 12:27:03 +06:00
94e5f74a90 reply with content 2022-03-29 12:20:52 +06:00
b9bffbb71b update dependencies 2022-03-27 13:47:38 +06:00
5ec5c3bdfd Fixes in "TextSourcesList" creating in from "RawMessageEntities" 2022-03-27 13:44:41 +06:00
a5ed2a82bb start 0.38.11 2022-03-27 13:36:21 +06:00
1b7dd11e75 Merge pull request #545 from InsanusMokrassar/0.38.10
0.38.10
2022-03-25 10:36:21 +06:00
8d0307fb7f update github building yml 2022-03-24 16:49:08 +06:00
c500f0a281 update reply and sendPhoto with PhotoSize 2022-03-24 16:47:07 +06:00
b3abeaa5ae Update packages_publishing.yml 2022-03-23 17:03:08 +06:00
8930a66134 with*Action block called once 2022-03-23 11:48:20 +06:00
76a2a2ca74 start 0.38.10 2022-03-23 05:06:48 +06:00
7dff8af460 Update gradle.properties 2022-03-23 05:01:09 +06:00
5261ab1cf0 start 0.38.10 2022-03-23 05:00:28 +06:00
f45c653fca Merge pull request #544 from InsanusMokrassar/0.38.9
0.38.9
2022-03-22 19:06:29 +06:00
5e03098ca8 fill changelog 2022-03-22 19:06:02 +06:00
2a3d8e6e73 publication scripts update 2022-03-22 16:26:18 +06:00
8c3dac7932 signature clashes fixes 2022-03-21 23:23:19 +06:00
83fb7f7bcb add opportunity to use several bots in one to serve bots requests 2022-03-21 23:06:23 +06:00
459414e7dd add RistFeature to SerializationModule 2022-03-21 17:03:37 +06:00
508d51f5a9 reply with copyMessage 2022-03-20 12:14:26 +06:00
36f22579b2 add MessageContent.Companion#serializationModule 2022-03-20 11:52:29 +06:00
d3d5f7f2e6 start 0.38.9 2022-03-20 11:50:52 +06:00
1369680419 Merge pull request #543 from InsanusMokrassar/0.38.8
0.38.8
2022-03-19 21:15:03 +06:00
f6e819be62 simpleFilter to list wrapper 2022-03-19 12:49:14 +06:00
553da0a3ee update dependencies and fill changelog 2022-03-19 12:27:46 +06:00
bace2724da a little bit refactor MediaGroupTriggers.kt 2022-03-19 10:15:44 +06:00
be710612b5 Update MediaGroupTriggers.kt 2022-03-19 09:55:48 +06:00
ea19c10f41 start 0.38.8 2022-03-18 19:18:34 +06:00
1b88881c62 Update README.md 2022-03-14 02:50:38 +06:00
be62c4fe37 Merge pull request #540 from InsanusMokrassar/0.38.7
0.38.7
2022-03-12 14:32:20 +06:00
d3499cdde1 update microutils and fill changelog 2022-03-12 13:41:49 +06:00
01d04f9d2b Update gradle-wrapper.properties 2022-03-11 22:31:19 +06:00
d290992e40 Update gradle.properties 2022-03-11 22:30:52 +06:00
867bdb8bd7 Update CHANGELOG.md 2022-03-08 00:18:25 +06:00
5f0a67187f add default votes in SimplePollOption 2022-03-08 00:15:29 +06:00
d9638849e2 update dependencies 2022-03-07 12:44:55 +06:00
5568720b5b start 0.38.7 2022-03-07 12:44:08 +06:00
c0c591fe08 Merge pull request #535 from InsanusMokrassar/0.38.6
0.38.6
2022-02-27 22:22:04 +06:00
3c1f8421c9 Update CHANGELOG.md 2022-02-27 10:04:28 +06:00
35b20d102c Update gradle.properties 2022-02-26 22:01:41 +06:00
70e6dc8fc9 update dependencies 2022-02-19 23:07:30 +06:00
4f0de376ab MentionTextSource#username 2022-02-19 23:02:58 +06:00
10c52c695c start 0.38.6 2022-02-19 22:54:41 +06:00
2d2c745527 Merge pull request #532 from InsanusMokrassar/0.38.5
0.38.5
2022-02-08 16:53:27 +06:00
1238843bde update dependencies 2022-02-08 16:50:38 +06:00
1ebaa1c387 deeplinking fixes and additions 2022-02-07 18:08:06 +06:00
b2bc47ec04 start 0.38.5 2022-02-07 18:00:13 +06:00
514620b2bd Merge pull request #530 from InsanusMokrassar/0.38.4
0.38.4
2022-02-02 10:05:11 +06:00
054f06e18b Update gradle.properties 2022-02-01 23:39:07 +06:00
00d467d091 classcasts and raw fields updates 2022-02-01 23:25:22 +06:00
6426ed6571 fixes 2022-02-01 23:05:47 +06:00
fba6707f44 fixes in create sticker set methods 2022-02-01 17:59:23 +06:00
0c2fe6bce7 support of Telegram Bot API 5.7 2022-02-01 16:23:26 +06:00
eee9fe439f fix of #529 2022-01-31 17:52:01 +06:00
3547c09383 start 0.38.4 2022-01-31 17:48:10 +06:00
956f02d26d Merge pull request #526 from InsanusMokrassar/0.38.3
0.38.3
2022-01-14 16:47:49 +06:00
468fc0a49b Update CHANGELOG.md 2022-01-14 16:31:57 +06:00
441f3cee3d Update gradle.properties 2022-01-14 16:31:19 +06:00
1ea79b9f7f update changelog 2022-01-13 19:01:17 +06:00
094f88867d Update gradle.properties 2022-01-13 13:52:49 +06:00
85a8a8eedc update dokka version 2022-01-12 13:02:23 +06:00
28fce0b4c1 New extensions "TelegramBot#send*" for media groups with contents 2022-01-12 11:39:35 +06:00
2f5fdcdca0 start 0.38.3 2022-01-12 11:37:34 +06:00
21799d7e4a add exceptions handling readme 2022-01-11 21:22:10 +06:00
ca409db0ee Merge pull request #525 from InsanusMokrassar/0.38.2
0.38.2
2022-01-10 23:08:50 +06:00
33c8ee0803 remove redundant copyMessage 2022-01-10 13:48:00 +06:00
0c0ec22348 update microutils dependency 2022-01-10 13:16:50 +06:00
24bffbbd97 addopportunity to copy messages using just content 2022-01-10 13:15:10 +06:00
a615d1c4fd copyMessages for media groups 2022-01-10 13:06:18 +06:00
a76b7977b3 start 0.38.2 2022-01-10 12:38:48 +06:00
6004879aef Merge pull request #524 from InsanusMokrassar/0.38.1
0.38.1
2022-01-07 13:37:06 +06:00
126 changed files with 2841 additions and 1003 deletions

View File

@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 1.8
java-version: 11
- name: Build
run: ./gradlew dokkaHtml
- name: Publish KDocs

View File

@@ -7,10 +7,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Fix android 31.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/31.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
java-version: 11
- name: Rewrite version
run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"
@@ -21,7 +18,7 @@ jobs:
run: ./gradlew build
- name: Publish
continue-on-error: true
run: ./gradlew publishAllPublicationsToGithubPackagesRepository --no-parallel -x signJsPublication -x signJvmPublication -x signKotlinMultiplatformPublication
run: ./gradlew publishAllPublicationsToGithubPackagesRepository --no-parallel
env:
GITHUBPACKAGES_USER: ${{ github.actor }}
GITHUBPACKAGES_PASSWORD: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,5 +1,132 @@
# TelegramBotAPI changelog
## 0.38.13
* `Core`:
* Fixes in `mention` creation
* Deprecate `StorageFileInfo`
* `BehaviourBuilder`:
* In the expectations a lot of `on*Message` extensions have been added (like `onContentMessage`). These extensions could be useful when with the `Content` its message info is important
## 0.38.12
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.17` -> `0.9.19`
* `Coroutines`: `1.6.0` -> `1.6.1`
* `Core`:
* New type `TextedMediaContent` which will unite `TextedInput` and `MediaContent`
* `MediaGroupContent` and all subsequent inheritors have been replaced to the package `dev.inmo.tgbotapi.types.message.content.media`
* `MediaGroupContent` Now extends `TextedMediaContent` instead of `MediaContent`
* Add `reply` functions with the texted content with including of text
* `Utils`:
* Improve work with retrieving of accumulated updates
## 0.38.11
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.16` -> `0.9.17`
* `Klock`: `2.6.3` -> `2.7.0`
* `Core`:
* Fixes in `TextSourcesList` creating in from `RawMessageEntities`
* Old ways to create keyboards (`matrix` and `row`) have been deprecated
* `API`:
* Add ability to `reply` with `Poll`
* Add ability to `reply` with any `MessageContent`
* Add ability to `reply` with any `TelegramMediaFile`
## 0.38.10
* `API`:
* All `with*Action` extensions got a contracts which declare that `block` will be called once
* Add several extensions `TelegramBot#sendPhoto` with `PhotoSize`
* Add several extensions `TelegramBot#reply` with `PhotoSize`
## 0.38.9
* `Core`:
* New function `MessageContent.Companion#serializationModule`
* Now it is possible to create `TelegramBot` (`RequestsExecutor`) with several bots under the hood and opportunity
for bots requests load balancing or fault-fix via sending of the requests via another bot
* `API`:
* Add replies which will use another message as a source for reply (`copyMessage`)
## 0.38.8
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.12` -> `0.9.16`
* `Klock`: `2.6.2` -> `2.6.3`
* `Ktor`: `1.6.7` -> `1.6.8`
* `BehaviourBuilder`:
* Fixes in `onMediaGroup` and dependent functions
* Add several new extensions for `SimpleFilter`:
* `SimpleFilter#listAll`
* `SimpleFilter#listAny`
* `SimpleFilter#listNone`
## 0.38.7
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.9` -> `0.9.12`
* `Klock`: `2.5.2` -> `2.6.2`
* `Core`:
* `SimplePollOption#votes` now is `0` by default
* New function `PollOption.Companion#simple`
## 0.38.6
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.6` -> `0.9.9`
* `Klock`: `2.4.13` -> `2.5.2`
* `Core`:
* New member of `MentionTextSource` - `username`
## 0.38.5
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.5` -> `0.9.6`
* `Core`:
* `Username` got new property `usernameWithoutAt` which will return `username` without leading `@`
* `Utils`:
* Several new functions for working with deep links:
* `makeUsernameDeepLinkPrefix`
* `makeTelegramDeepLink`
* `makeDeepLink`
## 0.38.4
__This update contains including of [Telegram Bot API 5.7](https://core.telegram.org/bots/api-changelog#january-31-2022)__
* `Core`:
* Support of new fields `Sticker`
* Support of new fields `StickerSet`
* Support of new fields in creating of sticker set and sticker
* `Utils`:
* Rename `PathedFile` to avoid clash with core file (fix of [#529](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/529))
## 0.38.3
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.1` -> `0.9.2`
* `Klock`: `2.4.10` -> `2.4.12`
* `UUID`: `0.3.1` -> `0.4.0`
* `API`
* New extensions `TelegramBot#send*` for media groups with contents
## 0.38.2
* `Common`:
* `Version`:
* `MicroUtils`: `0.9.0` -> `0.9.1`
* `API`
* New extensions `TelegramBot#copyMessages` for media groups
## 0.38.1
* `Core`:

View File

@@ -1,8 +1,8 @@
# 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-5.6-blue)](https://core.telegram.org/bots/api-changelog#december-30-2021)
# 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-5.7-blue)](https://core.telegram.org/bots/api-changelog#january-31-2022)
| [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Build Status](https://github.com/InsanusMokrassar/TelegramBotAPI/workflows/Build/badge.svg)](https://github.com/InsanusMokrassar/TelegramBotAPI/actions) [![Small survey](https://img.shields.io/static/v1?label=Google&message=Survey&color=blue)](https://forms.gle/2Hex2ynbHWHhi1KY7) [![Chat in Telegram](https://img.shields.io/static/v1?label=Telegram&message=Chat&color=blue)](https://t.me/InMoTelegramBotAPI) |
| [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Build Status](https://github.com/InsanusMokrassar/TelegramBotAPI/workflows/Build/badge.svg)](https://github.com/InsanusMokrassar/TelegramBotAPI/actions) [![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) [![Chat in Telegram](https://img.shields.io/static/v1?label=Telegram&message=Chat&color=blue&logo=telegram)](https://t.me/InMoTelegramBotAPI) |
|:---:|
| [![Create bot](https://img.shields.io/static/v1?label=Github&message=Template&color=blue)](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template/generate) [![Examples](https://img.shields.io/static/v1?label=Github&message=Examples&color=blue)](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/) [![KDocs](https://img.shields.io/static/v1?label=Dokka&message=KDocs&color=blue)](https://tgbotapi.inmo.dev/index.html) [![Mini tutorial](https://img.shields.io/static/v1?label=Bookstack&message=Tutorial&color=blue)](https://bookstack.inmo.dev/books/telegrambotapi/chapter/introduction-tutorial) |
| [![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/) [![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) |
Hello! This is a set of libraries for working with Telegram Bot API.
@@ -28,7 +28,7 @@ Other configuration examples:
suspend fun main() {
val bot = telegramBot(TOKEN)
bot.buildBehaviour {
bot.buildBehaviourWithLongPolling {
println(getMe())
onCommand("start") {
@@ -69,7 +69,7 @@ and maybe some updates it got after launch)
suspend fun main() {
val bot = telegramBot(TOKEN)
bot.buildBehaviour {
bot.buildBehaviourWithLongPolling {
println(getMe())
val nameReplyMarkup = ReplyKeyboardMarkup(

View File

@@ -1,3 +1 @@
dokka_version=1.6.0
org.gradle.jvmargs=-Xmx1024m

View File

@@ -6,18 +6,20 @@ kotlin.incremental=true
kotlin.incremental.js=true
kotlin_version=1.6.10
kotlin_coroutines_version=1.6.0
kotlin_coroutines_version=1.6.1
kotlin_serialisation_runtime_version=1.3.2
klock_version=2.4.10
uuid_version=0.3.1
ktor_version=1.6.7
klock_version=2.7.0
uuid_version=0.4.0
ktor_version=1.6.8
micro_utils_version=0.9.0
micro_utils_version=0.9.20
javax_activation_version=1.1.1
dokka_version=1.6.10
library_group=dev.inmo
library_version=0.38.1
library_version=0.38.13
github_release_plugin_version=2.2.12

View File

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

View File

@@ -9,8 +9,8 @@ publishing {
artifact javadocsJar
pom {
description = "This project just include all subproject of TelegramBotAPI"
name = "Telegram Bot API"
description = "${project.description}"
name = "${project.name}"
url = "https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI"
scm {

1
publish.kpsb Normal file
View File

@@ -0,0 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"${project.description}","url":"https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -0,0 +1,97 @@
# Exceptions handling
Unfortunatelly, exceptions handling in this library is a bit difficult in some places, but that have at least two reasons: flexibility and usability.
## "In place" handling
In case you know, where exceptions are happening, you may use several tools for exceptions catching:
* Catching with result
* Catching with callback
### Catching with result
If you prefer to receive `Result` objects instead of some weird callbacks, you may use the next syntax:
```kotlin
safelyWithResult {
// do something
}.onSuccess { // will be called if everything is right
// handle success
}.onFailure { // will be called if something went wrong
// handle error
it.printStackTrace()
}.getOrThrow() // will return value or throw exception
```
### Catching with callback
Also there is more simple (in some cases) way to handle exceptions with callbacks:
```kotlin
safely(
{
// handle error
it.printStackTrace()
null // return value
}
) {
// do something
}
```
### Bonus: different types of handling
There are two types of handling:
* Just safely - when you are using something to obviously retrieve value or throw exception. When handling callback has been skipped, it will throw exception by default. For example:
```kotlin
safely(
{
it.printStackTrace()
"error"
}
) {
error("Hi :)") // emulate exception throwing
"ok"
} // result will be with type String
```
* Safely without exceptions - almost the same as `safely`, but this type by default allow to return nullable value (when exception was thrown) instead of just throwing (as with `safely`):
```kotlin
safelyWithouExceptions {
// do something
} // will returns nullable result type
```
## Global exceptions handling
The most simple way to configure exceptions handling is to change `CoroutineContext` when you are creating your `CoroutineScope` for bot processing:
```kotlin
val bot = telegramBot("TOKEN")
bot.buildBehaviour (
scope = scope,
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
// ...
}
```
OR
```kotlin
val bot = telegramBotWithBehaviour (
"TOKEN",
scope = scope,
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
// ...
}
```
Here we have used `ContextSafelyExceptionHandler` class. It will pass default handling of exceptions and will call the block in most cases when something inside of your bot logic has thrown exception.

View File

@@ -17,8 +17,9 @@ plugins {
project.version = "$library_version"
project.group = "$library_group"
project.description = "API extensions with \"Telegram Bot API\"-like extensions for TelegramBot and RequestsExecutor"
apply from: "publish.gradle"
apply from: "../publish.gradle"
repositories {
mavenLocal()

View File

@@ -1,79 +0,0 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "API extensions which provide work with RequestsExecutor of TelegramBotAPI almost like it is described in original Telegram Bot API reference"
name = "Telegram Bot API Extensions for API"
url = "https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI-extensions-api"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/TelegramBotAPI.git[push=]https://github.com/insanusmokrassar/TelegramBotAPI.git"
url = "https://github.com/insanusmokrassar/TelegramBotAPI.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
repositories {
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
}

View File

@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot API Extensions for API","description":"API extensions which provide work with RequestsExecutor of TelegramBotAPI almost like it is described in original Telegram Bot API reference","url":"https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI-extensions-api","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.extensions.api.get
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.get.GetStickerSet
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
suspend fun TelegramBot.getStickerSet(
name: String

View File

@@ -0,0 +1,173 @@
package dev.inmo.tgbotapi.extensions.api.send
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.send.media.sendMediaGroup
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChatId: ChatIdentifier,
messages: List<MediaGroupMessage<MediaGroupContent>>,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
): List<MediaGroupMessage<MediaGroupContent>> {
val first = messages.first().content.toMediaGroupMemberInputMedia().let {
if (text != null) {
when (it) {
is InputMediaAudio -> it.copy(text = text, parseMode = parseMode)
is InputMediaDocument -> it.copy(text = text, parseMode = parseMode)
is InputMediaPhoto -> it.copy(text = text, parseMode = parseMode)
is InputMediaVideo -> it.copy(text = text, parseMode = parseMode)
}
} else {
it
}
}
return sendMediaGroup(
toChatId,
listOf(first) + messages.drop(1).map {
it.content.toMediaGroupMemberInputMedia()
},
disableNotification,
protectContent,
replyToMessageId,
allowSendingWithoutReply
)
}
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: Chat,
messages: List<MediaGroupMessage<MediaGroupContent>>,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat.id, messages, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: ChatIdentifier,
update: SentMediaGroupUpdate,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat, update.data, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: Chat,
update: SentMediaGroupUpdate,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat.id, update, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChatId: ChatIdentifier,
messages: List<MediaGroupMessage<MediaGroupContent>>,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
): List<MediaGroupMessage<MediaGroupContent>> {
val first = messages.first().content.toMediaGroupMemberInputMedia().let {
when (it) {
is InputMediaAudio -> InputMediaAudio(it.file, entities, it.duration, it.performer, it.title, it.thumb)
is InputMediaDocument -> InputMediaDocument(it.file, entities, it.thumb, it.disableContentTypeDetection)
is InputMediaPhoto -> InputMediaPhoto(it.file, entities)
is InputMediaVideo -> InputMediaVideo(it.file, entities, it.width, it.height, it.duration, it.thumb)
}
}
return sendMediaGroup(
toChatId,
listOf(first) + messages.drop(1).map {
it.content.toMediaGroupMemberInputMedia()
},
disableNotification,
protectContent,
replyToMessageId,
allowSendingWithoutReply
)
}
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: Chat,
messages: List<MediaGroupMessage<MediaGroupContent>>,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat.id, messages, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: ChatIdentifier,
update: SentMediaGroupUpdate,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat, update.data, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)
/**
* Send media group via [sendMediaGroup] extension with edited [entities] of first [messages] element. Other elements
* will be copied as they are
*/
suspend inline fun TelegramBot.copyMessages(
toChat: Chat,
update: SentMediaGroupUpdate,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = copyMessages(toChat.id, update, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply)

View File

@@ -10,15 +10,22 @@ import dev.inmo.tgbotapi.requests.abstracts.InputFile
import dev.inmo.tgbotapi.requests.send.media.rawSendingMediaGroupsWarning
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSource
import dev.inmo.tgbotapi.types.MessageEntity.textsources.TextSourcesList
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.dice.DiceAnimationType
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.files.abstracts.TelegramMediaFile
import dev.inmo.tgbotapi.types.files.sticker.Sticker
import dev.inmo.tgbotapi.types.games.Game
import dev.inmo.tgbotapi.types.location.StaticLocation
import dev.inmo.tgbotapi.types.location.*
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.payments.LabeledPrice
import dev.inmo.tgbotapi.types.payments.abstracts.Currency
import dev.inmo.tgbotapi.types.polls.*
@@ -116,6 +123,7 @@ suspend inline fun TelegramBot.reply(
longitude: Double,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(
to.chat,
@@ -123,6 +131,7 @@ suspend inline fun TelegramBot.reply(
longitude,
disableNotification,
protectContent,
allowSendingWithoutReply,
to.messageId,
replyMarkup
)
@@ -136,12 +145,14 @@ suspend inline fun TelegramBot.reply(
location: StaticLocation,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(
to.chat,
location,
disableNotification,
protectContent,
allowSendingWithoutReply,
to.messageId,
replyMarkup
)
@@ -569,6 +580,17 @@ suspend inline fun TelegramBot.reply(
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(to.chat, photo, text, parseMode, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
photoSize: PhotoSize,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(to.chat, photoSize, text, parseMode, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.replyWithPhoto(
to: Message,
@@ -590,6 +612,16 @@ suspend inline fun TelegramBot.reply(
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(to.chat, photo, entities, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
photoSize: PhotoSize,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(to.chat, photoSize, entities, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
// Sticker
@@ -863,3 +895,329 @@ suspend inline fun TelegramBot.reply(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendQuizPoll(to.chat, isClosed, quizPoll, question, options, correctOptionId, isAnonymous, entities, closeInfo, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
poll: Poll,
isClosed: Boolean = false,
question: String = poll.question,
options: List<String> = poll.options.map { it.text },
isAnonymous: Boolean = poll.isAnonymous,
closeInfo: ScheduledCloseInfo? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = when (poll) {
is RegularPoll -> reply(
to = to,
poll = poll,
isClosed = isClosed,
question = question,
options = options,
isAnonymous = isAnonymous,
allowMultipleAnswers = isAnonymous,
closeInfo = closeInfo,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is UnknownPollType -> error("Unable to send poll with unknown type ($poll)")
is QuizPoll -> reply(
to = to,
quizPoll = poll,
entities = poll.textSources,
isClosed = isClosed,
question = question,
options = options,
isAnonymous = isAnonymous,
closeInfo = closeInfo,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
}
suspend inline fun TelegramBot.reply(
to: Message,
fromChatId: ChatIdentifier,
messageId: MessageIdentifier,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = copyMessage(fromChatId, to.chat.id, messageId, text, parseMode, disableNotification, protectContent, to.messageId, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
fromChat: Chat,
messageId: MessageIdentifier,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = reply(to, fromChat.id, messageId, text, parseMode, disableNotification, protectContent, allowSendingWithoutReply, replyMarkup)
suspend inline fun TelegramBot.reply(
to: Message,
copy: Message,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = reply(to, copy.chat.id, copy.messageId, text, parseMode, disableNotification, protectContent, allowSendingWithoutReply, replyMarkup)
suspend fun TelegramBot.reply(
to: Message,
content: MessageContent,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) {
execute(
content.createResend(
to.chat.id,
disableNotification,
protectContent,
to.messageId,
allowSendingWithoutReply,
replyMarkup
)
)
}
suspend fun TelegramBot.reply(
to: Message,
mediaFile: TelegramMediaFile,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) {
when (mediaFile) {
is AudioFile -> reply(
to = to,
audio = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is AnimationFile -> reply(
to = to,
animation = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is VoiceFile -> reply(
to = to,
voice = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is VideoFile -> reply(
to = to,
video = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is VideoNoteFile -> reply(
to = to,
videoNote = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is DocumentFile -> reply(
to = to,
document = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is Sticker -> reply(
to = to,
sticker = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is PhotoSize -> reply(
to = to,
photoSize = mediaFile,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
else -> reply(
to = to,
document = mediaFile.asDocumentFile(),
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
}
}
suspend fun TelegramBot.reply(
to: Message,
content: TextedMediaContent,
text: String?,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) {
when (content) {
is VoiceContent -> reply(
to = to,
voice = content.media,
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is AudioMediaGroupContent -> reply(
to = to,
audio = content.media,
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is PhotoContent -> reply(
to = to,
photoSize = content.media,
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is VideoContent -> reply(
to = to,
video = content.media,
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is AnimationContent -> reply(
to = to,
animation = content.media,
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
else -> reply(
to = to,
document = content.media.asDocumentFile(),
text = text,
parseMode = parseMode,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
}
}
suspend fun TelegramBot.reply(
to: Message,
content: TextedMediaContent,
entities: List<TextSource>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) {
when (content) {
is VoiceContent -> reply(
to = to,
voice = content.media,
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is AudioMediaGroupContent -> reply(
to = to,
audio = content.media,
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is PhotoContent -> reply(
to = to,
photoSize = content.media,
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is VideoContent -> reply(
to = to,
video = content.media,
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
is AnimationContent -> reply(
to = to,
animation = content.media,
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
else -> reply(
to = to,
document = content.media.asDocumentFile(),
entities = entities,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyMarkup = replyMarkup
)
}
}

View File

@@ -8,15 +8,20 @@ import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.actions.*
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import kotlinx.coroutines.*
import kotlin.contracts.*
import kotlin.coroutines.coroutineContext
private const val refreshTime: MilliSeconds = (botActionActualityTime - 1) * 1000L
typealias TelegramBotActionCallback<T> = suspend TelegramBot.() -> T
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withAction(
actionRequest: SendAction,
block: TelegramBotActionCallback<T>
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val botActionJob = CoroutineScope(coroutineContext).launch {
while (isActive) {
delay(refreshTime)
@@ -30,46 +35,190 @@ suspend fun <T> TelegramBot.withAction(
return result.getOrThrow()
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withAction(
chatId: ChatId,
action: BotAction,
block: TelegramBotActionCallback<T>
) = withAction(
SendAction(chatId, action),
block
)
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(
SendAction(chatId, action),
block
)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withAction(
chat: Chat,
action: BotAction,
block: TelegramBotActionCallback<T>
) = withAction(
chat.id,
action,
block
)
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(
chat.id,
action,
block
)
}
suspend fun <T> TelegramBot.withTypingAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, TypingAction, block)
suspend fun <T> TelegramBot.withUploadPhotoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadPhotoAction, block)
suspend fun <T> TelegramBot.withRecordVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordVideoAction, block)
suspend fun <T> TelegramBot.withUploadVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadVideoAction, block)
suspend fun <T> TelegramBot.withRecordVoiceAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordVoiceAction, block)
suspend fun <T> TelegramBot.withUploadVoiceAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadVoiceAction, block)
suspend fun <T> TelegramBot.withUploadDocumentAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadDocumentAction, block)
suspend fun <T> TelegramBot.withFindLocationAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, FindLocationAction, block)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, RecordVideoNoteAction, block)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, UploadVideoNoteAction, block)
suspend fun <T> TelegramBot.withChooseStickerAction(chatId: ChatId, block: TelegramBotActionCallback<T>) = withAction(chatId, ChooseStickerAction, block)
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withTypingAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, TypingAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadPhotoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, UploadPhotoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, RecordVideoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVideoAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, UploadVideoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVoiceAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, RecordVoiceAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVoiceAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, UploadVoiceAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadDocumentAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, UploadDocumentAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withFindLocationAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, FindLocationAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, RecordVideoNoteAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, UploadVideoNoteAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withChooseStickerAction(chatId: ChatId, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chatId, ChooseStickerAction, block)
}
suspend fun <T> TelegramBot.withTypingAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, TypingAction, block)
suspend fun <T> TelegramBot.withUploadPhotoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadPhotoAction, block)
suspend fun <T> TelegramBot.withRecordVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordVideoAction, block)
suspend fun <T> TelegramBot.withUploadVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadVideoAction, block)
suspend fun <T> TelegramBot.withRecordVoiceAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordVoiceAction, block)
suspend fun <T> TelegramBot.withUploadVoiceAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadVoiceAction, block)
suspend fun <T> TelegramBot.withUploadDocumentAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadDocumentAction, block)
suspend fun <T> TelegramBot.withFindLocationAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, FindLocationAction, block)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, RecordVideoNoteAction, block)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, UploadVideoNoteAction, block)
suspend fun <T> TelegramBot.withChooseStickerAction(chat: Chat, block: TelegramBotActionCallback<T>) = withAction(chat, ChooseStickerAction, block)
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withTypingAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, TypingAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadPhotoAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, UploadPhotoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, RecordVideoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVideoAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, UploadVideoAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVoiceAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, RecordVoiceAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVoiceAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, UploadVoiceAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadDocumentAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, UploadDocumentAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withFindLocationAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, FindLocationAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withRecordVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, RecordVideoNoteAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withUploadVideoNoteAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, UploadVideoNoteAction, block)
}
@OptIn(ExperimentalContracts::class)
suspend fun <T> TelegramBot.withChooseStickerAction(chat: Chat, block: TelegramBotActionCallback<T>) : T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return withAction(chat, ChooseStickerAction, block)
}

View File

@@ -18,6 +18,7 @@ suspend fun TelegramBot.sendLocation(
longitude: Double,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = execute(
@@ -27,6 +28,7 @@ suspend fun TelegramBot.sendLocation(
longitude,
disableNotification = disableNotification,
protectContent = protectContent,
allowSendingWithoutReply = allowSendingWithoutReply,
replyToMessageId = replyToMessageId,
replyMarkup = replyMarkup
)
@@ -41,6 +43,7 @@ suspend fun TelegramBot.sendLocation(
location: StaticLocation,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(
@@ -49,6 +52,7 @@ suspend fun TelegramBot.sendLocation(
location.longitude,
disableNotification,
protectContent,
allowSendingWithoutReply,
replyToMessageId,
replyMarkup
)
@@ -63,6 +67,7 @@ suspend fun TelegramBot.sendLocation(
longitude: Double,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(
@@ -71,6 +76,7 @@ suspend fun TelegramBot.sendLocation(
longitude,
disableNotification,
protectContent,
allowSendingWithoutReply,
replyToMessageId,
replyMarkup
)
@@ -84,6 +90,7 @@ suspend fun TelegramBot.sendLocation(
location: StaticLocation,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(
@@ -92,6 +99,7 @@ suspend fun TelegramBot.sendLocation(
location.longitude,
disableNotification,
protectContent,
allowSendingWithoutReply,
replyToMessageId,
replyMarkup
)
@@ -106,9 +114,10 @@ suspend fun TelegramBot.sendStaticLocation(
longitude: Double,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(chatId, latitude, longitude, disableNotification, protectContent, replyToMessageId, replyMarkup)
) = sendLocation(chatId, latitude, longitude, disableNotification, protectContent, allowSendingWithoutReply, replyToMessageId, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -119,9 +128,10 @@ suspend fun TelegramBot.sendStaticLocation(
location: StaticLocation,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(chatId, location.latitude, location.longitude, disableNotification, protectContent, replyToMessageId, replyMarkup)
) = sendLocation(chatId, location.latitude, location.longitude, disableNotification, protectContent, allowSendingWithoutReply, replyToMessageId, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -133,9 +143,10 @@ suspend fun TelegramBot.sendStaticLocation(
longitude: Double,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(chat.id, latitude, longitude, disableNotification, protectContent, replyToMessageId, replyMarkup)
) = sendLocation(chat.id, latitude, longitude, disableNotification, protectContent, allowSendingWithoutReply, replyToMessageId, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -146,6 +157,7 @@ suspend fun TelegramBot.sendStaticLocation(
location: StaticLocation,
disableNotification: Boolean = false,
protectContent: Boolean = false,
allowSendingWithoutReply: Boolean? = null,
replyToMessageId: MessageIdentifier? = null,
replyMarkup: KeyboardMarkup? = null
) = sendLocation(chat.id, location.latitude, location.longitude, disableNotification, protectContent, replyToMessageId, replyMarkup)
) = sendLocation(chat.id, location.latitude, location.longitude, disableNotification, protectContent, allowSendingWithoutReply, replyToMessageId, replyMarkup)

View File

@@ -6,8 +6,12 @@ import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.AudioContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentContent
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlin.jvm.JvmName
/**
* @see SendMediaGroup
@@ -41,6 +45,38 @@ suspend fun TelegramBot.sendMediaGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendMediaGroup
*/
@RiskFeature(rawSendingMediaGroupsWarning)
@JvmName("sendMedaGroupByContent")
suspend fun TelegramBot.sendMediaGroup(
chatId: ChatIdentifier,
media: List<MediaGroupContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendMediaGroup(
chatId, media.map { it.toMediaGroupMemberInputMedia() }, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendMediaGroup
*/
@RiskFeature(rawSendingMediaGroupsWarning)
@JvmName("sendMedaGroupByContent")
suspend fun TelegramBot.sendMediaGroup(
chat: Chat,
media: List<MediaGroupContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendMediaGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendPlaylist
*/
@@ -71,6 +107,36 @@ suspend fun TelegramBot.sendPlaylist(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendPlaylist
*/
@JvmName("sendPlaylistByContent")
suspend fun TelegramBot.sendPlaylist(
chatId: ChatIdentifier,
media: List<AudioContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendPlaylist(
chatId, media.map { it.toMediaGroupMemberInputMedia() }, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendPlaylist
*/
@JvmName("sendPlaylistByContent")
suspend fun TelegramBot.sendPlaylist(
chat: Chat,
media: List<AudioContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendPlaylist(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendDocumentsGroup
*/
@@ -101,6 +167,36 @@ suspend fun TelegramBot.sendDocumentsGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendDocumentsGroup
*/
@JvmName("sendDocumentsByContent")
suspend fun TelegramBot.sendDocumentsGroup(
chatId: ChatIdentifier,
media: List<DocumentContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendDocumentsGroup(
chatId, media.map { it.toMediaGroupMemberInputMedia() }, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendDocumentsGroup
*/
@JvmName("sendDocumentsByContent")
suspend fun TelegramBot.sendDocumentsGroup(
chat: Chat,
media: List<DocumentContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendDocumentsGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendVisualMediaGroup
*/
@@ -130,3 +226,33 @@ suspend fun TelegramBot.sendVisualMediaGroup(
) = sendVisualMediaGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendVisualMediaGroup
*/
@JvmName("sendVisualMediaGroupByContent")
suspend fun TelegramBot.sendVisualMediaGroup(
chatId: ChatIdentifier,
media: List<VisualMediaGroupContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendVisualMediaGroup(
chatId, media.map { it.toMediaGroupMemberInputMedia() }, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)
/**
* @see SendVisualMediaGroup
*/
@JvmName("sendVisualMediaGroupByContent")
suspend fun TelegramBot.sendVisualMediaGroup(
chat: Chat,
media: List<VisualMediaGroupContent>,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null
) = sendVisualMediaGroup(
chat.id, media, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply
)

View File

@@ -9,8 +9,7 @@ import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.files.Photo
import dev.inmo.tgbotapi.types.files.biggest
import dev.inmo.tgbotapi.types.files.*
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -88,6 +87,38 @@ suspend fun TelegramBot.sendPhoto(
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chat.id, photo, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
* [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard] as a builders for that param
*/
suspend fun TelegramBot.sendPhoto(
chatId: ChatIdentifier,
photoSize: PhotoSize,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chatId, photoSize.fileId, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
* [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard] as a builders for that param
*/
suspend fun TelegramBot.sendPhoto(
chat: Chat,
photoSize: PhotoSize,
text: String? = null,
parseMode: ParseMode? = null,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chat.id, photoSize, text, parseMode, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
@@ -159,3 +190,33 @@ suspend inline fun TelegramBot.sendPhoto(
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chat.id, photo, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
* [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard] as a builders for that param
*/
suspend inline fun TelegramBot.sendPhoto(
chatId: ChatIdentifier,
photoSize: PhotoSize,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chatId, photoSize.fileId, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or
* [dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard] as a builders for that param
*/
suspend inline fun TelegramBot.sendPhoto(
chat: Chat,
photoSize: PhotoSize,
entities: TextSourcesList,
disableNotification: Boolean = false,
protectContent: Boolean = false,
replyToMessageId: MessageIdentifier? = null,
allowSendingWithoutReply: Boolean? = null,
replyMarkup: KeyboardMarkup? = null
) = sendPhoto(chat.id, photoSize, entities, disableNotification, protectContent, replyToMessageId, allowSendingWithoutReply, replyMarkup)

View File

@@ -7,7 +7,7 @@ import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
/**
* @param replyMarkup Some of [KeyboardMarkup]. See [dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard] or

View File

@@ -0,0 +1,90 @@
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.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

@@ -11,44 +11,48 @@ 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, sticker, emojis, containsMasks, maskPosition)
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, sticker, emojis, containsMasks, maskPosition)
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, sticker, emojis, containsMasks, maskPosition
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, sticker, emojis, containsMasks, maskPosition
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)

View File

@@ -11,44 +11,48 @@ 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, sticker, emojis, containsMasks, maskPosition)
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, sticker, emojis, containsMasks, maskPosition)
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, sticker, emojis, containsMasks, maskPosition
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, sticker, emojis, containsMasks, maskPosition
user.id, name, title, sticker, emojis, containsMasks, maskPosition
)

View File

@@ -0,0 +1,58 @@
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.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

@@ -3,7 +3,7 @@ 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.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
suspend fun TelegramBot.deleteStickerFromSet(
sticker: FileId

View File

@@ -3,7 +3,7 @@ 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.SetStickerPositionInSet
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
suspend fun TelegramBot.setStickerPositionInSet(
sticker: FileId,

View File

@@ -17,8 +17,9 @@ plugins {
project.version = "$library_version"
project.group = "$library_group"
project.description = "Behaviour Builder extension with built-in FSM"
apply from: "publish.gradle"
apply from: "../publish.gradle"
repositories {
mavenLocal()

View File

@@ -1,79 +0,0 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "FSM extension for dev.inmo:tgbotapi.extensions.behaviour_builder.fsm"
name = "Telegram Bot Behaviour Builder FSM Extensions"
url = "https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.behaviour_builder"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/TelegramBotAPI.git[push=]https://github.com/insanusmokrassar/TelegramBotAPI.git"
url = "https://github.com/insanusmokrassar/TelegramBotAPI.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
repositories {
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
}

View File

@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot Behaviour Builder FSM Extensions","description":"FSM extension for dev.inmo:tgbotapi.extensions.behaviour_builder.fsm","url":"https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.behaviour_builder","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -17,8 +17,9 @@ plugins {
project.version = "$library_version"
project.group = "$library_group"
project.description = "Behaviour Builder DSL"
apply from: "publish.gradle"
apply from: "../publish.gradle"
repositories {
mavenLocal()

View File

@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot API Steps Extensions","description":"This extensions project contains tools for simple interaction with chats","url":"https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.steps","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]}}

View File

@@ -1,79 +0,0 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "This extensions project contains tools for simple interaction with chats"
name = "Telegram Bot API Steps Extensions"
url = "https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.steps"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/TelegramBotAPI.git[push=]https://github.com/insanusmokrassar/TelegramBotAPI.git"
url = "https://github.com/insanusmokrassar/TelegramBotAPI.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
repositories {
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
}

View File

@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot API Steps Extensions","description":"This extensions project contains tools for simple interaction with chats","url":"https://insanusmokrassar.github.io/TelegramBotAPI/tgbotapi.extensions.steps","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -11,16 +11,19 @@ import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
import kotlin.reflect.KClass
typealias CommonMessageToContentMapper<T> = suspend CommonMessage<T>.() -> T?
private suspend fun <O> BehaviourContext.waitCommonMessage(
private suspend fun <O> BehaviourContext.waitCommonContent(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
@@ -74,7 +77,7 @@ private suspend inline fun <reified T : MessageContent> BehaviourContext.waitCon
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: SimpleFilter<CommonMessage<T>>? = null,
noinline mapper: CommonMessageToContentMapper<T>? = null
) : List<T> = waitCommonMessage<T>(
) : List<T> = waitCommonContent<T>(
count,
initRequest,
includeMediaGroups,
@@ -87,7 +90,8 @@ private suspend inline fun <reified T : MessageContent> BehaviourContext.waitCon
contentConverter(mapper)
).toList()
suspend fun BehaviourContext.waitContentMessage(
suspend fun BehaviourContext.waitContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
@@ -198,6 +202,14 @@ suspend fun BehaviourContext.waitVisualMediaGroupContent(
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<VisualMediaGroupContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToContentMapper<TextedMediaContent>? = null
) = waitContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },

View File

@@ -0,0 +1,281 @@
@file:Suppress("unused")
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.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.toList
typealias CommonMessageToCommonMessageMapper<T> = suspend CommonMessage<T>.() -> CommonMessage<T>?
internal suspend fun <O : MessageContent> BehaviourContext.waitCommonMessage(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
errorFactory: NullableRequestBuilder<*> = { null },
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: suspend CommonMessage<MessageContent>.() -> CommonMessage<O>?
): Flow<CommonMessage<O>> = expectFlow(
initRequest,
count,
errorFactory
) {
val messages = when (it) {
is SentMediaGroupUpdate -> {
if (includeMediaGroups) {
it.data.map { it as CommonMessage<MessageContent> }
} else {
emptyList()
}
}
is BaseSentMessageUpdate -> listOf(it.data)
else -> return@expectFlow emptyList()
}
messages.mapNotNull { message ->
val asCommonMessage = message as CommonMessage<MessageContent>
if (filter == null || filter(asCommonMessage)) {
asCommonMessage.mapper()
} else {
null
}
}
}
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
}
private suspend inline fun <reified T : MessageContent> BehaviourContext.waitContentMessage(
count: Int = 1,
initRequest: Request<*>? = null,
includeMediaGroups: Boolean = true,
noinline errorFactory: NullableRequestBuilder<*> = { null },
noinline filter: SimpleFilter<CommonMessage<T>>? = null,
noinline mapper: CommonMessageToCommonMessageMapper<T>? = null
) : List<CommonMessage<T>> = waitCommonMessage<T>(
count,
initRequest,
includeMediaGroups,
errorFactory,
filter ?.let {
{
it.withContent<T>() ?.let { filter(it) } == true
}
},
contentMessageConverter(mapper)
).toList()
suspend fun BehaviourContext.waitContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MessageContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MessageContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitContactMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<ContactContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<ContactContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitDiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<DiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitGameMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<GameContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<GameContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<LocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitLiveLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<LiveLocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<LiveLocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitStaticLocationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StaticLocationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<StaticLocationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitPollMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<PollContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<PollContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitTextMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<TextContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<TextContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitVenueMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VenueContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VenueContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitAudioMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<AudioMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AudioMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitDocumentMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<DocumentMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DocumentMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitMediaMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<MediaContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MediaContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitAnyMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<MediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<MediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitVisualMediaGroupContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VisualMediaGroupContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitTextedMediaContentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<TextedMediaContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitAnimationMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<AnimationContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AnimationContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitAudioMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<AudioContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<AudioContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitDocumentMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<DocumentContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<DocumentContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitPhotoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<PhotoContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<PhotoContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitStickerMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<StickerContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<StickerContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitVideoMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = false,
filter: SimpleFilter<CommonMessage<VideoContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VideoContent>? = null
) = waitContentMessage(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitVideoNoteMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VideoNoteContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VideoNoteContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitVoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<VoiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<VoiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitInvoiceMessage(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
filter: SimpleFilter<CommonMessage<InvoiceContent>>? = null,
mapper: CommonMessageToCommonMessageMapper<InvoiceContent>? = null
) = waitContentMessage(count, initRequest, false, errorFactory, filter, mapper)

View File

@@ -12,6 +12,10 @@ import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseEditMessageUpdate
import kotlinx.coroutines.flow.toList
@@ -173,6 +177,14 @@ suspend fun BehaviourContext.waitEditedVisualMediaGroupContent(
filter: SimpleFilter<CommonMessage<VisualMediaGroupContent>>? = null,
mapper: CommonMessageToContentMapper<VisualMediaGroupContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitEditedTextedMediaContent(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },
count: Int = 1,
includeMediaGroups: Boolean = true,
filter: SimpleFilter<CommonMessage<TextedMediaContent>>? = null,
mapper: CommonMessageToContentMapper<TextedMediaContent>? = null
) = waitEditedContent(count, initRequest, includeMediaGroups, errorFactory, filter, mapper)
suspend fun BehaviourContext.waitEditedAnimation(
initRequest: Request<*>? = null,
errorFactory: NullableRequestBuilder<*> = { null },

View File

@@ -5,8 +5,11 @@ import dev.inmo.tgbotapi.extensions.utils.asSentMediaGroupUpdate
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.PhotoContent
import dev.inmo.tgbotapi.types.message.content.media.VideoContent
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.utils.PreviewFeature
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toList

View File

@@ -14,6 +14,8 @@ import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
import dev.inmo.tgbotapi.types.update.abstracts.BaseSentMessageUpdate
@@ -22,7 +24,7 @@ import dev.inmo.tgbotapi.types.update.abstracts.Update
typealias CommonMessageFilter<T> = SimpleFilter<CommonMessage<T>>
inline fun <T : MessageContent> CommonMessageFilter(noinline block: CommonMessageFilter<T>) = block
internal suspend inline fun <BC : BehaviourContext, reified T : MessageContent> BC.onContent(
internal suspend inline fun <BC : BehaviourContext, reified T : MessageContent> BC.onContentMessageWithType(
noinline initialFilter: CommonMessageFilter<T>? = null,
noinline subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<T>, Update>? = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<T>, Any> = ByChatMessageMarkerFactory,
@@ -55,7 +57,7 @@ suspend fun <BC : BehaviourContext> BC.onContentMessage(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MessageContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<MessageContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MessageContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -79,7 +81,7 @@ suspend fun <BC : BehaviourContext> BC.onContact(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<ContactContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<ContactContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<ContactContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -103,7 +105,7 @@ suspend fun <BC : BehaviourContext> BC.onDice(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DiceContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<DiceContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DiceContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -127,7 +129,7 @@ suspend fun <BC : BehaviourContext> BC.onGame(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<GameContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<GameContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<GameContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -151,7 +153,7 @@ suspend fun <BC : BehaviourContext> BC.onLocation(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<LocationContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<LocationContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<LocationContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -175,7 +177,7 @@ suspend fun <BC : BehaviourContext> BC.onLiveLocation(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<LiveLocationContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<LiveLocationContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<LiveLocationContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -199,7 +201,7 @@ suspend fun <BC : BehaviourContext> BC.onStaticLocation(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<StaticLocationContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<StaticLocationContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<StaticLocationContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -223,7 +225,7 @@ suspend fun <BC : BehaviourContext> BC.onPoll(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<PollContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<PollContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<PollContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -247,7 +249,7 @@ suspend fun <BC : BehaviourContext> BC.onText(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<TextContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -271,7 +273,7 @@ suspend fun <BC : BehaviourContext> BC.onVenue(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VenueContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<VenueContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VenueContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -295,7 +297,7 @@ suspend fun <BC : BehaviourContext> BC.onAudioMediaGroup(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AudioMediaGroupContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<AudioMediaGroupContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AudioMediaGroupContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -319,7 +321,31 @@ suspend fun <BC : BehaviourContext> BC.onDocumentMediaGroupContent(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DocumentMediaGroupContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<DocumentMediaGroupContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DocumentMediaGroupContent>>
) = onContent(
) = 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,
* 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.onTextedMediaContent(
initialFilter: CommonMessageFilter<TextedMediaContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextedMediaContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<TextedMediaContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextedMediaContent>>
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -343,7 +369,7 @@ suspend fun <BC : BehaviourContext> BC.onMediaCollection(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaCollectionContent<TelegramMediaFile>>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<MediaCollectionContent<TelegramMediaFile>>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaCollectionContent<TelegramMediaFile>>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -367,7 +393,7 @@ suspend fun <BC : BehaviourContext> BC.onMedia(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<MediaContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<MediaContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<MediaContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -391,7 +417,7 @@ suspend fun <BC : BehaviourContext> BC.onAnimation(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AnimationContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<AnimationContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AnimationContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -415,7 +441,7 @@ suspend fun <BC : BehaviourContext> BC.onAudio(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<AudioContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<AudioContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<AudioContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -439,7 +465,7 @@ suspend fun <BC : BehaviourContext> BC.onDocument(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<DocumentContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<DocumentContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<DocumentContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -463,7 +489,7 @@ suspend fun <BC : BehaviourContext> BC.onPhoto(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<PhotoContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<PhotoContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<PhotoContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -487,7 +513,7 @@ suspend fun <BC : BehaviourContext> BC.onSticker(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<StickerContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<StickerContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<StickerContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -511,7 +537,7 @@ suspend fun <BC : BehaviourContext> BC.onVideo(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VideoContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<VideoContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VideoContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -535,7 +561,7 @@ suspend fun <BC : BehaviourContext> BC.onVideoNote(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VideoNoteContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<VideoNoteContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VideoNoteContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -559,7 +585,7 @@ suspend fun <BC : BehaviourContext> BC.onVoice(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<VoiceContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<VoiceContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<VoiceContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,
@@ -583,7 +609,7 @@ suspend fun <BC : BehaviourContext> BC.onInvoice(
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<InvoiceContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<InvoiceContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<InvoiceContent>>
) = onContent(
) = onContentMessageWithType(
initialFilter,
subcontextUpdatesFilter,
markerFactory,

View File

@@ -27,6 +27,8 @@ import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseEditMessageUpdate
import dev.inmo.tgbotapi.types.update.abstracts.Update
@@ -262,6 +264,30 @@ suspend fun <BC : BehaviourContext> BC.onEditedDocumentMediaGroupContent(
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.onEditedTextedMediaContent(
initialFilter: CommonMessageFilter<TextedMediaContent>? = null,
subcontextUpdatesFilter: CustomBehaviourContextAndTwoTypesReceiver<BC, Boolean, CommonMessage<TextedMediaContent>, Update> = MessageFilterByChat,
markerFactory: MarkerFactory<in CommonMessage<TextedMediaContent>, Any> = ByChatMessageMarkerFactory,
scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, CommonMessage<TextedMediaContent>>
)= onEditedContent(
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,

View File

@@ -9,9 +9,11 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByC
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.extensions.utils.asSentMediaGroupUpdate
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.PhotoContent
import dev.inmo.tgbotapi.types.message.content.media.VideoContent
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.update.abstracts.Update
import dev.inmo.tgbotapi.utils.PreviewFeature
@@ -22,7 +24,11 @@ internal suspend inline fun <BC : BehaviourContext, reified T : MediaGroupConten
markerFactory: MarkerFactory<in List<MediaGroupMessage<T>>, Any> = ByChatMediaGroupMarkerFactory,
noinline scenarioReceiver: CustomBehaviourContextAndTypeReceiver<BC, Unit, List<MediaGroupMessage<T>>>
) = on(markerFactory, initialFilter, subcontextUpdatesFilter, scenarioReceiver) {
(it.asSentMediaGroupUpdate() ?.data ?.takeIf { it.all { it is T } } as? List<MediaGroupMessage<T>>) ?.let(::listOfNotNull)
(it.asSentMediaGroupUpdate() ?.data ?.takeIf { messages ->
messages.all { message ->
message.content is T
}
} as? List<MediaGroupMessage<T>>) ?.let(::listOfNotNull)
}
/**

View File

@@ -4,6 +4,27 @@ typealias SimpleFilter<T> = suspend (T) -> Boolean
inline fun <T> SimpleFilter(noinline block: SimpleFilter<T>) = block
/**
* @return [SimpleFilter] which will return true in case when all the items in incoming data passed [this] filter
*/
fun <T> SimpleFilter<T>.listAll() = SimpleFilter<Iterable<T>> {
it.all { this@listAll(it) }
}
/**
* @return [SimpleFilter] which will return true in case when there is any item in incoming data passed [this] filter
*/
fun <T> SimpleFilter<T>.listAny() = SimpleFilter<Iterable<T>> {
it.any { this@listAny(it) }
}
/**
* @return [SimpleFilter] which will return true in case when there is no any item in incoming data passed [this] filter
*/
fun <T> SimpleFilter<T>.listNone() = SimpleFilter<Iterable<T>> {
it.none { this@listNone(it) }
}
/**
* Makes an AND (&&) operation between [this] and [other]
*/

View File

@@ -17,8 +17,9 @@ plugins {
project.version = "$library_version"
project.group = "$library_group"
project.description = "Core part of tgbotapi with all (and only) required functionality for working with Telegram Bot API"
apply from: "publish.gradle"
apply from: "../publish.gradle"
repositories {
mavenLocal()

View File

@@ -1,79 +0,0 @@
apply plugin: 'maven-publish'
task javadocsJar(type: Jar) {
classifier = 'javadoc'
}
publishing {
publications.all {
artifact javadocsJar
pom {
description = "Library for Object-Oriented and type-safe work with Telegram Bot API"
name = "Telegram Bot API Core"
url = "https://insanusmokrassar.github.io/TelegramBotAPI"
scm {
developerConnection = "scm:git:[fetch=]https://github.com/insanusmokrassar/TelegramBotAPI.git[push=]https://github.com/insanusmokrassar/TelegramBotAPI.git"
url = "https://github.com/insanusmokrassar/TelegramBotAPI.git"
}
developers {
developer {
id = "InsanusMokrassar"
name = "Ovsiannikov Aleksei"
email = "ovsyannikov.alexey95@gmail.com"
}
}
licenses {
license {
name = "Apache Software License 2.0"
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"
}
}
}
repositories {
if ((project.hasProperty('GITHUBPACKAGES_USER') || System.getenv('GITHUBPACKAGES_USER') != null) && (project.hasProperty('GITHUBPACKAGES_PASSWORD') || System.getenv('GITHUBPACKAGES_PASSWORD') != null)) {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
}
}
}
}
}
}
if (project.hasProperty("signing.gnupg.keyName")) {
apply plugin: 'signing'
signing {
useGpgCmd()
sign publishing.publications
}
task signAll {
tasks.withType(Sign).forEach {
dependsOn(it)
}
}
}

View File

@@ -1 +0,0 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"Telegram Bot API Core","description":"Library for Object-Oriented and type-safe work with Telegram Bot API","url":"https://insanusmokrassar.github.io/TelegramBotAPI","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.bot.Ktor
import dev.inmo.micro_utils.common.Optional
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import io.ktor.client.HttpClient

View File

@@ -5,6 +5,7 @@ import dev.inmo.tgbotapi.bot.BaseRequestsExecutor
import dev.inmo.tgbotapi.bot.Ktor.base.*
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.bot.exceptions.newRequestException
import dev.inmo.tgbotapi.bot.ktor.KtorPipelineStepsHolder
import dev.inmo.tgbotapi.bot.settings.limiters.ExceptionsOnlyLimiter
import dev.inmo.tgbotapi.bot.settings.limiters.RequestLimiter
import dev.inmo.tgbotapi.requests.abstracts.Request
@@ -56,7 +57,8 @@ class KtorRequestsExecutor(
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(),
private val jsonFormatter: Json = nonstrictJsonFormat
private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
private val callsFactories: List<KtorCallFactory> = callsFactories.run {
if (!excludeDefaultFactories) {
@@ -73,37 +75,51 @@ class KtorRequestsExecutor(
}
override suspend fun <T : Any> execute(request: Request<T>): T {
return safely(
{ e ->
throw if (e is ClientRequestException) {
val content = e.response.readText()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
} else {
e
}
}
) {
requestsLimiter.limit {
var result: T? = null
for (potentialFactory in callsFactories) {
result = potentialFactory.makeCall(
client,
telegramAPIUrlsKeeper,
request,
jsonFormatter
)
if (result != null) {
break
}
}
return runCatching {
safely(
{ e ->
pipelineStepsHolder.onRequestException(request, e) ?.let { return@safely it }
result ?: error("Can't execute request: $request")
throw if (e is ClientRequestException) {
val content = e.response.readText()
val responseObject = jsonFormatter.decodeFromString(Response.serializer(), content)
newRequestException(
responseObject,
content,
"Can't get result object from $content"
)
} else {
e
}
}
) {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit {
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 {
pipelineStepsHolder.onRequestReturnResult(it, request, callsFactories)
}
}

View File

@@ -1,6 +1,6 @@
package dev.inmo.tgbotapi.bot.Ktor.base
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.micro_utils.coroutines.*
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
import dev.inmo.tgbotapi.requests.DownloadFileStream
import dev.inmo.tgbotapi.requests.abstracts.Request
@@ -25,13 +25,16 @@ object DownloadFileChannelRequestCallFactory : KtorCallFactory {
val fullUrl = urlsKeeper.createFileLinkUrl(it.filePath)
ByteReadChannelAllocator {
val scope = CoroutineScope(coroutineContext)
val scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())
val outChannel = ByteChannel()
scope.launchSafelyWithoutExceptions {
client.get<HttpStatement>(fullUrl).execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.receive()
channel.copyAndClose(outChannel)
scope.launch {
runCatchingSafely {
client.get<HttpStatement>(fullUrl).execute { httpResponse ->
val channel: ByteReadChannel = httpResponse.receive()
channel.copyAndClose(outChannel)
}
}
scope.cancel()
}
outChannel
} as T

View File

@@ -0,0 +1,75 @@
package dev.inmo.tgbotapi.bot.ktor
import dev.inmo.tgbotapi.bot.Ktor.KtorCallFactory
import dev.inmo.tgbotapi.requests.abstracts.Request
interface KtorPipelineStepsHolder {
/**
* Will be called when any exception will happen due to the [request] handling. If returns value - that value
* will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
*/
suspend fun <T: Any> onRequestException(
request: Request<T>,
t: Throwable
): T? = null
/**
* Will always be called before requests executor will check all [callsFactories] for an opportunity to make call of
* [request]
*/
suspend fun onBeforeSearchCallFactory(
request: Request<*>,
callsFactories: List<KtorCallFactory>
) {}
/**
* Will always be called before [potentialFactory] will try to make [request]
*/
suspend fun onBeforeCallFactoryMakeCall(
request: Request<*>,
potentialFactory: KtorCallFactory
) {}
/**
* Will always be called after [potentialFactory] has tried to make [request] and got some [result]. If returns
* value - that value will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
*/
suspend fun <T: Any> onAfterCallFactoryMakeCall(
result: T?,
request: Request<T>,
potentialFactory: KtorCallFactory
): T? = result
/**
* Will be called when [resultCallFactory] is the [KtorCallFactory] from [callsFactories] which has successfully
* handled [request] and returned [result]. If returns value - that value will be returned from
* [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
*/
suspend fun <T: Any> onRequestResultPresented(
result: T,
request: Request<T>,
resultCallFactory: KtorCallFactory,
callsFactories: List<KtorCallFactory>
): T? = result
/**
* Will be called when there is no [KtorCallFactory] from [callsFactories] which may handle [request]. If returns
* value - that value will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
*/
suspend fun <T: Any> onRequestResultAbsent(
request: Request<T>,
callsFactories: List<KtorCallFactory>
): T? = null
/**
* This step will be called when the [result] has been retrieved (or exception has happened). If returns value -
* that value will be returned from [dev.inmo.tgbotapi.bot.RequestsExecutor.execute] instead
*/
suspend fun <T: Any> onRequestReturnResult(
result: Result<T>,
request: Request<T>,
callsFactories: List<KtorCallFactory>
): T = result.getOrThrow()
companion object : KtorPipelineStepsHolder
}

View File

@@ -0,0 +1,224 @@
package dev.inmo.tgbotapi.bot.multiserver
import dev.inmo.tgbotapi.bot.Ktor.KtorRequestsExecutorBuilder
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
import dev.inmo.tgbotapi.bot.RequestsExecutor
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.abstracts.Request
import dev.inmo.tgbotapi.utils.TelegramAPIUrlsKeeper
import dev.inmo.tgbotapi.utils.telegramBotAPIDefaultUrl
import kotlinx.coroutines.*
import kotlin.js.JsName
import kotlin.jvm.JvmName
/**
* This type of [RequestsExecutor] (aka [TelegramBot]) has been created to aggregate several bots under the hood and make
* requests, for example, with changing of api url
*
* @param bots Bots which will be used to [execute] [Request]s
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one [execute]. By default, will select next
* [TelegramBot] when called (or first when current index is last or -1)
* @param onClose This method will be called inside of [close] method. By default, will close all [bots]
*/
class SimpleMultiServerRequestsExecutor(
private val bots: List<TelegramBot>,
private val botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
private val onClose: () -> Unit = {
bots.forEach(TelegramBot::close)
}
) : RequestsExecutor {
override suspend fun <T : Any> execute(request: Request<T>): T {
var currentBot = bots.botSelector(-1, null)
while (currentCoroutineContext().isActive) {
val i = bots.indexOf(currentBot)
runCatching {
currentBot.execute(request)
}.onSuccess {
return it
}.onFailure {
currentBot = bots.botSelector(i, it)
}
}
error("Coroutine has been terminated")
}
override fun close() {
onClose()
}
companion object {
/**
* @param keepers Will be used to create result [bots]
* @param builder Will be called with each item from [keepers] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one [execute]. By default, will select next
* [TelegramBot] when called (or first when current index is last or -1)
* @param onClose This method will be called inside of [close] method. By default, will close all [bots]
*/
@JvmName("createByUrlsKeepers")
@JsName("createByUrlsKeepers")
inline operator fun invoke(
keepers: Iterable<TelegramAPIUrlsKeeper>,
crossinline builder: KtorRequestsExecutorBuilder.(TelegramAPIUrlsKeeper) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): SimpleMultiServerRequestsExecutor {
val bots = keepers.map { telegramBot(it) { builder(it) } }
return if (onClose == null) {
SimpleMultiServerRequestsExecutor(bots, botSelector)
} else {
SimpleMultiServerRequestsExecutor(bots, botSelector) {
onClose(bots)
}
}
}
/**
* @param tokens Will be used to create result [bots]
* @param builder Will be called with each item from [tokens] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one [execute]. By default, will select next
* [TelegramBot] when called (or first when current index is last or -1)
* @param onClose This method will be called inside of [close] method. By default, will close all [bots]
*/
@JvmName("createByTokens")
@JsName("createByTokens")
inline operator fun invoke(
tokens: Iterable<String>,
crossinline builder: KtorRequestsExecutorBuilder.(String) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): SimpleMultiServerRequestsExecutor {
val bots = tokens.map { telegramBot(it) { builder(it) } }
return if (onClose == null) {
SimpleMultiServerRequestsExecutor(bots, botSelector)
} else {
SimpleMultiServerRequestsExecutor(bots, botSelector) {
onClose(bots)
}
}
}
/**
* @param tokens [Pair]s with first parameter as a token and second parameter api url. Will be used to create
* result [bots]
* @param builder Will be called with each item from [tokens] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one [execute]. By default, will select next
* [TelegramBot] when called (or first when current index is last or -1)
* @param onClose This method will be called inside of [close] method. By default, will close all [bots]
*/
@JvmName("createByTokensAndApiUrls")
@JsName("createByTokensAndApiUrls")
inline operator fun invoke(
tokens: Iterable<Pair<String, String>>,
crossinline builder: KtorRequestsExecutorBuilder.(Pair<String, String>) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): SimpleMultiServerRequestsExecutor {
val bots = tokens.map { telegramBot(it.first, it.second) { builder(it) } }
return if (onClose == null) {
SimpleMultiServerRequestsExecutor(bots, botSelector)
} else {
SimpleMultiServerRequestsExecutor(bots, botSelector) {
onClose(bots)
}
}
}
}
}
/**
* Creates [SimpleMultiServerRequestsExecutor]
*
* @param bots Bots which will be used to [SimpleMultiServerRequestsExecutor.execute] [Request]s
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one
* [SimpleMultiServerRequestsExecutor.execute]. By default, will select next [TelegramBot] when called (or first when
* current index is last or -1)
* @param onClose This method will be called inside of [SimpleMultiServerRequestsExecutor.close] method. By default, will close all [bots]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun telegramBot(
bots: List<TelegramBot>,
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: () -> Unit = {
bots.forEach(TelegramBot::close)
}
): TelegramBot = SimpleMultiServerRequestsExecutor(bots, botSelector, onClose)
/**
* @param keepers Will be used to create result [SimpleMultiServerRequestsExecutor.bots]
* @param builder Will be called with each item from [keepers] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one
* [SimpleMultiServerRequestsExecutor.execute]. By default, will select next [TelegramBot] when called (or first when
* current index is last or -1)
* @param onClose This method will be called inside of [SimpleMultiServerRequestsExecutor.close] method. By default,
* will close all [SimpleMultiServerRequestsExecutor.bots]
*/
@JvmName("telegramBotByApiUrlsKeepers")
@JsName("telegramBotByApiUrlsKeepers")
inline fun telegramBot(
keepers: Iterable<TelegramAPIUrlsKeeper>,
crossinline builder: KtorRequestsExecutorBuilder.(TelegramAPIUrlsKeeper) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): TelegramBot = SimpleMultiServerRequestsExecutor(keepers, builder, botSelector, onClose)
/**
* @param tokens Will be used to create result [SimpleMultiServerRequestsExecutor.bots]
* @param builder Will be called with each item from [tokens] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one
* [SimpleMultiServerRequestsExecutor.execute]. By default, will select next [TelegramBot] when called (or first when
* current index is last or -1)
* @param onClose This method will be called inside of [SimpleMultiServerRequestsExecutor.close] method. By default,
* will close all [SimpleMultiServerRequestsExecutor.bots]
*/
@JvmName("telegramBotByTokens")
@JsName("telegramBotByTokens")
inline fun telegramBot(
tokens: Iterable<String>,
crossinline builder: KtorRequestsExecutorBuilder.(String) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): TelegramBot = SimpleMultiServerRequestsExecutor(tokens, builder, botSelector, onClose)
/**
* @param tokens [Pair]s with first parameter as a token and second parameter api url. Will be used to create
* result [SimpleMultiServerRequestsExecutor.bots]
* @param builder Will be called with each item from [tokens] in process of bots creation
* @param botSelector It is strategy by which the bot is selected for the execution. This lambda will receive **-1**
* when request execution just started and this call is first in context of one
* [SimpleMultiServerRequestsExecutor.execute]. By default, will select next
* [TelegramBot] when called (or first when current index is last or -1)
* @param onClose This method will be called inside of [SimpleMultiServerRequestsExecutor.close] method. By default,
* will close all [SimpleMultiServerRequestsExecutor.bots]
*/
@JvmName("telegramBotByTokensAndApiUrls")
@JsName("telegramBotByTokensAndApiUrls")
inline fun telegramBot(
tokens: Iterable<Pair<String, String>>,
crossinline builder: KtorRequestsExecutorBuilder.(Pair<String, String>) -> Unit = {},
noinline botSelector: suspend List<TelegramBot>.(currentBotIndex: Int, t: Throwable?) -> TelegramBot = { i, _ ->
getOrElse(i + 1) { first() }
},
noinline onClose: ((List<TelegramBot>) -> Unit)? = null
): TelegramBot = SimpleMultiServerRequestsExecutor(tokens, builder, botSelector, onClose)

View File

@@ -61,9 +61,9 @@ object InputFileSerializer : KSerializer<InputFile> {
@Serializable(InputFileSerializer::class)
data class MultipartFile (
val file: StorageFile,
val filename: String = file.storageFileInfo.fileName
val filename: String = file.fileName
) : InputFile() {
override val fileId: String = file.storageFileInfo.generateCustomName()
override val fileId: String = file.generateCustomName()
}
@Suppress("NOTHING_TO_INLINE", "unused")

View File

@@ -8,8 +8,8 @@ import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.AudioContent
import dev.inmo.tgbotapi.types.message.content.media.DocumentContent
import dev.inmo.tgbotapi.utils.*

View File

@@ -18,7 +18,7 @@ fun AddAnimatedStickerToSet(
return when (sticker) {
is MultipartFile -> CommonMultipartFileRequest(
data,
mapOf(pngStickerField to sticker)
mapOf(tgsStickerField to sticker)
)
is FileId -> data
}

View File

@@ -0,0 +1,50 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.StandardStickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import kotlinx.serialization.*
fun AddVideoStickerToSet(
userId: UserId,
stickerSetName: String,
sticker: InputFile,
emojis: String,
maskPosition: MaskPosition? = null
): Request<Boolean> {
val data = AddVideoStickerToSet(userId, stickerSetName, emojis, sticker as? FileId, maskPosition)
return when (sticker) {
is MultipartFile -> CommonMultipartFileRequest(
data,
mapOf(webmStickerField to sticker)
)
is FileId -> data
}
}
@Serializable
data class AddVideoStickerToSet internal constructor(
@SerialName(userIdField)
override val userId: UserId,
@SerialName(nameField)
override val name: String,
@SerialName(emojisField)
override val emojis: String,
@SerialName(webmStickerField)
val sticker: FileId? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null
) : StandardStickerSetAction {
init {
if(emojis.isEmpty()) {
throw IllegalArgumentException("Emojis must not be empty")
}
}
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "addStickerToSet"
}

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.CreateStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StandardStickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
@@ -10,16 +11,17 @@ import kotlinx.serialization.*
fun CreateNewAnimatedStickerSet(
userId: UserId,
name: String,
title: String,
sticker: InputFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
): Request<Boolean> {
val data = CreateNewAnimatedStickerSet(userId, name, emojis, sticker as? FileId, containsMasks, maskPosition)
val data = CreateNewAnimatedStickerSet(userId, name, title, emojis, sticker as? FileId, containsMasks, maskPosition)
return when (sticker) {
is MultipartFile -> CommonMultipartFileRequest(
data,
mapOf(pngStickerField to sticker)
mapOf(tgsStickerField to sticker)
)
is FileId -> data
}
@@ -31,6 +33,8 @@ data class CreateNewAnimatedStickerSet internal constructor(
override val userId: UserId,
@SerialName(nameField)
override val name: String,
@SerialName(titleField)
override val title: String,
@SerialName(emojisField)
override val emojis: String,
@SerialName(tgsStickerField)
@@ -39,7 +43,7 @@ data class CreateNewAnimatedStickerSet internal constructor(
val containsMasks: Boolean? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null
) : StandardStickerSetAction {
) : CreateStickerSetAction {
init {
if(emojis.isEmpty()) {
throw IllegalArgumentException("Emojis must not be empty")

View File

@@ -2,6 +2,7 @@ package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.CreateStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StandardStickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
@@ -10,12 +11,13 @@ import kotlinx.serialization.*
fun CreateNewStaticStickerSet(
userId: UserId,
name: String,
title: String,
sticker: InputFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
): Request<Boolean> {
val data = CreateNewStaticStickerSet(userId, name, emojis, sticker as? FileId, containsMasks, maskPosition)
val data = CreateNewStaticStickerSet(userId, name, title, emojis, sticker as? FileId, containsMasks, maskPosition)
return when (sticker) {
is MultipartFile -> CommonMultipartFileRequest(
data,
@@ -28,11 +30,12 @@ fun CreateNewStaticStickerSet(
fun CreateNewStickerSet(
userId: UserId,
name: String,
title: String,
sticker: InputFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
): Request<Boolean> = CreateNewStaticStickerSet(userId, name, sticker, emojis, containsMasks, maskPosition)
): Request<Boolean> = CreateNewStaticStickerSet(userId, name, title, sticker, emojis, containsMasks, maskPosition)
@Serializable
data class CreateNewStaticStickerSet internal constructor(
@@ -40,6 +43,8 @@ data class CreateNewStaticStickerSet internal constructor(
override val userId: UserId,
@SerialName(nameField)
override val name: String,
@SerialName(titleField)
override val title: String,
@SerialName(emojisField)
override val emojis: String,
@SerialName(pngStickerField)
@@ -48,7 +53,7 @@ data class CreateNewStaticStickerSet internal constructor(
val containsMasks: Boolean? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null
) : StandardStickerSetAction {
) : CreateStickerSetAction {
init {
if(emojis.isEmpty()) {
throw IllegalArgumentException("Emojis must not be empty")

View File

@@ -0,0 +1,57 @@
package dev.inmo.tgbotapi.requests.stickers
import dev.inmo.tgbotapi.requests.abstracts.*
import dev.inmo.tgbotapi.requests.common.CommonMultipartFileRequest
import dev.inmo.tgbotapi.requests.stickers.abstracts.CreateStickerSetAction
import dev.inmo.tgbotapi.requests.stickers.abstracts.StandardStickerSetAction
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import kotlinx.serialization.*
fun CreateNewVideoStickerSet(
userId: UserId,
linkName: String,
title: String,
sticker: InputFile,
emojis: String,
containsMasks: Boolean? = null,
maskPosition: MaskPosition? = null
): Request<Boolean> {
val data = CreateNewVideoStickerSet(userId, linkName, title, emojis, sticker as? FileId, containsMasks, maskPosition)
return when (sticker) {
is MultipartFile -> CommonMultipartFileRequest(
data,
mapOf(webmStickerField to sticker)
)
is FileId -> data
}
}
@Serializable
data class CreateNewVideoStickerSet internal constructor(
@SerialName(userIdField)
override val userId: UserId,
@SerialName(nameField)
override val name: String,
@SerialName(titleField)
override val title: String,
@SerialName(emojisField)
override val emojis: String,
@SerialName(webmStickerField)
val sticker: FileId? = null,
@SerialName(containsMasksField)
val containsMasks: Boolean? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null
) : CreateStickerSetAction {
init {
if(emojis.isEmpty()) {
throw IllegalArgumentException("Emojis must not be empty")
}
}
override val requestSerializer: SerializationStrategy<*>
get() = serializer()
override fun method(): String = "createNewStickerSet"
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.requests.stickers.abstracts
interface CreateStickerSetAction : StandardStickerSetAction {
val title: String
}

View File

@@ -46,6 +46,9 @@ fun Byte.toChatId(): ChatId = toLong().toChatId()
data class Username(
val username: String
) : ChatIdentifier() {
val usernameWithoutAt
get() = username.dropWhile { it == '@' }
init {
if (!username.startsWith("@")) {
throw IllegalArgumentException("Username must starts with `@`")

View File

@@ -168,6 +168,7 @@ const val botUsernameField = "bot_username"
const val switchInlineQueryCurrentChatField = "switch_inline_query_current_chat"
const val switchInlineQueryField = "switch_inline_query"
const val isAnimatedField = "is_animated"
const val isVideoField = "is_video"
const val inviteLinkField = "invite_link"
const val pinnedMessageField = "pinned_message"
const val customTitleField = "custom_title"
@@ -265,6 +266,7 @@ const val canManageVoiceChatsField = "can_manage_voice_chats"
const val canManageChatField = "can_manage_chat"
const val pngStickerField = "png_sticker"
const val tgsStickerField = "tgs_sticker"
const val webmStickerField = "webm_sticker"
const val oldChatMemberField = "old_chat_member"
const val newChatMemberField = "new_chat_member"

View File

@@ -20,11 +20,11 @@ internal data class RawMessageEntity(
internal fun RawMessageEntity.asTextSource(
source: String,
subParts: TextSourcesList
subParts: List<Pair<Int, TextSource>>
): TextSource {
val sourceSubstring: String = source.substring(range)
val subPartsWithRegulars by lazy {
subParts.fillWithRegulars(sourceSubstring)
subParts.map { (it.first - offset) to it.second }.fillWithRegulars(sourceSubstring)
}
return when (type) {
"mention" -> MentionTextSource(sourceSubstring, subPartsWithRegulars)
@@ -58,16 +58,14 @@ private inline operator fun <T : Comparable<T>> ClosedRange<T>.contains(other: C
return start <= other.start && endInclusive >= other.endInclusive
}
internal fun TextSourcesList.fillWithRegulars(source: String): TextSourcesList {
internal fun List<Pair<Int, TextSource>>.fillWithRegulars(source: String): TextSourcesList {
var index = 0
val result = mutableListOf<TextSource>()
for (i in 0 until size) {
val textSource = get(i)
val thisSourceInStart = source.startsWith(textSource.source, index)
if (!thisSourceInStart) {
val regularEndIndex = source.indexOf(textSource.source, index)
result.add(regular(source.substring(index, regularEndIndex)))
index = regularEndIndex
for (i in indices) {
val (offset, textSource) = get(i)
if (offset - index > 0) {
result.add(regular(source.substring(index, offset)))
index = offset
}
result.add(textSource)
index += textSource.source.length
@@ -83,9 +81,9 @@ internal fun TextSourcesList.fillWithRegulars(source: String): TextSourcesList {
private fun createTextSources(
originalFullString: String,
entities: RawMessageEntities
): TextSourcesList {
): List<Pair<Int, TextSource>> {
val mutableEntities = entities.toMutableList().apply { sortBy { it.offset } }
val resultList = mutableListOf<TextSource>()
val resultList = mutableListOf<Pair<Int, TextSource>>()
while (mutableEntities.isNotEmpty()) {
var parent = mutableEntities.removeFirst()
@@ -129,7 +127,7 @@ private fun createTextSources(
emptyList()
}
resultList.add(
parent.asTextSource(
parent.offset to parent.asTextSource(
originalFullString,
subtextSources
)

View File

@@ -1,5 +1,6 @@
package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.types.Username
import dev.inmo.tgbotapi.utils.RiskFeature
import dev.inmo.tgbotapi.utils.extensions.makeString
import dev.inmo.tgbotapi.utils.internal.*
@@ -23,6 +24,7 @@ data class MentionTextSource @RiskFeature(DirectInvocationOfTextSourceConstructo
override val markdown: String by lazy { source.mentionMarkdown() }
override val markdownV2: String by lazy { mentionMarkdownV2() }
override val html: String by lazy { mentionHTML() }
val username: Username = Username(source)
init {
if (!source.startsWith("@")) {
@@ -42,3 +44,6 @@ inline fun mention(vararg parts: TextSource) = mention(parts.toList())
@Suppress("NOTHING_TO_INLINE")
inline fun mention(whoToMention: String) = mention(regular(whoToMention))
@Suppress("NOTHING_TO_INLINE")
inline fun mention(whoToMention: Username) = mention(whoToMention.username.dropWhile { it == '@' })

View File

@@ -33,7 +33,12 @@ inline fun mention(parts: TextSourcesList, id: Identifier) = mention(parts, User
@Suppress("NOTHING_TO_INLINE")
inline fun Identifier.mention(parts: TextSourcesList) = mention(parts, this)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(user: User, vararg parts: TextSource) = mention(parts.toList(), user)
inline fun mention(user: User, vararg parts: TextSource) = mention(
textSourcesOrElseTextSource(parts.toList()) {
RegularTextSource("${user.lastName} ${user.firstName}")
},
user
)
@Suppress("NOTHING_TO_INLINE")
inline fun mention(text: String, user: User) = mention(user, regular(text))
@Suppress("NOTHING_TO_INLINE")

View File

@@ -0,0 +1,17 @@
package dev.inmo.tgbotapi.types.MessageEntity.textsources
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlin.js.JsName
import kotlin.jvm.JvmName
@RiskFeature
inline fun textSourcesOrElse(
textSources: TextSourcesList,
block: () -> TextSourcesList
): TextSourcesList = textSources.takeIf { it.isNotEmpty() } ?: block()
@RiskFeature
inline fun textSourcesOrElseTextSource(
textSources: TextSourcesList,
block: () -> TextSource
): TextSourcesList = textSources.takeIf { it.isNotEmpty() } ?: listOf(block())

View File

@@ -4,6 +4,7 @@ import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.FileUniqueId
import dev.inmo.tgbotapi.types.fileUniqueIdField
import dev.inmo.tgbotapi.types.files.abstracts.*
import dev.inmo.tgbotapi.types.message.content.media.DocumentContent
import dev.inmo.tgbotapi.utils.MimeType
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -24,11 +25,15 @@ data class DocumentFile(
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile
@Suppress("NOTHING_TO_INLINE")
inline fun TelegramMediaFile.asDocumentFile() = DocumentFile(
fileId,
fileUniqueId,
fileSize,
(this as? ThumbedMediaFile) ?.thumb,
(this as? MimedMediaFile) ?.mimeType,
(this as? CustomNamedMediaFile) ?.fileName
)
inline fun TelegramMediaFile.asDocumentFile() = if (this is DocumentFile) {
this
} else {
DocumentFile(
fileId,
fileUniqueId,
fileSize,
(this as? ThumbedMediaFile) ?.thumb,
(this as? MimedMediaFile) ?.mimeType,
(this as? CustomNamedMediaFile) ?.fileName
)
}

View File

@@ -1,32 +1,6 @@
package dev.inmo.tgbotapi.types.files
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.files.abstracts.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import dev.inmo.tgbotapi.types.files.sticker.Sticker
@Serializable
data class Sticker(
@SerialName(fileIdField)
override val fileId: FileId,
@SerialName(fileUniqueIdField)
override val fileUniqueId: FileUniqueId,
@SerialName(widthField)
override val width: Int,
@SerialName(heightField)
override val height: Int,
@SerialName(thumbField)
override val thumb: PhotoSize? = null,
@SerialName(emojiField)
val emoji: String? = null,
@SerialName(stickerSetNameField)
val stickerSetName: StickerSetName? = null,
@SerialName(isAnimatedField)
val isAnimated: Boolean = false,
@SerialName(maskPositionField)
val maskPosition: MaskPosition? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null
) : TelegramMediaFile, SizedMediaFile, ThumbedMediaFile
@Deprecated("Replaced", ReplaceWith("Sticker", "dev.inmo.tgbotapi.types.files.sticker.Sticker"))
typealias Sticker = Sticker

View File

@@ -0,0 +1,158 @@
package dev.inmo.tgbotapi.types.files.sticker
import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.files.PhotoSize
import dev.inmo.tgbotapi.types.files.abstracts.*
import dev.inmo.tgbotapi.types.stickers.MaskPosition
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
@Serializable
@RiskFeature("This class is used for serialization/deserialization of Sticker interface")
data class StickerSurrogate(
val file_id: FileId,
val file_unique_id: FileUniqueId,
val width: Int,
val height: Int,
val is_animated: Boolean? = null,
val is_video: Boolean? = null,
val thumb: PhotoSize? = null,
val emoji: String? = null,
val set_name: StickerSetName? = null,
val mask_position: MaskPosition? = null,
val file_size: Long? = null
)
// TODO:: Serializer
@Serializable(StickerSerializer::class)
sealed interface Sticker : TelegramMediaFile, SizedMediaFile, ThumbedMediaFile {
val emoji: String?
val maskPosition: MaskPosition?
val stickerSetName: StickerSetName?
val isAnimated
get() = this is AnimatedSticker
val isVideo
get() = this is VideoSticker
companion object {
fun serializer(): KSerializer<Sticker> = StickerSerializer
}
}
object StickerSerializer : KSerializer<Sticker> {
override val descriptor: SerialDescriptor = StickerSurrogate.serializer().descriptor
override fun deserialize(decoder: Decoder): Sticker {
val surrogate = StickerSurrogate.serializer().deserialize(decoder)
return when {
surrogate.is_animated == true -> AnimatedSticker(
surrogate.file_id,
surrogate.file_unique_id,
surrogate.width,
surrogate.height,
surrogate.thumb,
surrogate.emoji,
surrogate.set_name,
surrogate.mask_position,
surrogate.file_size
)
surrogate.is_video == true -> VideoSticker(
surrogate.file_id,
surrogate.file_unique_id,
surrogate.width,
surrogate.height,
surrogate.thumb,
surrogate.emoji,
surrogate.set_name,
surrogate.mask_position,
surrogate.file_size
)
else -> SimpleSticker(
surrogate.file_id,
surrogate.file_unique_id,
surrogate.width,
surrogate.height,
surrogate.thumb,
surrogate.emoji,
surrogate.set_name,
surrogate.mask_position,
surrogate.file_size
)
}
}
override fun serialize(encoder: Encoder, value: Sticker) {
TODO("Not yet implemented")
}
}
@Serializable
data class SimpleSticker(
@SerialName(fileIdField)
override val fileId: FileId,
@SerialName(fileUniqueIdField)
override val fileUniqueId: FileUniqueId,
@SerialName(widthField)
override val width: Int,
@SerialName(heightField)
override val height: Int,
@SerialName(thumbField)
override val thumb: PhotoSize? = null,
@SerialName(emojiField)
override val emoji: String? = null,
@SerialName(stickerSetNameField)
override val stickerSetName: StickerSetName? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null,
) : Sticker
@Serializable
data class AnimatedSticker(
@SerialName(fileIdField)
override val fileId: FileId,
@SerialName(fileUniqueIdField)
override val fileUniqueId: FileUniqueId,
@SerialName(widthField)
override val width: Int,
@SerialName(heightField)
override val height: Int,
@SerialName(thumbField)
override val thumb: PhotoSize? = null,
@SerialName(emojiField)
override val emoji: String? = null,
@SerialName(stickerSetNameField)
override val stickerSetName: StickerSetName? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null,
) : Sticker
@Serializable
data class VideoSticker(
@SerialName(fileIdField)
override val fileId: FileId,
@SerialName(fileUniqueIdField)
override val fileUniqueId: FileUniqueId,
@SerialName(widthField)
override val width: Int,
@SerialName(heightField)
override val height: Int,
@SerialName(thumbField)
override val thumb: PhotoSize? = null,
@SerialName(emojiField)
override val emoji: String? = null,
@SerialName(stickerSetNameField)
override val stickerSetName: StickerSetName? = null,
@SerialName(maskPositionField)
override val maskPosition: MaskPosition? = null,
@SerialName(fileSizeField)
override val fileSize: Long? = null,
) : Sticker

View File

@@ -7,7 +7,7 @@ import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
data class ChannelMediaGroupMessage<T : MediaGroupContent>(
override val messageId: MessageIdentifier,

View File

@@ -5,7 +5,7 @@ import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
import dev.inmo.tgbotapi.types.message.abstracts.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
data class CommonMediaGroupMessage<T : MediaGroupContent>(
override val messageId: MessageIdentifier,

View File

@@ -7,6 +7,7 @@ import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.abstracts.*
import dev.inmo.tgbotapi.types.dice.Dice
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.files.sticker.Sticker
import dev.inmo.tgbotapi.types.games.RawGame
import dev.inmo.tgbotapi.types.location.Location
import dev.inmo.tgbotapi.types.message.ChatEvents.*

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.MediaGroupIdentifier
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
interface MediaGroupMessage<T : MediaGroupContent> : CommonMessage<T> {
val mediaGroupId: MediaGroupIdentifier

View File

@@ -0,0 +1,4 @@
package dev.inmo.tgbotapi.types.message.content.abstracts
@Deprecated("This class has been moved to the other package", ReplaceWith("AudioMediaGroupContent", "dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent"))
typealias AudioMediaGroupContent = dev.inmo.tgbotapi.types.message.content.media.AudioMediaGroupContent

View File

@@ -0,0 +1,4 @@
package dev.inmo.tgbotapi.types.message.content.abstracts
@Deprecated("This class has been moved to the other package", ReplaceWith("DocumentMediaGroupContent", "dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent"))
typealias DocumentMediaGroupContent = dev.inmo.tgbotapi.types.message.content.media.DocumentMediaGroupContent

View File

@@ -1,18 +1,4 @@
package dev.inmo.tgbotapi.types.message.content.abstracts
import dev.inmo.tgbotapi.CommonAbstracts.TextedInput
import dev.inmo.tgbotapi.types.InputMedia.*
interface MediaGroupContent : MediaContent, TextedInput {
fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia
}
interface VisualMediaGroupContent : MediaGroupContent {
override fun toMediaGroupMemberInputMedia(): VisualMediaGroupMemberInputMedia
}
interface AudioMediaGroupContent : MediaGroupContent {
override fun toMediaGroupMemberInputMedia(): AudioMediaGroupMemberInputMedia
}
interface DocumentMediaGroupContent : MediaGroupContent {
override fun toMediaGroupMemberInputMedia(): DocumentMediaGroupMemberInputMedia
}
@Deprecated("This class has been moved to the other package", ReplaceWith("MediaGroupContent", "dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent"))
typealias MediaGroupContent = dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent

View File

@@ -1,3 +1,41 @@
package dev.inmo.tgbotapi.types.message.content.abstracts
interface MessageContent: ResendableContent
import dev.inmo.tgbotapi.types.message.content.*
import dev.inmo.tgbotapi.types.message.content.media.*
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
import dev.inmo.tgbotapi.utils.RiskFeature
import kotlinx.serialization.modules.*
interface MessageContent: ResendableContent {
companion object {
@RiskFeature("This serialization module can be changed in near releases")
fun serializationModule(
additionalBuilder: PolymorphicModuleBuilder<MessageContent>.() -> Unit = {}
) = SerializersModule {
polymorphic(MessageContent::class) {
subclass(ContactContent::class)
subclass(VenueContent::class)
subclass(PollContent::class)
subclass(DiceContent::class)
subclass(TextContent::class)
subclass(LiveLocationContent::class)
subclass(StaticLocationContent::class)
subclass(PhotoContent::class)
subclass(VideoContent::class)
subclass(AudioContent::class)
subclass(DocumentContent::class)
subclass(VoiceContent::class)
subclass(VideoNoteContent::class)
subclass(AnimationContent::class)
subclass(StickerContent::class)
subclass(InvoiceContent::class)
additionalBuilder()
}
}
}
}

View File

@@ -0,0 +1,4 @@
package dev.inmo.tgbotapi.types.message.content.abstracts
@Deprecated("This class has been moved to the other package", ReplaceWith("VisualMediaGroupContent", "dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent"))
typealias VisualMediaGroupContent = dev.inmo.tgbotapi.types.message.content.media.VisualMediaGroupContent

View File

@@ -20,7 +20,7 @@ data class AnimationContent(
val includedDocument: DocumentFile?,
override val text: String?,
override val textSources: TextSourcesList = emptyList()
) : MediaContent, TextedInput {
) : TextedMediaContent {
override fun createResend(
chatId: ChatIdentifier,
disableNotification: Boolean,

View File

@@ -10,7 +10,6 @@ import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.AudioFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.AudioMediaGroupContent
import kotlinx.serialization.Serializable
@Serializable

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.types.InputMedia.AudioMediaGroupMemberInputMedia
import dev.inmo.tgbotapi.types.files.AudioFile
interface AudioMediaGroupContent : MediaGroupContent {
override val media: AudioFile
override fun toMediaGroupMemberInputMedia(): AudioMediaGroupMemberInputMedia
}

View File

@@ -12,7 +12,6 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.files.asDocumentFile
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.DocumentMediaGroupContent
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
import kotlinx.serialization.Serializable

View File

@@ -0,0 +1,10 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.types.InputMedia.*
import dev.inmo.tgbotapi.types.files.DocumentFile
interface DocumentMediaGroupContent : MediaGroupContent {
override val media: DocumentFile
override fun toMediaGroupMemberInputMedia(): DocumentMediaGroupMemberInputMedia
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
interface MediaGroupContent : TextedMediaContent {
fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia
}

View File

@@ -11,7 +11,6 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaCollectionContent
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
import kotlinx.serialization.Serializable
@Serializable

View File

@@ -6,7 +6,7 @@ import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.InputMediaDocument
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
import kotlinx.serialization.Serializable

View File

@@ -0,0 +1,6 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.CommonAbstracts.TextedInput
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
sealed interface TextedMediaContent : MediaContent, TextedInput

View File

@@ -10,7 +10,6 @@ import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
import dev.inmo.tgbotapi.types.files.VideoFile
import dev.inmo.tgbotapi.types.files.toInputMediaVideo
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
import kotlinx.serialization.Serializable
@Serializable

View File

@@ -0,0 +1,7 @@
package dev.inmo.tgbotapi.types.message.content.media
import dev.inmo.tgbotapi.types.InputMedia.VisualMediaGroupMemberInputMedia
interface VisualMediaGroupContent : MediaGroupContent {
override fun toMediaGroupMemberInputMedia(): VisualMediaGroupMemberInputMedia
}

View File

@@ -18,7 +18,7 @@ data class VoiceContent(
override val media: VoiceFile,
override val text: String? = null,
override val textSources: TextSourcesList = emptyList()
) : MediaContent, TextedInput {
) : TextedMediaContent {
override fun createResend(
chatId: ChatIdentifier,
disableNotification: Boolean,

View File

@@ -12,6 +12,13 @@ import kotlinx.serialization.encoding.Encoder
sealed class PollOption {
abstract val text: String
abstract val votes: Int
companion object {
fun simple(
text: String,
votes: Int = 0
) = SimplePollOption(text, votes)
}
}
@Serializable
@@ -19,7 +26,7 @@ data class SimplePollOption (
@SerialName(textField)
override val text: String,
@SerialName(votesCountField)
override val votes: Int
override val votes: Int = 0
) : PollOption()
@RiskFeature

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.stickers
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.files.PhotoSize
import dev.inmo.tgbotapi.types.files.Sticker
import dev.inmo.tgbotapi.types.files.sticker.Sticker
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -16,6 +16,8 @@ data class StickerSet(
val stickers: List<Sticker>,
@SerialName(isAnimatedField)
val isAnimated: Boolean = false,
@SerialName(isVideoField)
val isVideo: Boolean = false,
@SerialName(containsMasksField)
val containsMasks: Boolean = false,
@SerialName(thumbField)

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.update.MediaGroupUpdates
import dev.inmo.tgbotapi.types.UpdateIdentifier
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate
data class ChannelPostMediaGroupUpdate(

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.update.MediaGroupUpdates
import dev.inmo.tgbotapi.types.UpdateIdentifier
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.EditChannelPostUpdate
data class EditChannelPostMediaGroupUpdate(
@@ -10,4 +10,4 @@ data class EditChannelPostMediaGroupUpdate(
) : EditMediaGroupUpdate {
override val updateId: UpdateIdentifier = origin.updateId
override val data: MediaGroupMessage<MediaGroupContent> = origin.data as MediaGroupMessage<MediaGroupContent>
}
}

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.update.MediaGroupUpdates
import dev.inmo.tgbotapi.types.UpdateIdentifier
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.EditMessageUpdate
data class EditMessageMediaGroupUpdate(

View File

@@ -1,7 +1,7 @@
package dev.inmo.tgbotapi.types.update.MediaGroupUpdates
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.abstracts.*
/**

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.update.MediaGroupUpdates
import dev.inmo.tgbotapi.types.UpdateIdentifier
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.media.MediaGroupContent
import dev.inmo.tgbotapi.types.update.abstracts.BaseMessageUpdate
data class MessageMediaGroupUpdate(
@@ -10,4 +10,4 @@ data class MessageMediaGroupUpdate(
) : SentMediaGroupUpdate {
override val updateId: UpdateIdentifier = origins.last().updateId
override val data: List<MediaGroupMessage<MediaGroupContent>> = origins.mapNotNull { it.data as? MediaGroupMessage<MediaGroupContent> }
}
}

View File

@@ -2,34 +2,69 @@ package dev.inmo.tgbotapi.utils
import dev.inmo.tgbotapi.types.buttons.Matrix
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardRowBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardRowBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> row(block: RowBuilder<T>.() -> Unit): List<T> {
return RowBuilder<T>().also(block).row
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardRowBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardRowBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> MatrixBuilder<T>.row(block: RowBuilder<T>.() -> Unit) {
add(RowBuilder<T>().also(block).row)
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardRowBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardRowBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> MatrixBuilder<T>.row(vararg elements: T) {
add(elements.toList())
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> matrix(block: MatrixBuilder<T>.() -> Unit): Matrix<T> {
return MatrixBuilder<T>().also(block).matrix
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> flatMatrix(block: RowBuilder<T>.() -> Unit): Matrix<T> {
return MatrixBuilder<T>().apply {
row(block)
}.matrix
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardBuilder
*/
@Deprecated("This functionality will be removed soon")
fun <T> flatMatrix(vararg elements: T): Matrix<T> {
return MatrixBuilder<T>().apply {
row { elements.forEach { +it } }
}.matrix
}
/**
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.InlineKeyboardRowBuilder
* @see dev.inmo.tgbotapi.extensions.utils.types.buttons.ReplyKeyboardRowBuilder
*/
@Deprecated("This functionality will be removed soon")
operator fun <T> RowBuilder<T>.plus(t: T) = add(t)
open class RowBuilder<T> {

View File

@@ -1,6 +1,8 @@
package dev.inmo.tgbotapi.utils
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filename
import io.ktor.utils.io.*
import io.ktor.utils.io.core.ByteReadPacket
import io.ktor.utils.io.core.Input
@@ -13,6 +15,7 @@ import kotlinx.serialization.Serializable
* @param fileName This filename will be used in telegram system as name of file
*/
@Serializable
@Deprecated("Will be removed soon")
data class StorageFileInfo(
val fileName: String
) {
@@ -25,18 +28,35 @@ data class StorageFileInfo(
/**
* Contains info about file, which potentially can be sent to telegram system.
*
* @param storageFileInfo Information about this file
* @param fileName Filename
* @param inputSource Lambda which able to allocate [Input] for uploading/manipulating data
*
* @see StorageFileInfo
* @see asStorageFile
*/
data class StorageFile(
val storageFileInfo: StorageFileInfo,
val fileName: String,
private val inputSource: () -> Input
) {
val input: Input
get() = inputSource()
@Deprecated("This field will be removed soon. Use fileName instead of StorageFileInfo")
val storageFileInfo: StorageFileInfo
get() = StorageFileInfo(fileName)
/**
* This methods is required for random generation of name for keeping warranties about unique file name
*/
fun generateCustomName() = "${uuid4()}.${fileName.fileExtension}"
@Deprecated("This constructor will be removed soon. Use constructor with fileName instead of StorageFileInfo")
constructor(
storageFileInfo: StorageFileInfo,
inputSource: () -> Input
) : this(
storageFileInfo.fileName,
inputSource
)
}
@Suppress("NOTHING_TO_INLINE")
@@ -44,7 +64,7 @@ inline fun StorageFile(
fileName: String,
bytes: ByteArray
) = StorageFile(
StorageFileInfo(fileName)
fileName
) {
ByteReadPacket(bytes)
}
@@ -54,8 +74,8 @@ suspend inline fun StorageFile(
fileName: String,
byteReadChannel: ByteReadChannel
) = StorageFile(
StorageFileInfo(fileName),
byteReadChannel.asInput().let { { it } }
fileName,
inputSource = byteReadChannel.asInput().let { { it } }
)
@Suppress("NOTHING_TO_INLINE", "unused")

View File

@@ -3,43 +3,43 @@ package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.types.MessageEntity.textsources.*
import kotlin.test.assertTrue
const val testText = "It is simple hello world with #tag and @mention"
const val formattedV2Text = "It *_is_ ~__simple__~* ||hello world|| with \\#tag and @mention"
const val formattedHtmlText = "It <b><i>is</i> <s><u>simple</u></s></b> <span class=\"tg-spoiler\">hello world</span> with #tag and @mention"
const val testText = "It (is?) is simple hello world with #tag and @mention"
const val formattedV2Text = "It \\(is?\\) *_is_ ~__simple__~* ||hello world|| with \\#tag and @mention"
const val formattedHtmlText = "It (is?) <b><i>is</i> <s><u>simple</u></s></b> <span class=\"tg-spoiler\">hello world</span> with #tag and @mention"
internal val testTextEntities = listOf(
RawMessageEntity(
"bold",
3,
9,
9
),
RawMessageEntity(
"italic",
3,
9,
2
),
RawMessageEntity(
"strikethrough",
6,
12,
6
),
RawMessageEntity(
"underline",
6,
12,
6
),
RawMessageEntity(
"spoiler",
13,
19,
11
),
RawMessageEntity(
"hashtag",
30,
36,
4
),
RawMessageEntity(
"mention",
39,
45,
8
)
)

View File

@@ -2,8 +2,7 @@ package dev.inmo.tgbotapi.types.MessageEntity
import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.MessageEntity.textsources.*
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.*
class StringFormattingTests {
@Test
@@ -38,7 +37,7 @@ class StringFormattingTests {
@Test
fun testThatCreatingOfStringWithSimpleDSLWorksCorrectly() {
val sources: TextSourcesList = regular("It ") +
val sources: TextSourcesList = regular("It (is?) ") +
bold(italic("is") +
" " +
strikethrough(underline("simple"))) +
@@ -53,4 +52,43 @@ class StringFormattingTests {
assertEquals(formattedV2Text, sources.toMarkdownV2Texts().first())
assertEquals(formattedHtmlText, sources.toHtmlTexts().first())
}
@Test
fun testForRepeatingWordsInOneSentenceWithTheSecondOneFormatted() {
val sourceText = "link link"
val messageEntities = listOf(
RawMessageEntity("bold", 5, 4),
RawMessageEntity("text_link", 6, 2, "google.com")
)
val textSources = messageEntities.asTextSources(sourceText)
val (regular, bold) = textSources
assertTrue(regular is RegularTextSource)
assertTrue(bold is BoldTextSource)
assertTrue(regular.source == "link ")
assertTrue(bold.source == "link")
assertTrue((bold.subsources[0] as? RegularTextSource) ?.source == "l")
assertTrue((bold.subsources[1] as? TextLinkTextSource) ?.source == "in")
assertTrue((bold.subsources[1] as? TextLinkTextSource) ?.url == "google.com")
assertTrue((bold.subsources[2] as? RegularTextSource) ?.source == "k")
assertTrue(bold.subsources.size == 3)
assertTrue(textSources.size == 2)
}
@Test
fun testForRepeatingWordsInOneSentenceWithTheSecondOneFormattedInsideOfFormatting() {
val sourceText = "text"
val messageEntities = listOf(
RawMessageEntity("bold", 0, 4),
RawMessageEntity("text_link", 3, 1, "google.com")
)
val textSources = messageEntities.asTextSources(sourceText)
val (bold) = textSources
assertTrue(bold is BoldTextSource)
assertTrue(bold.source == "text")
assertTrue((bold.subsources[0] as? RegularTextSource) ?.source == "tex")
assertTrue((bold.subsources[1] as? TextLinkTextSource) ?.source == "t")
assertTrue((bold.subsources[1] as? TextLinkTextSource) ?.url == "google.com")
assertTrue(bold.subsources.size == 2)
assertTrue(textSources.size == 1)
}
}

View File

@@ -7,7 +7,7 @@ import java.nio.file.Files
fun StorageFile(
file: File
) = StorageFile(
StorageFileInfo(file.name)
file.name
) {
file.inputStream().asInput()
}

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